Skip to content

[Jinyong] Week7 미션#76

Merged
LATE-BL00MER merged 8 commits into
Jinyongfrom
Jinyong-week7
May 20, 2026
Merged

[Jinyong] Week7 미션#76
LATE-BL00MER merged 8 commits into
Jinyongfrom
Jinyong-week7

Conversation

@LATE-BL00MER
Copy link
Copy Markdown

@LATE-BL00MER LATE-BL00MER commented May 12, 2026

🔗 연관 이슈

closes #75

🛠 작업 내용

6주차 코드 수정
내가 진행중인 미션 조회
내가 생성한 리뷰를 조회(별점, 순, ID순)
Request Body가 있는 API에 검증 어노테이션 붙혀 검증하기

🖼 스크린샷 (선택)

👀 리뷰 요구사항 (선택)

🤖 AI 활용

  • AI 사용 안 함
  • 코드 작성 아이디어 참고
  • 테스트/리팩토링 보조
  • 문서/주석 작성 보조
  • 기타 (아래에 간단히 작성)

💬 나의 프롬프트

워크북의 과제를 내가 어떤 방향성을 가지 해결해야 해?

오프셋 기반 페이지네이션에서 pageSize, pageNumber, sort를 각각 어떤 의미로 사용해야 하고, Swagger에서는 어떤 값으로 테스트해야 돼?

내가 워크북을 참고해서 짠 코드는 ~인데 이걸 내 ERD방식으로 고치려면 어떻게 해야 해?

Request Body 검증 어노테이션을 적용한 뒤, 잘못된 요청을 보냈을 때 Swagger에서 400 응답이 나와야 하는데 500응답이 나와 어떻게 해결해야 해?

🧠 AI 응답

이번 과제의 핵심은 Controller, Service, Repository, Converter, DTO가 연결되는 API 흐름을 이해하고, 이를 내 프로젝트 구조와 ERD에 맞게 적용하는 것이라고 설명했습니다. 또한 “가게 미션 조회”, “내가 진행 중인 미션 조회”, “내가 작성한 리뷰 조회”, “Request Body 검증”이 각각 어떤 기능을 검증하기 위한 것인지 단계별로 정리해주었음.

pageSize는 한 페이지에 가져올 데이터 개수이고, pageNumber는 조회할 페이지 번호이며, sort는 정렬 기준이라고 설명함. 예를 들어 Swagger에서 pageSize=10, pageNumber=0, sort=id로 요청하면 id 기준으로 첫 번째 페이지의 데이터를 10개 조회한다는 의미라고 답변함

“내가 진행 중인 미션 조회”는 Mission 테이블만 조회하는 것이 아니라, 사용자와 미션의 관계를 저장하는 MemberMission 매핑 엔티티를 기준으로 조회해야 한다고 설명함 따라서 memberId와 MemberMissionStatus.ONGOING 조건을 사용해 Repository 쿼리를 작성하고, 조회 결과를 Converter를 통해 응답 DTO로 변환하는 흐름으로 구현해야 한다고 안내했음

미처 코드를 수정하지 못한 GeneralExceptionAdvice에서 MethodArgumentNotValidException을 처리하고, 각 필드별 에러 메시지를 응답 body의 result에 담아 내려주도록 수정하는 방법을 제시했음

✅ 내가 최종 선택한 방법 (이유)

과제를 하는 데에 있어 방향성을 상실한 느낌이 들었고 정확히 무엇을 해야 하는지 알게 되었다.

이 방식이 워크북에서 제시한 오프셋 기반 페이지네이션 구조와 가장 비슷하고, Swagger에서도 페이지 크기, 페이지 번호, 정렬 기준을 직접 넣어 테스트할 수 있기 때문이다.

"진행 중"이라는 상태는 미션 자체의 값이 아니라 사용자와 미션 사이의 관계에서 정해지는 값이기 때문에, MemberMissionStatus.ONGOING을 조건으로 조회하는 것이 내 ERD 구조에 더 적절하다고 판단했다.

GeneralExceptionAdvice에서 검증 실패 예외를 처리하는 방식을 선택했는데 워크북의 검증하기 부분을 제대로 이해하지 못한 상태였었다. AI가 짜준 코드를 보면서 400응답이 뜨게 되었다.

💡 나만의 Tip (선택)

@LATE-BL00MER LATE-BL00MER self-assigned this May 12, 2026
Copy link
Copy Markdown
Contributor

@kjhh2605 kjhh2605 left a comment

Choose a reason for hiding this comment

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

