Skip to content

Conversation

@leegwichan
Copy link
Member

@leegwichan leegwichan commented Aug 16, 2025

✨ 개요

  • '마이페이지'에서 사용할 "내가 올린 스트리 조회 API" 구현
  • @EntityGraph를 이용한 조회 쿼리 횟수 최적화

🧾 관련 이슈

closed #170

🔍 참고 사항 (선택)

Summary by CodeRabbit

  • 신규 기능
    • 회원 본인 스토리 목록 조회 API 추가(/api/stories/member). 페이지/사이즈 쿼리 지원, 최신순 정렬, 항목에 스토리 ID/이미지 URL/가게명 포함. 인증 필요.
  • 개선
    • 매장 카카오ID 기반 스토리 조회 성능 및 응답 일관성 개선.
    • 컨트롤러 레벨 요청 파라미터 검증 강화(페이지/사이즈 범위 검사로 잘못된 요청 차단).
  • 문서
    • 회원 스토리 목록 API 문서 및 예시 응답 추가, 인증·오류 케이스 포함.
  • 테스트
    • 서비스·컨트롤러·문서화 테스트 보강: 페이징, 정렬, 권한 및 오류 시나리오 추가.

@coderabbitai
Copy link

coderabbitai bot commented Aug 16, 2025

Walkthrough

내가 올린 스트리 조회를 위한 신규 엔드포인트(GET /api/stories/member)와 응답 DTO(StoriesInMemberResponse, StoryInMemberResponse)를 추가하고, 서비스·레포지토리 계층에 회원별 페이지네이션 조회를 구현했다. 컨트롤러들에 @validated가 적용되었고, 테스트 및 문서화가 이에 맞게 보강되었다.

Changes

Cohort / File(s) Summary
Controller API 추가
src/main/java/eatda/controller/story/StoryController.java
GET /api/stories/member 엔드포인트 추가. page/size 검증(@Min/@max, 기본값), LoginMember 사용, 서비스 위임 후 StoriesInMemberResponse 반환. 클래스에 @Validated 추가.
응답 DTO 추가
src/main/java/eatda/controller/story/StoriesInMemberResponse.java, src/main/java/eatda/controller/story/StoryInMemberResponse.java
회원별 스토리 목록 응답 레코드 2종 추가. Story 기반 보조 생성자 포함(StoryInMemberResponse).
Service 구현/수정
src/main/java/eatda/service/story/StoryService.java
회원별 페이지네이션 조회 메서드 추가: getPagedStoryByMemberId(long memberId, int page, int size) → StoriesInMemberResponse 반환. 기존 getPagedStoryPreviews의 Page→List 매핑 방식 일부 변경.
Repository 확장
src/main/java/eatda/repository/story/StoryRepository.java
findAllByMemberIdOrderByCreatedAtDesc(Long memberId, Pageable pageable) 추가. 기존 findAllByStoreKakaoIdOrderByCreatedAtDesc@EntityGraph(attributePaths = {"member"}) 추가.
컨트롤러 테스트/문서화
src/test/java/eatda/controller/story/StoryControllerTest.java, src/test/java/eatda/document/story/StoryDocumentTest.java
/api/stories/member 관련 테스트 및 RestDocs 문서 추가/수정. Authorization 헤더 사용으로 인증 토큰 주입 변경. Kakao ID 조회 테스트 보강/구조 조정.
서비스 테스트/픽스처
src/test/java/eatda/service/story/StoryServiceTest.java, src/test/java/eatda/service/BaseServiceTest.java
getPagedStoryByMemberId 단위 테스트 추가. StoryGenerator 픽스처 필드 주입.
테스트 유틸
src/test/java/eatda/controller/BaseControllerTest.java
accessToken(Member) 오버로드 추가(멤버 지정 토큰 발급).
기타 컨트롤러에 검증 활성화
src/main/java/eatda/controller/cheer/CheerController.java, src/main/java/eatda/controller/store/StoreController.java
클래스 수준에 @Validated 애너테이션 추가(파라미터 제약 활성화).

Sequence Diagram(s)

