Skip to content

[FEAT] 일반 유저 - 예약 목록 및 상세정보 조회 api 구현#20

Merged
buzz0331 merged 9 commits into
developfrom
feat/#18-member-reservation-history
Sep 18, 2025
Merged

[FEAT] 일반 유저 - 예약 목록 및 상세정보 조회 api 구현#20
buzz0331 merged 9 commits into
developfrom
feat/#18-member-reservation-history

Conversation

@buzz0331

@buzz0331 buzz0331 commented Sep 17, 2025

Copy link
Copy Markdown
Contributor

#️⃣연관된 이슈

closes #18

📝작업 내용

예약 목록 (무한 스크롤)

  1. 로그인한 일반 유저와 요청된 예약 상태로 예약 목록을 조회 및 페이지네이션 (N+1 방지를 위한 foodTruck FETCH JOIN)
  2. 각 Reservation에서 FoodTruck을 꺼내서 Response DTO에 매핑

예약 상세 정보 조회

  1. reservationId로 예약 정보를 조회 (N+1 방지를 위한 foodTruck, member FETCH JOIN)
  2. 각 Reservation에서 FoodTruck을 꺼내서 Response DTO에 매핑

예약 상세 정보 조회를 구현하면서 상균님이 이미 사장님 예약 상세정보 조회 api에서 구현해둔 쿼리를 사용하였는데요. 그 페이지에선 Member의 정보가 필요하기 때문에 member 테이블을 FETCH JOIN 하였는데 현재 제가 구현한 api에서는 Member를 get해오지 않기 때문에 굳이 필요하지는 않는 상황입니다. 따라서 불필요한 JOIN이기는 한데 쿼리 선언을 하나 늘리는 것보다 기존 쿼리를 그대로 사용하는게 더 낫지 않나 라고 생각해서 일단 그대로 사용한 상황입니다. 이 부분에 대해서 상균님은 어떻게 생각하시는지 궁금합니다!!

스크린샷

스크린샷 2025-09-17 오후 5 36 34 스크린샷 2025-09-17 오후 5 37 28

💬리뷰 요구사항

dto에서 파싱되는 부분(전기 사용 여부, 계약금 문자열 파싱)을 도메인 내부에서 이루어지도록 해서 중복 코드를 줄이고자 했습니다! (추후에 문자열 요구사항이 변경되더라도 도메인에서만 수정하면 되기 때문에 유지보수에 더 수월할 것이라고 생각했습니다~) 확인해주시면 감사하겠습니다.

Summary by CodeRabbit

  • 신기능

    • 내 예약 내역을 무한 스크롤로 조회하는 API 추가
    • 특정 예약의 상세 정보를 조회하는 API 추가
    • 예약 응답에서 보증금과 전기 사용 여부가 읽기 쉬운 형식으로 표시됨
  • 문서

    • 회원/사용자 관련 API의 Swagger 문서 가독성 개선 및 주석 정리

@coderabbitai

coderabbitai Bot commented Sep 17, 2025

Copy link
Copy Markdown

Walkthrough

회원 예약 목록/상세 조회 기능이 추가되었습니다. 컨트롤러와 서비스에 엔드포인트 및 위임 로직이 도입되었고, 예약 레포지토리에 커서 기반 조회가 추가되었습니다. 응답/요청 DTO 신설, Reservation/ReservationInfo 보조 메서드 추가, 일부 레포지토리 페치 전략을 EntityGraph로 변경, Swagger 주석 형식이 정리되었습니다.

Changes

Cohort / File(s) Summary
Member 예약 API 추가
src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java, .../member/application/MemberService.java, .../member/application/reservation/MemberReservationService.java
회원 예약 목록/상세 조회 엔드포인트 추가. MemberService에 검증 후 위임 메서드 2개 추가. MemberReservationService 신설, 상태별 커서 페이징 목록/상세 조회 및 소유자 검증 구현.
DTO 추가 (요청/응답)
.../member/presentation/dto/request/GetReservationHistoryRequest.java, .../member/presentation/dto/response/MemberReservationHistoryResponse.java, .../member/presentation/dto/response/MemberReservationDetailResponse.java
예약 이력 조회 요청 DTO와 목록/상세 응답 DTO 신설. Swagger 스키마 주석 포함. Reservation/푸드트럭에서 값 매핑하는 of(...) 제공.
Reservation 도메인 확장
src/main/java/konkuk/chacall/domain/reservation/domain/model/Reservation.java, .../domain/value/ReservationInfo.java
Reservation에 isReservedBy(Long) 추가. ReservationInfo에 전기 사용/보증금 포맷팅 메서드 추가.
예약 레포지토리 커서 조회
src/main/java/konkuk/chacall/domain/reservation/domain/repository/ReservationRepository.java
회원별 상태 필터 + 커서 페이징 findMemberReservationsByStatusWithCursor(...) 추가. @EntityGraph(attributePaths = {"foodTruck"})로 연관 즉시 로딩.
EntityGraph로 페치 전략 변경
.../member/domain/repository/RatingRepository.java, .../member/domain/repository/SavedFoodTruckRepository.java
기존 JOIN FETCH 제거, @EntityGraph로 연관 엔티티 로딩 지정. 쿼리 단순화, 시그니처 불변.
Owner 응답 포맷팅 위임
src/main/java/konkuk/chacall/domain/owner/presentation/dto/response/OwnerReservationDetailResponse.java
전기/보증금 문자열 생성 로직을 ReservationInfo의 새 메서드 호출로 대체.
Swagger 주석 정리(형식)
src/main/java/konkuk/chacall/domain/user/presentation/UserController.java
@operation 주석 포맷 변경(내용 동일), 기능 변화 없음.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant MC as MemberController
  participant MS as MemberService
  participant MRS as MemberReservationService
  participant RR as ReservationRepository

  rect rgb(240,248,255)
  note over C,MC: GET /members/me/reservations (무한스크롤)
  C->>MC: GetReservationHistoryRequest
  MC->>MS: getMemberReservations(request, memberId)
  MS->>MS: 멤버 검증
  MS->>MRS: getMemberReservations(member, request.status, lastCursor, pageSize)
  MRS->>RR: findMemberReservationsByStatusWithCursor(memberId, status, lastCursor, pageable)
  RR-->>MRS: Slice<Reservation{foodTruck}>
  MRS-->>MS: CursorPagingResponse<MemberReservationHistoryResponse>
  MS-->>MC: BaseResponse<CursorPagingResponse<...>>
  MC-->>C: 200 OK
  end

  rect rgb(245,255,250)
  note over C,MC: GET /members/me/reservations/{reservationId}
  C->>MC: reservationId
  MC->>MS: getMemberReservationDetail(reservationId, memberId)
  MS->>MS: 멤버 검증
  MS->>MRS: getMemberReservationDetail(reservationId, member)
  MRS->>RR: findById(reservationId) (연관 로딩 포함)
  RR-->>MRS: Reservation{member, foodTruck}
  MRS->>MRS: reservation.isReservedBy(memberId) 확인
  alt 소유자 아님
    MRS-->>MS: BusinessException
    MS-->>MC: 에러 응답
    MC-->>C: 403/404
  else 소유자 맞음
    MRS-->>MS: MemberReservationDetailResponse
    MS-->>MC: BaseResponse<...>
    MC-->>C: 200 OK
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • ksg1227

Poem

새싹 같은 요청, 톡— 커서가 튕겨요 🌱
토끼는 깡총, 목록을 한 입씩 넘겨요 🥕
상세도 콕, 전기와 보증금 말끔히 포맷!
예약의 길 따라 발자국 남기며
히스토리 춤추고, 응답은 반짝— OK! ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning 대체로 컨트롤러·서비스·DTO·ReservationRepository·ReservationInfo 관련 변경은 PR 목적(예약 목록·상세조회)과 일치하지만 RatingRepository와 SavedFoodTruckRepository에 적용된 @EntityGraph 변경과 일부 임포트/포맷 변경은 이 PR의 핵심 목표와 직접 관련이 없어 범위를 벗어난 변경으로 판단됩니다. 해결을 위해 RatingRepository 및 SavedFoodTruckRepository 관련 변경을 별도 PR로 분리하거나 이 PR 설명에 포함된 변경 이유와 성능·동작 영향(테스트 결과 포함)을 명시해 주세요; 분리 불가 시에는 해당 변경이 필요한 근거와 회귀 방지를 위한 테스트를 함께 제공해 주시기 바랍니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Linked Issues Check ❓ Inconclusive 링크된 이슈 #18에는 구체적 작업 항목이나 수락 기준이 비어 있어(PR 설명과 제목으로는 구현 의도가 파악되나) PR이 이슈의 명시적 요구사항을 완전히 충족했는지 판단할 수 없습니다. 해결을 위해 이슈에 엔드포인트 스펙, 응답 포맷, 권한 요구사항, 예외 처리 및 기대 동작(무한 스크롤 동작 방식) 같은 상세 수락 기준을 추가하거나 PR에 구현된 항목별 체크리스트를 명시해 주세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 제목 "[FEAT] 일반 유저 - 예약 목록 및 상세정보 조회 api 구현"은 PR의 핵심 변경사항(일반 유저 대상 예약 목록 조회 및 예약 상세 조회 API 구현)을 명확하고 간결하게 요약하고 있어 히스토리 스캔 시 주요 목적을 바로 이해할 수 있습니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#18-member-reservation-history

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (17)
src/main/java/konkuk/chacall/domain/user/presentation/UserController.java (2)

25-28: Swagger @operation 멀티라인 포맷 변경 OK

동작 변화 없이 가독성만 개선된 것으로 보입니다. 필요 시 @ApiResponses/@apiresponse로 성공/에러 스펙을 명시해 주면 문서 품질이 더 좋아집니다.


37-40: updateUserInfo의 응답 설계 검토 제안

BaseResponse.ok(null) 대신 204 No Content를 고려하면 불필요한 payload를 줄일 수 있습니다. API 일관성 측면에서 팀 컨벤션 확인 바랍니다.

src/main/java/konkuk/chacall/domain/reservation/domain/model/Reservation.java (1)

52-54: 소유자 확인 메서드 안정성 보강 제안

현재 구현은 정상이나, 방어적 차원에서 NPE 방지를 위해 Objects.equals 사용을 권장합니다.

-    public boolean isReservedBy(Long userId) {
-        return this.member.getUserId().equals(userId);
-    }
+    public boolean isReservedBy(Long userId) {
+        return this.member != null && java.util.Objects.equals(this.member.getUserId(), userId);
+    }
src/main/java/konkuk/chacall/domain/member/domain/repository/SavedFoodTruckRepository.java (1)

21-29: 첫 페이지(lastCursor null) 처리 및 커서 조건 보강 제안

lastCursor가 null일 경우 조회가 불가능합니다. JPQL에 null 허용 조건을 추가하면 최초 페이지 로딩이 쉬워집니다.

-    @Query("SELECT sft FROM SavedFoodTruck sft " +
-            "WHERE sft.member = :member " +
-            "AND sft.savedFoodTruckId < :lastCursor " +
-            "ORDER BY sft.savedFoodTruckId DESC")
+    @Query("SELECT sft FROM SavedFoodTruck sft " +
+            "WHERE sft.member = :member " +
+            "AND (:lastCursor IS NULL OR sft.savedFoodTruckId < :lastCursor) " +
+            "ORDER BY sft.savedFoodTruckId DESC")

추가로 savedFoodTruckId DESC + range 조건을 활용 중이므로 해당 컬럼 인덱스가 있는지 확인 부탁드립니다.

src/main/java/konkuk/chacall/domain/member/domain/repository/RatingRepository.java (1)

17-21: @Param 누락 가능성 — 파라미터 바인딩 안전성 확보

컴파일 옵션(-parameters)에 의존하지 않도록 @Param("member")를 명시해 주세요. 런타임 바인딩 오류를 예방합니다.

-    List<Rating> findAllByMemberAndIsRatedFalse(User member);
+    List<Rating> findAllByMemberAndIsRatedFalse(@org.springframework.data.repository.query.Param("member") User member);
src/main/java/konkuk/chacall/domain/reservation/domain/value/ReservationInfo.java (3)

51-53: 네이밍 미세개선 제안

parsingIsUserElectricity → formatElectricityInfo 처럼 “parsing”보다 “format” 계열 네이밍이 의도(표시용 가공)를 더 잘 드러냅니다. 선택사항입니다.


55-57: 금액 표기 천단위 구분자 적용 제안

“50000원” 대신 “50,000원” 형태가 일반적입니다. 간단히 NumberFormat을 적용하세요.

-    public String parsingReservationDeposit() {
-        return reservationDeposit + "원";
-    }
+    public String parsingReservationDeposit() {
+        java.text.NumberFormat nf = java.text.NumberFormat.getInstance(java.util.Locale.KOREA);
+        return nf.format(reservationDeposit) + "원";
+    }

9-11: 불필요한 import 정리

org.apache.catalina.LifecycleState, java.time.LocalDate는 사용되지 않습니다. 제거해 주세요.

-import org.apache.catalina.LifecycleState;
-
-import java.time.LocalDate;
src/main/java/konkuk/chacall/domain/reservation/domain/repository/ReservationRepository.java (1)

36-46: 멤버 예약 커서 조회: null 커서 허용 및 N+1 점검

  • lastCursor null 시 첫 페이지 조회가 불가합니다. null 허용 조건을 추가하세요.
  • DTO에서 foodTruck의 대표 사진 접근 시 추가 연관 로딩이 필요하다면 EntityGraph에 연쇄 경로를 포함해 N+1을 방지하세요(예: foodTruck.photoList 등, 실제 필드명 확인 필요).
-    @Query("SELECT r FROM Reservation r " +
-            "WHERE r.member.userId = :memberId " +
-            "AND r.reservationStatus = :status " +
-            "AND r.reservationId < :lastCursor " +
-            "ORDER BY r.reservationId DESC")
+    @Query("SELECT r FROM Reservation r " +
+            "WHERE r.member.userId = :memberId " +
+            "AND r.reservationStatus = :status " +
+            "AND (:lastCursor IS NULL OR r.reservationId < :lastCursor) " +
+            "ORDER BY r.reservationId DESC")
src/main/java/konkuk/chacall/domain/member/presentation/dto/request/MemberReservationHistoryResponse.java (2)

23-33: 대표 사진 접근 시 NPE 방지

foodTruckPhotoList 또는 mainPhotoUrl이 null일 가능성에 대비한 안전 가드가 필요합니다. 기본 이미지 사용 정책이 있다면 그 URL을 적용하세요.

-    public static MemberReservationHistoryResponse of(Reservation reservation, FoodTruck foodTruck) {
-        List<String> dateTimeList = reservation.getReservationInfo().getFormattedDateTimeInfos();
-
-        return new MemberReservationHistoryResponse(
-                reservation.getReservationId(),
-                foodTruck.getFoodTruckPhotoList().getMainPhotoUrl(),
-                foodTruck.getName(),
-                reservation.getReservationInfo().getReservationAddress(),
-                dateTimeList
-        );
-    }
+    public static MemberReservationHistoryResponse of(Reservation reservation, FoodTruck foodTruck) {
+        List<String> dateTimeList = reservation.getReservationInfo().getFormattedDateTimeInfos();
+        String mainPhotoUrl = null;
+        if (foodTruck.getFoodTruckPhotoList() != null) {
+            mainPhotoUrl = foodTruck.getFoodTruckPhotoList().getMainPhotoUrl();
+        }
+        return new MemberReservationHistoryResponse(
+                reservation.getReservationId(),
+                mainPhotoUrl,
+                foodTruck.getName(),
+                reservation.getReservationInfo().getReservationAddress(),
+                dateTimeList
+        );
+    }

18-20: 스키마 설명(최대 2개)와 실제 데이터 제한 불일치

문서에 “최대 2개”라고 되어 있으나 제한 로직이 없습니다. 스트림에서 limit(2) 적용을 권장합니다.

-        List<String> dateTimeList = reservation.getReservationInfo().getFormattedDateTimeInfos();
+        List<String> dateTimeList = reservation.getReservationInfo()
+                .getFormattedDateTimeInfos()
+                .stream()
+                .limit(2)
+                .toList();

스펙상 ‘최대 2개’가 진짜 비즈니스 제약인지(백엔드에서 강제) 혹은 문서 표현인지(프론트 가이드) 확인 부탁드립니다.

src/main/java/konkuk/chacall/domain/member/presentation/dto/request/MemberReservationDetailResponse.java (2)

9-39: 패키지/네이밍 정합성

Response DTO가 dto.request 패키지에 위치합니다. dto.response로 이동하는 것이 일관성 측면에서 바람직합니다.


41-55: 사진 NPE 가드 추가 — PDF 게터명(getPdfUrl) 확인 완료

foodTruck.getFoodTruckPhotoList()가 null이면 NPE 발생 가능하므로 아래 패치 적용 권장. reservation.getPdfUrl()는 코드베이스에서 일관되게 사용되고 있어 변경 불필요합니다.

   public static MemberReservationDetailResponse of(Reservation reservation, FoodTruck foodTruck) {
       List<String> dateTimeList = reservation.getReservationInfo().getFormattedDateTimeInfos();
+      String photoUrl = (foodTruck.getFoodTruckPhotoList() == null)
+              ? null
+              : foodTruck.getFoodTruckPhotoList().getMainPhotoUrl();

       return new MemberReservationDetailResponse(
-              foodTruck.getFoodTruckPhotoList().getMainPhotoUrl(),
+              photoUrl,
               foodTruck.getName(),
               reservation.getReservationInfo().getReservationAddress(),
               dateTimeList,
               reservation.getPdfUrl(),
               reservation.getReservationInfo().getMenu(),
               reservation.getReservationInfo().parsingReservationDeposit(),
               reservation.getReservationInfo().parsingIsUserElectricity(),
               reservation.getReservationInfo().getEtcRequest()
       );
   }
src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java (1)

96-106: 예약 상세 API: 예외 스키마 명시 권장

RESERVATION_NOT_FOUND, RESERVATION_NOT_OWNED에 대한 @ExceptionDescription 추가 시 문서 신뢰도가 높아집니다.

src/main/java/konkuk/chacall/domain/member/application/reservation/MemberReservationService.java (2)

40-49: 소유권 검증 및 상세 매핑 흐름 적절

엔티티 조회 → 소유권 체크 → DTO 매핑 순서 명확합니다. 다만 현재 쿼리에 불필요한 member JOIN이 포함되어 있다면, 멤버 전용 상세 조회용 경량 쿼리(예: findByIdWithFoodTruck)로 분리해도 좋습니다.