[키워드 조사]

  • Page와 Slice의 차이를 전체 개수 조회 여부와 무한 스크롤 적합성 기준으로 정리한 점이 좋습니다. 추가로 Page를 사용할 때 count 쿼리가 실행되는 이유와 Slice가 pageSize + 1 방식으로 다음 페이지 여부를 판단하는 흐름까지 함께 정리하면 이해가 더 명확해집니다.
  • Stream API는 DTO 변환 사례와 연결한 점이 적절합니다. 다만 map, filter, toList의 역할 차이와 원본 컬렉션을 변경하지 않는다는 특징을 함께 정리하는 것을 권장합니다.
  • 객체 그래프 탐색에서 지연 로딩과 N+1 문제를 연결한 점이 좋습니다. 추가로 fetch join, @EntityGraph, DTO 직접 조회를 각각 언제 선택하는지 비교해 보면 실무적인 판단 기준이 높아집니다.
  • @Valid@validated의 사용 위치를 구분한 점이 적절합니다. 컨트롤러의 @RequestParam 검증에는 @validated와 제약 애너테이션을 함께 적용해야 한다는 흐름을 예제와 함께 보완하는 것을 권장합니다.

[코드 리뷰]

  • 미션 생성, 페이지 기반 조회, 커서 기반 조회를 컨트롤러-서비스-레포지토리로 분리하려는 구조가 잘 드러납니다. 특히 요청 DTO 검증과 전역 예외 처리까지 연결한 점은 좋은 방향입니다.
  • 다만 페이지 파라미터 전달 순서, 커서 정렬 기준의 일관성, 객체 그래프 탐색 시 추가 쿼리 가능성은 API 동작 정확성과 JPA 이해에 직접 영향을 주므로 보완이 필요합니다.
  • .idea/workspace.xml 및 shelf 파일은 개인 IDE 상태와 실행 환경 정보가 포함될 수 있으므로 저장소 추적 대상에서 제외하고, 필요한 경우 .gitignore에 추가하는 것을 권장합니다.
  • 테스트는 실행을 시도했으나 현재 실행 환경에 Java Runtime이 없어 ./gradlew test 검증은 완료하지 못했습니다.

) {
BaseSuccessCode code = MissionSuccessCode.OK;
return ApiResponse.onSuccess(code, missionService.getMissionList(storeId));
return ApiResponse.onSuccess(code, missionService.getMissionList(storeId, pageSize, pageNumber, sort));
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.

서비스 메서드 시그니처는 (storeId, pageNumber, pageSize, sort) 순서인데, 컨트롤러에서는 (storeId, pageSize, pageNumber, sort)로 전달되고 있습니다. MVC 계층 간 DTO나 파라미터 전달 순서가 어긋나면 요청한 페이지와 크기가 서로 바뀌어 조회되므로, 파라미터 순서를 맞추거나 서비스 요청 DTO로 묶어 전달하는 방식을 권장합니다.



// ID 기준이면 다음 커서는 마지막 리뷰의 ID
if (query.equals("id")) {
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.

위 switch에서는 query.toLowerCase()로 대소문자를 보정하지만, 다음 커서 계산에서는 원본 query.equals("id")를 사용하고 있습니다. query=ID처럼 들어오면 ID 조회는 수행되지만 커서는 score 형식으로 생성될 수 있습니다. 정규화한 값을 지역 변수로 보관해 조회 분기와 커서 생성이 같은 기준을 사용하도록 정리하는 것을 권장합니다.

public static ReviewResDTO.MyReview toMyReview(Review review) {
return ReviewResDTO.MyReview.builder()
.reviewId(review.getId())
.userId(review.getMember().getId())
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.

목록 조회 후 DTO 변환 과정에서 member, store, memberMission을 객체 그래프 탐색으로 접근하고 있습니다. 연관관계가 지연 로딩이면 리뷰 개수만큼 추가 쿼리가 발생할 수 있으므로, 목록 API에서는 fetch join, @EntityGraph, 또는 필요한 필드만 조회하는 DTO projection을 검토하는 것이 좋습니다.

차이점 (표)

| 구분 | Page | Slice |
| --- | --- | --- |
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.

마크다운 표 구분선 앞에 들여쓰기가 있어 일부 렌더러에서 표로 인식되지 않을 수 있습니다. 표 문법은 | --- | --- | --- |처럼 같은 열 개수와 들여쓰기 없는 형태로 맞추는 것을 권장합니다.


OK(HttpStatus.OK,
CREATED(HttpStatus.OK,
"MISSION200_1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CREATED는 HttpStatus 중, 201번이며 HttpStatus.CREATED | "MISSION201_1"을 반환하는 것이 올바른 성공 코드입니다.

public ApiResponse<List<MissionResDTO.MissionInfo>> getMissionList(
@RequestParam Long storeId
// 가게 미션 생성(POST)
@PostMapping("/v1/stores/{storeId}/missions")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

v1처럼 버전 관리를 뜻하는 Path에 대해, 보통의 버전 관리는 메소드 단위가 아닌 Controller 단위로 이루어집니다. 따라서 코드의 양을 줄일 수 있게 RequestMapping에 같이 명시하는 것을 권장드립니다.

@LATE-BL00MER LATE-BL00MER merged commit 35386f5 into Jinyong May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants