Skip to content

Conversation

@leegwichan
Copy link
Member

@leegwichan leegwichan commented Aug 15, 2025

✨ 개요

  • 기능 추가
    • '최신 응원 검색 API'에 회원 정보 추가
  • 성능 개선
    • spring.jpa.properties 일부 설정을 통해 배치 형식으로 쿼리를 처리하도록 개선
    • @EntityGraph를 활용하여 쿼리 횟수를 N+1 개에서 1개로 횟수 감소

🧾 관련 이슈

closed #164

🔍 참고 사항 (선택)

Summary by CodeRabbit

  • 신기능
    • 응원 목록 응답에 작성자 정보(memberId, memberNickname) 필드가 추가되었습니다.
  • 성능
    • 목록 조회 시 관련 엔티티를 함께 로딩하여 응답 속도를 개선했습니다.
    • 배치 쓰기 및 정렬 설정으로 데이터베이스 쓰기 효율을 향상했습니다.
  • 문서
    • 응원 조회 API 문서에 신규 작성자 필드가 반영되었습니다.
  • 테스트
    • 변경된 응답 스키마에 맞춰 통합 테스트와 예시 응답을 업데이트했습니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

CheerPreviewResponse가 Store 파라미터 없이 Cheer와 imageUrl만으로 생성되도록 변경되고, 응답 레코드에 memberId와 memberNickname이 추가되었습니다. 최신 응원 조회 관련 리포지토리 메서드에 EntityGraph가 적용되었습니다. 서비스 매핑과 REST Docs 테스트가 이에 맞게 수정되었고, application.yml/test 설정에 Hibernate 배치 관련 옵션이 추가되었습니다.

Changes

Cohort / File(s) Summary
DTO 응답 스키마 변경
src/main/java/eatda/controller/cheer/CheerPreviewResponse.java
생성자에서 Store 파라미터 제거(Cheer, String), store 필드들을 cheer.getStore()로 유도, memberId/ memberNickname 레코드 컴포넌트 추가, 불필요한 Store import 제거
서비스 매핑 수정
src/main/java/eatda/service/cheer/CheerService.java
toCheersResponse에서 CheerPreviewResponse 생성 시 Store 인자 제거, 이미지 URL 로직 유지
리포지토리 페치 전략 보강
src/main/java/eatda/repository/cheer/CheerRepository.java
findAllByOrderByCreatedAtDesc에 store, member EAGER 로딩(EntityGraph) 추가; findAllByStoreOrderByCreatedAtDesc에 member EAGER 로딩(EntityGraph) 추가
운영/테스트 환경 설정 추가
src/main/resources/application.yml, src/test/resources/application.yml
Hibernate jdbc.batch_size=100, order_inserts=true, order_updates=true 추가; 기존 format_sql 유지
문서/테스트 업데이트
src/test/java/eatda/document/store/CheerDocumentTest.java
최신 응원 조회 응답에 memberId, memberNickname 필드 반영; 샘플/기대 응답 및 생성자 사용부 업데이트

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Controller as CheerController
  participant Service as CheerService
  participant Repo as CheerRepository
  participant S3 as ImageStorage

  Client->>Controller: GET /cheers/latest?page=...
  Controller->>Service: toCheersResponse(pageable)
  Service->>Repo: findAllByOrderByCreatedAtDesc(pageable) [EntityGraph: store, member]
  Repo-->>Service: List<Cheer> (store, member eager)
  loop for each Cheer
    Service->>S3: getPreSignedUrl(cheer.imageKey)
    S3-->>Service: imageUrl
    Service-->>Service: new CheerPreviewResponse(cheer, imageUrl)
  end
  Service-->>Controller: List<CheerPreviewResponse>
  Controller-->>Client: 200 OK (JSON with memberId, memberNickname)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