28-38: 확인: 리포지토리 쿼리에서 정렬·커서 보장됨 — pageSize 상한 추가 필요

  • 정렬·커서: src/main/java/konkuk/chacall/domain/reservation/domain/repository/ReservationRepository.java (Query에 AND r.reservationId < :lastCursor ORDER BY r.reservationId DESC) — 현재 쿼리에서 커서 조건과 정렬이 보장됩니다.
  • 페이지 크기 제약: src/main/java/konkuk/chacall/global/common/dto/CursorPagingRequest.java에는 @Min(1)만 존재(하한 OK), 상한(@Max) 없음. 조치: CursorPagingRequest.size에 @Max(예:100) 추가하거나 서비스 호출부에서 pageSize = Math.min(pageSize, MAX_PAGE_SIZE)로 클램프 적용.
src/main/java/konkuk/chacall/domain/member/presentation/dto/request/GetReservationHistoryRequest.java (1)

18-21: 중첩 페이징 기본값은 안전하나 size 상한 미정 — 상한 추가 권장

HasPaging#pagingOrDefault()는 cursorPagingRequest()가 null일 때 new CursorPagingRequest(null, null)를 반환하고, CursorPagingRequest의 생성자에서 cursor=Long.MAX_VALUE와 size=20으로 채워 기본값 제공은 안전합니다. 다만 size에 대한 상한(@max 또는 MAX_PAGE_SIZE 상수 및 검증)이 정의되어 있지 않으므로 과도한 요청을 막기 위해 상한을 추가하세요.

수정 위치: src/main/java/konkuk/chacall/global/common/dto/CursorPagingRequest.java

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 218e1f7 and 3cff163.