sequenceDiagram
  participant C as Client
  participant SC as StoryController
  participant SS as StoryService
  participant SR as StoryRepository
  participant DB as Database

  C->>SC: GET /api/stories/member?page&size (Authorization)
  SC->>SS: getPagedStoryByMemberId(memberId, page, size)
  SS->>SR: findAllByMemberIdOrderByCreatedAtDesc(memberId, Pageable)
  SR->>DB: Query stories by memberId (paged, desc)
  DB-->>SR: List<Story>
  SR-->>SS: List<Story>
  SS-->>SC: StoriesInMemberResponse
  SC-->>C: 200 OK + JSON
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
내가 올린 스트리 조회 API 구현 (#170)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
카카오ID 조회 메서드에 @EntityGraph 추가 (src/main/java/eatda/repository/story/StoryRepository.java) 성능/조회 전략 변경으로, 이슈의 '내가 올린 스트리 조회' 핵심 구현과 직접적인 관련이 없음.
getPagedStoryPreviews의 Page→List 매핑 리팩터링 (src/main/java/eatda/service/story/StoryService.java) 기존 미리보기 로직 내부 리팩터링으로, 신규 회원별 조회 기능 요구사항과는 별개 변경으로 보임.

Poem

토끼가 말하네, 깡총 깡총 코드 밭에서 🥕
"내가 올린 이야기, 한 장씩 페이지로 꺼내자!"
멤버의 토큰 쓱—인증하고 요청하면,
가게 이름 반짝이며 이야기들이 줄지어.
깡총—응답은 200, 당근으로 축하해!

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ 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/PRODUCT-258

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (10)
src/main/java/eatda/controller/story/StoryInMemberResponse.java (1)

3-3: NPE 방지: 편의 생성자에서 story null 체크 추가 제안

story가 null인 경우 즉시 NPE가 발생합니다. 방어적으로 null을 명시적으로 검증해두면 문제를 빨리 드러내고 디버깅에 도움이 됩니다.

적용 제안(diff):

 package eatda.controller.story;

 import eatda.domain.story.Story;
+import java.util.Objects;

 public record StoryInMemberResponse(
         Long id,
         String imageUrl,
         String storeName
 ) {

     public StoryInMemberResponse(Story story, String imageUrl) {
-        this(story.getId(), imageUrl, story.getStoreName());
+        this(Objects.requireNonNull(story, "story must not be null").getId(),
+             imageUrl,
+             story.getStoreName());
     }
 }

Also applies to: 11-13

src/main/java/eatda/controller/story/StoriesInMemberResponse.java (1)

5-6: 응답 불변성 강화를 위한 List 방어적 복사 제안

호출자가 stories 리스트를 변경할 수 없도록 불변성을 보장하면 예측 가능성이 높아집니다.

적용 제안(diff):

 public record StoriesInMemberResponse(List<StoryInMemberResponse> stories) {
+    public StoriesInMemberResponse {
+        // JDK 10+ : 불변 리스트로 방어적 복사
+        stories = List.copyOf(stories);
+    }
 }

JDK 8/9를 사용한다면 아래 대안도 고려 가능합니다(참고용):

public StoriesInMemberResponse {
    stories = Collections.unmodifiableList(new ArrayList<>(stories));
}
src/main/java/eatda/controller/story/StoryController.java (1)

64-65: 사소한 개선: ResponseEntity.ok(...) 사용으로 간결화

동일한 의미이므로 더 간결한 형태를 권장합니다.

적용 제안(diff):

-        return ResponseEntity.status(HttpStatus.OK)
-                .body(response);
+        return ResponseEntity.ok(response);
src/test/java/eatda/service/story/StoryServiceTest.java (1)

273-319: 추가 케이스 제안: 타 회원 스토리 배제·범위 밖 페이지

기능은 충분히 검증되지만, 아래 보완 테스트를 고려해보세요.

  • 타 회원의 스토리가 섞여 있는 경우에도 지정 회원의 스토리만 반환되는지 확인
  • 총 페이지 수를 초과하는 page 요청 시 빈 목록을 반환하는지 확인

원하시면 위 두 케이스를 포함한 테스트 메서드를 바로 추가해드릴게요.

src/main/java/eatda/service/story/StoryService.java (2)

63-69: Stream 매핑 구현 간결하고 명확함 + Projection 고려 여지

현재 구현은 명확하고 충분히 읽기 좋습니다. 다만 트래픽/데이터량이 커질 경우, 리스트 엔드포인트에서 엔티티 전체를 로딩하기보다 JPA 인터페이스 기반 Projection(예: id, imageKey만)으로 조회한 후 URL 변환을 하는 방식이 성능에 유리할 수 있습니다.

원하시면 Story의 요약 Projection을 정의하고 Repository 메서드를 Projection으로 반환하도록 시그니처를 제안드릴 수 있습니다.


106-115: 회원별 페이지네이션 조회 구현 LGTM + 인덱스 권장

구현과 응답 매핑이 요구사항에 부합합니다. 성능 측면에서 아래 개선을 고려해 주세요.

  • DB 인덱스: WHERE member_id = ? AND ORDER BY created_at DESC 패턴을 위해 (member_id, created_at DESC) 복합 인덱스 추가 추천
  • 네이밍: 반환 타입이 Page가 아닌 List wrapper이므로 메서드명에서 “Paged”를 빼는 것도 고려 가능 (선택 사항)

예시 (마이그레이션):

  • Flyway: Vxxx__add_index_story_member_created_at.sql
    CREATE INDEX idx_story_member_created_at_desc ON story (member_id, created_at DESC);

원하시면 Flyway 마이그레이션 스크립트 템플릿을 생성해드릴게요.

src/main/java/eatda/repository/story/StoryRepository.java (1)

13-16: 새 쿼리 메서드 및 EntityGraph 적용 적절

  • findAllByMemberIdOrderByCreatedAtDesc: 서비스 요구사항과 일치합니다.
  • @entitygraph(member): 카카오ID 기반 상세 리스트에서 member 정보를 즉시 로딩해 N+1을 회피할 수 있어 적절합니다.

추가로, memberId + createdAt DESC 복합 인덱스를 DB에 추가하면 페이지네이션 성능을 더 확보할 수 있습니다.

src/test/java/eatda/document/story/StoryDocumentTest.java (1)

253-259: 응답 필드 네이밍 일관성 검토 제안

회원별 목록에서 story id 필드를 stories[].id로, 다른 엔드포인트(예: 프리뷰/카카오ID)에서는 stories[].storyId를 사용합니다. 의도된 차별화라면 그대로 유지해도 되지만, 클라이언트 입장에서 동일 개념의 필드명이 통일되면 사용성이 좋아집니다.

이 네이밍 차이가 의도된 것인지 확인 부탁드립니다. 의도되었다면, API 문서에 차이를 명시해 두는 것도 방법입니다.

src/test/java/eatda/controller/story/StoryControllerTest.java (2)

16-16: HttpHeaders 상수 일관 사용 제안

HttpHeaders.AUTHORIZATION를 임포트하셨으니, 본 파일 내 다른 테스트에서도 문자열 리터럴 "Authorization" 대신 상수를 통일해 쓰면 가독성과 오타 방지가 좋아집니다.

예: header(HttpHeaders.AUTHORIZATION, accessToken())


140-171: 중첩 클래스 구조 평탄화 제안

GetStoriesByKakaoId가 GetStoriesByMemberId 내부에 중첩되어 있습니다. 기능 도메인이 다른 테스트 스위트는 동일 계층으로 두면 네이밍/탐색성이 좋아집니다.

원하시면 중첩 해제한 구조로 리팩터 제안 드릴게요.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f87e69b and c5859ad.

📒 Files selected for processing (10)
  • src/main/java/eatda/controller/story/StoriesInMemberResponse.java (1 hunks)
  • src/main/java/eatda/controller/story/StoryController.java (1 hunks)
  • src/main/java/eatda/controller/story/StoryInMemberResponse.java (1 hunks)
  • src/main/java/eatda/repository/story/StoryRepository.java (1 hunks)
  • src/main/java/eatda/service/story/StoryService.java (3 hunks)
  • src/test/java/eatda/controller/BaseControllerTest.java (1 hunks)
  • src/test/java/eatda/controller/story/StoryControllerTest.java (2 hunks)
  • src/test/java/eatda/document/story/StoryDocumentTest.java (3 hunks)
  • src/test/java/eatda/service/BaseServiceTest.java (2 hunks)
  • src/test/java/eatda/service/story/StoryServiceTest.java (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/test/java/eatda/service/story/StoryServiceTest.java (1)
src/test/java/eatda/document/story/StoryDocumentTest.java (5)
  • Nested (45-123)
  • Nested (125-163)
  • Nested (165-237)
  • Nested (239-307)
  • Nested (309-371)
src/test/java/eatda/controller/story/StoryControllerTest.java (1)
src/test/java/eatda/document/story/StoryDocumentTest.java (5)
  • Nested (45-123)
  • Nested (125-163)
  • Nested (165-237)
  • Nested (239-307)
  • Nested (309-371)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (11)
src/test/java/eatda/controller/BaseControllerTest.java (1)

136-138: 테스트 편의 메서드 추가 LGTM

특정 멤버로 토큰 발급이 필요한 테스트 시나리오에 유용합니다. 단순·명확하며 기존 흐름과도 일관적입니다.

src/test/java/eatda/service/BaseServiceTest.java (1)

13-13: 스토리 픽스처 주입 LGTM

StoryGenerator 주입으로 서비스 레벨 테스트 작성이 더 수월해졌습니다. 기존 픽스처 구성과도 자연스럽게 어우러집니다.

Also applies to: 60-62

src/main/java/eatda/controller/story/StoryController.java (1)

57-66: 신규 “내가 올린 스트리 조회” 엔드포인트 설계 적절합니다

  • 경로: GET /api/stories/member
  • 파라미터 검증: page(최소 0), size(1~50, 기본 5)
  • 로그인 회원 컨텍스트(LoginMember) 기반 조회

서비스 계층과 DTO 구성(StoriesInMemberResponse, StoryInMemberResponse)과도 일관됩니다.

src/test/java/eatda/service/story/StoryServiceTest.java (3)

276-292: 회원별 정렬·매핑 검증 테스트 LGTM

동일 회원의 스토리를 createdAt DESC로 정렬하여 최신순으로 반환되는지, id와 storeName 필드가 정확히 매핑되는지 검증이 깔끔합니다.


294-309: 페이지네이션 동작 검증 LGTM

page=1, size=2에서 기대한 1건(story1)만 반환되는 케이스가 잘 잡혀 있습니다. 페이징 경계 조건을 확인하는 좋은 테스트입니다.


311-319: 존재하지 않는 회원 ID에 대한 빈 결과 검증 LGTM

비존재 memberId 요청 시 빈 목록 반환을 명확히 보장합니다.

src/main/java/eatda/service/story/StoryService.java (1)

99-103: LGTM: toList()로 마무리하는 매핑

기존 동작을 해치지 않으면서 가독성이 좋습니다.

src/test/java/eatda/document/story/StoryDocumentTest.java (3)

242-251: 요청 스펙 문서화 적절 (인증/페이징 파라미터)

Authorization 헤더와 page/size 제약 설명이 명확합니다.


261-282: 성공 케이스 문서화 LGTM

Mockito stubbing과 RestDocs 문서화 흐름이 명확합니다. 페이지/사이즈 파라미터도 예시로 포함되어 재현성 좋습니다.


284-307: 에러 케이스 문서화 LGTM

권한/토큰/회원ID 오류에 대한 파라미터화 테스트로 문서 일관성 보장 좋습니다.

src/test/java/eatda/controller/story/StoryControllerTest.java (1)

112-137: 회원의 스토리 목록 조회 컨트롤러 테스트 LGTM

  • 인증 헤더 포함, page/size 쿼리 파라미터, 최신순 정렬 검증 모두 적절합니다.
  • 응답 DTO(StoriesInMemberResponse)의 필드(id) 기준 검증도 계약과 일치합니다.

@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/eatda/controller/store/StoreController.java (1)

19-19: 클래스 레벨 @validated 추가 적절 — 페이징 파라미터 공통화 제안

각 컨트롤러에서 반복되는 page/size 제약을 공통 DTO(예: PagingParams)로 캡슐화하면 재사용성과 유지보수성이 좋아집니다. @PositiveOrZero / @positive 사용도 읽기성이 더 좋습니다.

예시:

// 공통 DTO
public record PagingParams(
    @jakarta.validation.constraints.PositiveOrZero int page,
    @jakarta.validation.constraints.Positive @jakarta.validation.constraints.Max(50) int size
) {}

// 사용 예시
public ResponseEntity<StoresResponse> getStores(PagingParams paging, @RequestParam(required = false) String category) {
    StoresResponse response = storeService.getStores(paging.page(), paging.size(), category);
    return ResponseEntity.ok(response);
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c5859ad and 764485c.

📒 Files selected for processing (3)
  • src/main/java/eatda/controller/cheer/CheerController.java (2 hunks)
  • src/main/java/eatda/controller/store/StoreController.java (1 hunks)
  • src/main/java/eatda/controller/story/StoryController.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/eatda/controller/story/StoryController.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/eatda/controller/store/StoreController.java (2)
src/main/java/eatda/controller/cheer/CheerController.java (1)
  • Validated (24-59)
src/main/java/eatda/controller/story/StoryController.java (1)
  • Validated (24-78)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (2)
src/main/java/eatda/controller/cheer/CheerController.java (1)

15-15: 컨트롤러 수준 검증 활성화를 위한 @validated import 추가 적절

클래스 레벨 검증을 위한 준비가 잘 되어 있습니다. 이 변경으로 메서드 파라미터의 Bean Validation 제약조건이 유효하게 동작합니다.

src/main/java/eatda/controller/store/StoreController.java (1)

13-13: 검증 활성화를 위한 @validated import 추가 적절

메서드 파라미터 검증을 위한 필수 import가 반영되었습니다.

Copy link
Member

@lvalentine6 lvalentine6 left a comment

Choose a reason for hiding this comment

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

이번 PR도 고생하셨습니다! 🍏
마이페이지 API가 추가되었으니
이미지 기능을 개선하는 작업에 여기도 포함을 해야겠군요... 😭

@leegwichan leegwichan merged commit e21ba4e into develop Aug 16, 2025
8 checks passed
@leegwichan leegwichan deleted the feat/PRODUCT-258 branch August 16, 2025 13:16
@github-actions
Copy link

🎉 This PR is included in version 1.4.0-develop.87 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions
Copy link

🎉 This PR is included in version 1.8.0-develop.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions
Copy link

🎉 This PR is included in version 1.8.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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.

[PRODUCT-258] [Feat] 내가 올린 스토리 조회 API 구현

3 participants