최신 응원 검색 API 응답에 member 정보 추가: memberId 포함 (#164)
최신 응원 검색 API 응답에 member 정보 추가: memberName 포함 (#164) 필드명이 memberNickname으로 추가되어 명세의 memberName과 불일치

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Hibernate 배치 설정 추가 (src/main/resources/application.yml) 이슈 #164는 응답에 멤버 정보 추가만 요구하며 퍼포먼스/배치 설정 변경은 명시되어 있지 않음.
Hibernate 배치 설정 추가 (src/test/resources/application.yml) 테스트 프로파일의 JPA 배치 관련 설정 변경은 이슈 목적과 직접 관련이 없음.

Poem

새 응원 바람에 귀 쫑긋 세우고,
닉네임 반짝 memberId도 출동!
가게 향기 솔솔, 사진 URL 퐁퐁—
그래프 탄탄, N+1은 꽁꽁.
깡총깡총 배포길, 오늘도 굿 점프! 🐰✨

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-255

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.

@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 (5)
src/main/java/eatda/repository/cheer/CheerRepository.java (2)

19-20: Store 기준 최신 응원 조회에도 store 프록시 초기화가 필요하면 함께 페치하는 것을 고려

현재 @EntityGraph(attributePaths = {"member"})만 지정되어 있습니다. 만약 CheerInStoreResponse에서 cheer.getStore()의 다른 필드(예: 이름/주소 등)를 접근한다면, Store 연관도 함께 페치하는 편이 안전합니다. 일관성과 추후 변경에 대비해 {"store", "member"}로 맞추는 것을 권장합니다.

적용 예시:

-    @EntityGraph(attributePaths = {"member"})
+    @EntityGraph(attributePaths = {"store", "member"})
     List<Cheer> findAllByStoreOrderByCreatedAtDesc(Store store, Pageable pageable);

16-20: 중복 attributePaths 문자열을 줄이려면 NamedEntityGraph 사용 고려

엔티티(예: Cheer) 측에 NamedEntityGraph를 정의해두면 재사용성이 올라가고 오타 위험이 줄어듭니다.

예시(엔티티에 추가):

@NamedEntityGraph(
  name = "Cheer.withStoreAndMember",
  attributeNodes = {
    @NamedAttributeNode("store"),
    @NamedAttributeNode("member")
  }
)
@Entity
public class Cheer { ... }

리포지토리에서는 다음과 같이 사용할 수 있습니다:

@EntityGraph(value = "Cheer.withStoreAndMember")
List<Cheer> findAllByOrderByCreatedAtDesc(Pageable pageable);
src/main/java/eatda/service/cheer/CheerService.java (1)

74-74: 이미지 키가 null인 경우의 안전성 확보

cheer.getImageKey()가 null일 수 있다면 getPreSignedUrl 호출에서 NPE/예외가 날 수 있습니다. 삼항 연산자로 null을 안전하게 처리하는 것을 권장합니다.

적용 예시:

-                .map(cheer -> new CheerPreviewResponse(cheer, imageStorage.getPreSignedUrl(cheer.getImageKey())))
+                .map(cheer -> new CheerPreviewResponse(
+                        cheer,
+                        cheer.getImageKey() != null ? imageStorage.getPreSignedUrl(cheer.getImageKey()) : null
+                ))
src/test/java/eatda/document/store/CheerDocumentTest.java (2)

159-162: GetCheers 응답 문서에 memberId/memberNickname 추가 LGTM — PII/optional 여부만 확인 바랍니다

  • 스키마/타입 정의와 위치(cheerDescription 뒤) 모두 적절합니다.
  • 다만 탈퇴/비공개/제재 등의 케이스에서 닉네임/회원 ID가 마스킹되거나 결측될 가능성이 있다면 필드를 optional 처리하거나 설명에 정책을 명시해 주세요. 현재는 둘 다 필수로 문서화되어 있습니다.

선택적으로 아래와 같이 optional 표시를 고려해 보세요(정책에 따라 적용):

-                        fieldWithPath("cheers[].cheerDescription").type(STRING).description("응원 내용"),
-                        fieldWithPath("cheers[].memberId").type(NUMBER).description("응원 작성자 회원 ID"),
-                        fieldWithPath("cheers[].memberNickname").type(STRING).description("응원 작성자 닉네임")
+                        fieldWithPath("cheers[].cheerDescription").type(STRING).description("응원 내용"),
+                        fieldWithPath("cheers[].memberId").type(NUMBER).description("응원 작성자 회원 ID").optional(),
+                        fieldWithPath("cheers[].memberNickname").type(STRING).description("응원 작성자 닉네임").optional()

또한 동일 도메인 API 간 명명 일관성 점검을 권장합니다. 본 테스트의 최신 응원 조회는 cheerDescription, 가게별 응원 조회는 description을 사용하고 있어 소비자 입장에서 혼동될 수 있습니다(변경 여부는 별도 논의 권장).


170-173: 샘플 응답에 새 필드 포함 OK — 본문 값 검증 assertion을 추가해 회귀 방지 강화 제안

현재 상태코드만 검증하고 있어, 새로 추가된 memberId/memberNickname 매핑이 깨져도 테스트가 통과할 수 있습니다. 간단한 본문 검증을 추가하면 회귀를 예방할 수 있습니다.

  • 추가 import:

    • import static org.hamcrest.Matchers.equalTo;
  • then 체인의 본문 검증 예시:

.then()
    .statusCode(200)
    .body("cheers.size()", equalTo(2))
    .body("cheers[0].memberId", equalTo(5))
    .body("cheers[0].memberNickname", equalTo("커찬"))
    .body("cheers[1].memberId", equalTo(8))
    .body("cheers[1].memberNickname", equalTo("찬커"));

테스트 명 세부 표현도 최신 용어(“응원 검색”)로 맞추면 가독성이 더 좋아집니다(선택).

📜 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 8d683aa and b36108c.

📒 Files selected for processing (6)
  • src/main/java/eatda/controller/cheer/CheerPreviewResponse.java (1 hunks)
  • src/main/java/eatda/repository/cheer/CheerRepository.java (1 hunks)
  • src/main/java/eatda/service/cheer/CheerService.java (1 hunks)
  • src/main/resources/application.yml (1 hunks)
  • src/test/java/eatda/document/store/CheerDocumentTest.java (2 hunks)
  • src/test/resources/application.yml (1 hunks)
🔇 Additional comments (4)
src/test/resources/application.yml (1)

32-35: 테스트 프로파일에도 동일한 배치 옵션을 반영한 점 좋습니다

운영 설정과 테스트 설정의 일관성이 확보되어 회귀 리스크가 낮아졌습니다. H2에서도 Hibernate 레벨의 배치 설정은 적용 가능하므로 문제 없습니다.

src/main/java/eatda/repository/cheer/CheerRepository.java (1)

16-18: 최신 응원 조회에서 N+1 완화 위한 @entitygraph 적용 좋습니다

store, member를 미리 로딩해 응답 매핑 시 Lazy 로딩이 줄어듭니다. open-in-view: false 환경과도 잘 맞습니다.

src/main/java/eatda/controller/cheer/CheerPreviewResponse.java (2)

14-15: 요구사항 용어 확인: memberName vs memberNickname

PR 목적에는 “memberName 추가”로 기재되어 있으나, 필드명은 memberNickname입니다. 실제로 노출해야 하는 값이 실명(name)인지 닉네임(nickname)인지 확인이 필요합니다. API 컨트랙트/문서와의 용어 일치가 중요합니다.


18-30: 생성자 단순화 및 파생 필드 계산 방식은 적절합니다

CheerimageUrl만 받아 필요한 정보를 파생시키는 접근이 읽기 좋고, @EntityGraph와 조합 시 Lazy 예외 위험도 낮습니다.

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도 고생하셨습니다! 🎉
cheer_tag 쓰기시 성능 고려해서 배치 도입하신건가요??

@leegwichan
Copy link
Member Author

넵! 여러개를 저장하는 로직이 도입됨에 따라 설정까지 같이 추가했습니다!

@leegwichan leegwichan merged commit f87e69b into develop Aug 16, 2025
10 checks passed
@leegwichan leegwichan deleted the feat/PRODUCT-255 branch August 16, 2025 00:38
@github-actions
Copy link

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

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-255] [Feat] '최신 응원 검색 API'의 유저 정보 추가

3 participants