📒 Files selected for processing (13)
  • src/main/java/konkuk/chacall/domain/member/application/MemberService.java (3 hunks)
  • src/main/java/konkuk/chacall/domain/member/application/reservation/MemberReservationService.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/member/domain/repository/RatingRepository.java (2 hunks)
  • src/main/java/konkuk/chacall/domain/member/domain/repository/SavedFoodTruckRepository.java (2 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java (6 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/dto/request/GetReservationHistoryRequest.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/dto/request/MemberReservationDetailResponse.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/dto/request/MemberReservationHistoryResponse.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/owner/presentation/dto/response/OwnerReservationDetailResponse.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/reservation/domain/model/Reservation.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/reservation/domain/repository/ReservationRepository.java (2 hunks)
  • src/main/java/konkuk/chacall/domain/reservation/domain/value/ReservationInfo.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/user/presentation/UserController.java (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/konkuk/chacall/domain/member/application/reservation/MemberReservationService.java (2)
src/main/java/konkuk/chacall/global/common/exception/EntityNotFoundException.java (1)
  • EntityNotFoundException (6-11)
src/main/java/konkuk/chacall/domain/member/application/MemberService.java (1)
  • Service (18-74)
🔇 Additional comments (9)
src/main/java/konkuk/chacall/domain/owner/presentation/dto/response/OwnerReservationDetailResponse.java (1)

52-54: 표시 로직의 도메인 이동 OK

ReservationInfo의 포맷터를 재사용하도록 정리된 점 좋습니다. Member/Owner 응답 간 표현 일관성 유지에 도움이 됩니다.

현재 getFormattedDateTimeInfos()가 “yyyy.MM.dd ~ yyyy.MM.dd 13시~19시” 형태를 반환합니다. 스키마 예시와 실제 포맷이 일치하는지 문서/클라이언트 측 기대치 확인 부탁드립니다.

src/main/java/konkuk/chacall/domain/member/application/MemberService.java (2)

24-24: 의존성 주입 정합성 OK

MemberReservationService 주입 및 사용 흐름이 자연스럽습니다.


68-73: 상세 조회 위임 흐름 적절

멤버 검증 후 서비스 계층으로 위임하는 구조가 명확합니다.

src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java (5)

30-33: Swagger 주석 정리 LGTM

가독성 개선에 동의합니다.


44-47: Swagger 주석 정리 LGTM

변경 내용 문제 없습니다.


57-60: Swagger 주석 정리 LGTM

문제 없습니다.


71-74: Swagger 주석 정리 LGTM

문제 없습니다.


83-94: 예약 내역 목록 API — 예외 주석 추가 및 중첩 쿼리파라미터 노출 확인

  • MemberController#getMemberReservations에 @ExceptionDescription이 없습니다. OwnerController의 사례와 동일하게 예외 스키마 주석을 추가하세요. (파일: src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java — getMemberReservations)
  • GetReservationHistoryRequest는 CursorPagingRequest(cursorPagingRequest)를 포함합니다. 현재 컨트롤러 파라미터에만 @ParameterObject가 적용되어 있어 springdoc가 중첩 필드(cursor, size)를 의도대로 노출하는지 확인해야 합니다. 노출되지 않으면 GetReservationHistoryRequest의 cursorPagingRequest 구성요소에 @ParameterObject를 추가하거나 컨트롤러에서 CursorPagingRequest를 직접 수신하도록 변경하세요. (파일: src/main/java/konkuk/chacall/domain/member/presentation/dto/request/GetReservationHistoryRequest.java, CursorPagingRequest: src/main/java/konkuk/chacall/global/common/dto/CursorPagingRequest.java)
  • 검증 방법: 빌드 후 OpenAPI 스펙 또는 swagger-ui에서 cursor/size 쿼리파라미터가 정상 노출되는지 확인하세요.
src/main/java/konkuk/chacall/domain/member/presentation/dto/request/GetReservationHistoryRequest.java (1)

11-16: 확인 완료 — allowableValues가 Enum과 일치함
GetReservationHistoryRequest의 allowableValues(PENDING, CONFIRMED, CANCELLED)이 ReservationStatus enum(PENDING, CONFIRMED, CANCELLED)과 정확히 일치하므로 변경 불필요.

Comment on lines +59 to +66
public CursorPagingResponse<MemberReservationHistoryResponse> getMemberReservations(GetReservationHistoryRequest request, Long memberId) {
// 멤버 유효성 검사 및 조회
User member = memberValidator.validateAndGetMember(memberId);

CursorPagingRequest cursorPagingRequest = request.pagingOrDefault();

return memberReservationService.getMemberReservations(member, request, cursorPagingRequest.cursor(), cursorPagingRequest.size());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

페이지 크기 상한 및 기본값 보장 확인 필요

request.pagingOrDefault()cursorPagingRequest.size()에 상한/하한 검증이 내장됐는지 확인해 주세요. 과도한 size는 데이터베이스/네트워크 부하를 유발할 수 있습니다.

아래 스크립트로 CursorPagingRequest 제약과 HasPaging#pagingOrDefault 존재 여부를 점검해 주세요.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# CursorPagingRequest 정의 및 size 제약(@Min/@Max 등) 확인
rg -nP --type=java 'record\s+CursorPagingRequest\b|class\s+CursorPagingRequest\b' -C3
rg -n --type=java '@Min|@Max|MAX_PAGE_SIZE|\bsize\s*\(' -C2

# HasPaging 인터페이스에 pagingOrDefault 기본 메서드 있는지 확인
rg -n --type=java 'interface\s+HasPaging\b|default\s+pagingOrDefault\s*\(' -C3

Length of output: 7990


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== CursorPagingRequest.java ==="
sed -n '1,240p' src/main/java/konkuk/chacall/global/common/dto/CursorPagingRequest.java || true
echo
echo "=== HasPaging.java ==="
sed -n '1,240p' src/main/java/konkuk/chacall/global/common/dto/HasPaging.java || true
echo
echo "=== SavedFoodTruckService.java (for PageRequest usage) ==="
sed -n '1,240p' src/main/java/konkuk/chacall/domain/member/application/foodtruck/SavedFoodTruckService.java || true
echo
echo "=== Search for MAX/DEFAULT constants ==="
rg -n --hidden --no-ignore 'MAX_PAGE_SIZE|DEFAULT_PAGE_SIZE|DEFAULT_SIZE|MAX_SIZE|MAX_PAGE' || true

Length of output: 5007


상한값 미적용 — CursorPagingRequest에 최대 페이지 크기 제한(또는 서비스 레벨 클램프) 적용 필요

검증 결과: CursorPagingRequest는 @min(1) 및 DEFAULT_SIZE(20)를 가지나 상한(@max 또는 MAX_SIZE)이 없음. HasPaging.pagingOrDefault는 기본값 제공으로 정상 동작. size가 그대로 PageRequest.of 또는 하위 서비스로 전달되어 과도한 요청 시 DB/네트워크 부하 가능.

수정 대상(정확한 위치):

  • src/main/java/konkuk/chacall/global/common/dto/CursorPagingRequest.java — @max 추가 또는 생성자에서 size = Math.min(size, MAX_PAGE_SIZE) 같은 프로그래매틱 클램프 도입.
  • src/main/java/konkuk/chacall/domain/member/application/foodtruck/SavedFoodTruckService.java — PageRequest.of(0, cursorPagingRequest.size()) 를 PageRequest.of(0, Math.min(cursorPagingRequest.size(), MAX_PAGE_SIZE)) 등으로 방어 코딩.
  • src/main/java/konkuk/chacall/domain/member/application/MemberService.java 및 src/main/java/konkuk/chacall/domain/owner/application/OwnerService.java — 하위 서비스로 전달되는 size에 대해 상한 적용 또는 하위 서비스에서 상한을 보장하도록 변경.

권장 방식: MAX_PAGE_SIZE(예: 50~100) 상수 정의 → CursorPagingRequest에 @max(…) 또는 생성자 클램프 적용 + 호출부에도 방어 코딩 및 단위 테스트 추가.

🤖 Prompt for AI Agents
In src/main/java/konkuk/chacall/domain/member/application/MemberService.java
around lines 59-66 and related flow, the passed paging size has no enforced
upper bound which can cause excessive DB/network load; define a MAX_PAGE_SIZE
constant (e.g. 50–100) in a common paging class, add either @Max(MAX_PAGE_SIZE)
or programmatic clamping (size = Math.min(size, MAX_PAGE_SIZE)) in
src/main/java/konkuk/chacall/global/common/dto/CursorPagingRequest.java, and
apply defensive clamps where sizes are used (e.g. change PageRequest.of(0,
cursorPagingRequest.size()) calls in
src/main/java/konkuk/chacall/domain/member/application/foodtruck/SavedFoodTruckService.java
and ensure MemberService.java and
src/main/java/konkuk/chacall/domain/owner/application/OwnerService.java either
pass the clamped size or re-clamp before delegating); add unit tests verifying
the clamp/enforcement and update any API validation docs accordingly.

@@ -0,0 +1,35 @@
package konkuk.chacall.domain.member.presentation.dto.request;

@coderabbitai coderabbitai Bot Sep 17, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

패키지 위치/이름 불일치

Response DTO가 request 패키지에 위치합니다. 패키지/경로를 response로 정정해 주세요(리팩터 후 import 영향 점검 필요).

-package konkuk.chacall.domain.member.presentation.dto.request;
+package konkuk.chacall.domain.member.presentation.dto.response;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package konkuk.chacall.domain.member.presentation.dto.request;
package konkuk.chacall.domain.member.presentation.dto.response;

@ksg1227 ksg1227 Sep 17, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@buzz0331
코래 리뷰따라 이 클래스 패키지 위치만 바꿔주면 될 것 같습니다!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ksg1227 네, 맞습니다! Response DTO가 request 패키지에 있는 것은 확실히 잘못된 위치네요.

MemberReservationHistoryResponse 클래스를 response 패키지로 이동시키시면 됩니다. 이동 후에는 이 클래스를 import하는 다른 파일들의 import 문도 자동으로 업데이트되는지 확인해보시기 바랍니다.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 맞네요 확인했슴다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

ksg1227
ksg1227 previously approved these changes Sep 17, 2025

@ksg1227 ksg1227 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번 기능에서는 흠 잡을 부분이 전혀 없었습니다 아주 굿👍🏻
언급해주신 내용에 대해서는, 저도 그냥 기존에 있던 쿼리를 그대로 가져다가 사용해도 괜찮을 것 같습니다. 어차피 member 까지 join 한다고 해서 성능에 큰 차이가 있을 것 같지도 않고, 재사용하는 것 나쁘지 않은 것 같아요! 수고하셨습니당

Optional<Reservation> findByIdWithDetails(@Param("reservationId") Long reservationId);


@EntityGraph(attributePaths = {"foodTruck"})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 푸드트럭은 fetchJoin 으로 가져오면 좋겠다 생각했는데 딱 적용해주셨군요 아주 좋습니다

Comment on lines +52 to +53
reservation.getReservationInfo().parsingReservationDeposit(),
reservation.getReservationInfo().parsingIsUserElectricity(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReservationInfo 내에서 처리하는 것 좋습니다! 훨씬 낫네요 👍🏻 제꺼까지 고쳐주셔서 감사합니다 ㅎㅎ

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/main/java/konkuk/chacall/domain/member/application/MemberService.java (1)

56-63: 커서 페이지 사이즈 상한 클램프 필요 (이전 코멘트와 동일 사안)

request.pagingOrDefault()로 얻은 size에 상한이 없습니다. 과도한 size는 부하 유발합니다. 최소 서비스 경계에서라도 클램프 적용해 주세요.

-        CursorPagingRequest cursorPagingRequest = request.pagingOrDefault();
-
-        return memberReservationService.getMemberReservations(member, request, cursorPagingRequest.cursor(), cursorPagingRequest.size());
+        CursorPagingRequest cursorPagingRequest = request.pagingOrDefault();
+        int clampedSize = Math.min(cursorPagingRequest.size(), 50); // TODO: 공용 MAX_PAGE_SIZE로 치환
+        return memberReservationService.getMemberReservations(member, request, cursorPagingRequest.cursor(), clampedSize);

다음 스크립트로 공용 상한이 이미 정의되어 있는지 확인해 주세요.

#!/bin/bash
rg -n --type=java 'MAX_PAGE_SIZE|@Max\(|clamp|withMaxSize' -C2
rg -n --type=java 'record\s+CursorPagingRequest\b|class\s+CursorPagingRequest\b' -C3
🧹 Nitpick comments (7)
src/main/java/konkuk/chacall/domain/member/application/MemberService.java (2)

5-5: 와일드카드 import 지양

DTO import를 와일드카드로 열면 변경에 취약해집니다. 사용 중인 타입만 명시적으로 import해 주세요.

-import konkuk.chacall.domain.member.presentation.dto.request.*;
-import konkuk.chacall.domain.member.presentation.dto.response.*;
+import konkuk.chacall.domain.member.presentation.dto.request.GetReservationHistoryRequest;
+import konkuk.chacall.domain.member.presentation.dto.request.RegisterRatingRequest;
+import konkuk.chacall.domain.member.presentation.dto.request.UpdateFoodTruckSaveStatusRequest;
+import konkuk.chacall.domain.member.presentation.dto.response.MemberReservationDetailResponse;
+import konkuk.chacall.domain.member.presentation.dto.response.MemberReservationHistoryResponse;
+import konkuk.chacall.domain.member.presentation.dto.response.ReservationForRatingResponse;
+import konkuk.chacall.domain.member.presentation.dto.response.SavedFoodTruckResponse;
+import konkuk.chacall.domain.member.presentation.dto.response.SavedFoodTruckStatusResponse;

Also applies to: 7-8


65-70: 소유권 검증과 불필요 JOIN 제거 확인 요청

상세 조회는 reservationId가 해당 member의 소유인지 검증되어야 합니다(404/403 중 하나로 응답 일관화). 또한 PR 설명대로 현재 쿼리가 member까지 FETCH JOIN 한다면, 본 API에서는 불필요하므로 제거/분리 고려가 필요합니다.

src/main/java/konkuk/chacall/domain/member/presentation/dto/response/MemberReservationHistoryResponse.java (2)

18-20: “최대 2개” 제약 보장 위치 명확화

표기만 있고 코드 레벨 제약이 보이지 않습니다. ReservationInfo#getFormattedDateTimeInfos()가 2개로 클램프하는지 확인하고, 아니면 여기서도 limit(2)로 방어해 주세요.


23-33: 사진 URL null 처리/대체값 전략 필요

foodTruck.getFoodTruckPhotoList().getMainPhotoUrl()가 null일 수 있습니다. 스키마 nullable 지정 또는 기본 이미지로 대체하는 방어 로직이 필요합니다.

-                foodTruck.getFoodTruckPhotoList().getMainPhotoUrl(),
+                // TODO: 도메인/VO에서 기본값 보장되면 그대로 사용
+                foodTruck.getFoodTruckPhotoList().getMainPhotoUrl(), // null 가능 시 스키마에 nullable=true 반영 또는 기본 URL 적용
src/main/java/konkuk/chacall/domain/member/presentation/dto/response/MemberReservationDetailResponse.java (3)

9-39: DTO 필드 타입 재고: 금액/전기여부는 타입으로, 표현은 DTO에서 포맷

  • deposit을 String(“50000원”)으로 들고 오면 통화 계산/정렬/합계에 제약이 큽니다. KRW 정수(Long) 또는 Money VO로 노출하고, 표시용 문자열은 DTO 변환 단계에서 포맷하세요.
  • electricityInfo도 String(“가능/불가”) 대신 boolean/Enum을 권장합니다.

예시(방향성):

  • 도메인: long getReservationDepositAmountKrw(), boolean isElectricityAvailable()
  • DTO: NumberFormat/MessageSource를 통해 "50,000원", 현지화된 "가능/불가"로 포맷

9-39: 상세 응답에 reservationId 포함 고려

상세 화면에서도 추후 액션(공유/신고/재요청 등)에 식별자가 필요할 수 있습니다. reservationId 필드 추가를 검토해 주세요.


41-55: 도메인 파싱 메서드 명/책임 정교화

parsingReservationDeposit(), parsingIsUserElectricity()는 네이밍과 책임이 “포맷팅”에 가깝습니다.

  • 도메인은 값(타입/규칙)까지 책임, 문자열 표현은 DTO/프리젠테이션 책임으로 분리해 주세요.
  • 특히 parsingIsUserElectricity()는 오탈자 가능성이 있습니다(Use vs User). 의미가 “전기 사용 여부”라면 isElectricityAvailable() 같은 불리언 메서드가 적절합니다.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cff163 and 113cd5c.

📒 Files selected for processing (5)
  • src/main/java/konkuk/chacall/domain/member/application/MemberService.java (3 hunks)
  • src/main/java/konkuk/chacall/domain/member/application/reservation/MemberReservationService.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java (6 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/dto/response/MemberReservationDetailResponse.java (1 hunks)
  • src/main/java/konkuk/chacall/domain/member/presentation/dto/response/MemberReservationHistoryResponse.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/konkuk/chacall/domain/member/application/reservation/MemberReservationService.java
  • src/main/java/konkuk/chacall/domain/member/presentation/MemberController.java
🔇 Additional comments (1)
src/main/java/konkuk/chacall/domain/member/application/MemberService.java (1)

21-21: 서비스 의존성 주입 추가 LGTM

MemberReservationService 주입 위치/가시성 적절합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 일반유저 - 예약 목록 조회 및 상세페이지 조회 api 구현

2 participants