Skip to content

[Refactor]#65 getMyLikedCommunities N+1 쿼리 제거 #66

Merged
seokjun01 merged 2 commits intodevelopfrom
refactor/#65/N+1-쿼리-개선
Mar 2, 2026

Hidden character warning

The head ref may contain hidden characters: "refactor/#65/N+1-\ucffc\ub9ac-\uac1c\uc120"
Merged

[Refactor]#65 getMyLikedCommunities N+1 쿼리 제거 #66
seokjun01 merged 2 commits intodevelopfrom
refactor/#65/N+1-쿼리-개선

Conversation

@seokjun01
Copy link
Copy Markdown
Collaborator

@seokjun01 seokjun01 commented Mar 2, 2026

문제

LikeUseCaseImpl.getMyLikedCommunities에서 페이지 내 커뮤니티 수만큼 DB 쿼리가 추가로 발생하는 N+1 문제가 있었습니다.

page.map() 람다 내부에서 commentRepository.countByCommunityId()를 단건으로 반복 호출하기 때문에, 한 페이지에 20개의 게시글이 있으면 댓글 수 조회 쿼리만 20번 추가 실행됩니다.

// 수정 전 — 게시글 수(N)만큼 쿼리 발생
return page.map(community -> {
    long commentCount =
        commentRepository.countByCommunityId(community.getCommunityId()); // N번 호출
    return MyLikedCommunityResponse.of(community, community.getLikeCount(), commentCount);
});

해결

이미 CommunityUseCaseImpl.getMyCommunities에서 동일한 문제를 배치 쿼리로 해결한 패턴이 존재합니다.
getMyLikedCommunities에도 동일한 방식을 적용하여 N번 쿼리를 1번으로 줄였습니다.

// 수정 후 — 배치 조회 1번으로 해결
List<Long> communityIds = page.getContent().stream()
    .map(Community::getCommunityId).toList();
Map<Long, Long> commentCounts = commentRepository.countByCommunityIds(communityIds);

return page.map(community -> {
    long commentCount = commentCounts.getOrDefault(community.getCommunityId(), 0L);
    return MyLikedCommunityResponse.of(community, community.getLikeCount(), commentCount);
});

테스트

LikeUseCaseImplTest.getMyLikedCommunities 테스트를 배치 방식에 맞게 교체했습니다.

테스트 케이스 검증 내용
latestSort_usesBatchCommentCount 배치 메서드(countByCommunityIds) 호출 확인, 단건 메서드(countByCommunityId) 미호출(never()) 확인, commentCount 정상 매핑 확인
popularSort_usesBatchCommentCount 정렬 방식과 무관하게 배치 쿼리 사용 확인
emptyPage_callsBatchWithEmptyList 결과 없을 때 빈 리스트로 배치 호출되는지 확인
missingCommentCountEntry_defaultsToZero Map에 해당 ID가 없을 때 0으로 기본값 처리 확인

변경 파일

  • application/community/usecase/LikeUseCaseImpl.java
  • test/.../usecase/LikeUseCaseImplTest.java

Summary by CodeRabbit

  • Performance Improvements

    • Optimized comment count loading when viewing your liked communities, improving page load performance.
    • Enhanced robustness by ensuring missing comment counts gracefully default to zero.
  • Tests

    • Expanded test coverage for comment retrieval functionality and edge case scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Modified LikeUseCaseImpl.getMyLikedCommunities to batch retrieve comment counts using a single commentRepository.countByCommunityIds() call instead of per-item queries. Updated corresponding tests to verify batch behavior and added edge case tests for empty pages and missing count entries.

Changes

Cohort / File(s) Summary
Batch Comment Count Retrieval
src/main/java/.../community/usecase/LikeUseCaseImpl.java
Collects all community IDs from paginated results, queries comment counts in single batch call via commentByCommunityIds(), uses map-based lookup to populate commentCount for each item, defaults to 0 when entry absent. Adds imports for List and Map.
Test Updates and Edge Cases
src/test/java/.../community/usecase/LikeUseCaseImplTest.java
Replaces per-item countByCommunityId verifications with countByCommunityIds batch call assertions. Adds two new test cases: emptyPage_callsBatchWithEmptyList (validates empty list handling) and missingCommentCountEntry_defaultsToZero (validates default zero for missing map entries). Updates display names and imports for Collections and Map.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • [refactor] #65 N+1 쿼리 개선 #65: Directly addresses the same N+1 query optimization in getMyLikedCommunities by consolidating per-item comment count lookups into a single batch operation.

Poem

🐰 Hop hop, the queries did sing,

Once many, now one—a batching thing!

No more the N+1 dance so slow,

One call to fetch, watch databases glow! ✨

Maps and defaults make the logic right,

Performance gains, what a delight! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: refactoring to eliminate N+1 queries in getMyLikedCommunities by replacing per-item lookups with batch queries, which directly matches the changeset content.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#65/N+1-쿼리-개선

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

@seokjun01 seokjun01 changed the title refactor: getMyLikedCommunities N+1 쿼리 제거 — 단건 조회를 배치 조회로 교체 [Refactor]#65 getMyLikedCommunities N+1 쿼리 제거 Mar 2, 2026
@seokjun01 seokjun01 self-assigned this Mar 2, 2026
@seokjun01 seokjun01 requested a review from jinisim March 2, 2026 09:22
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (3)
src/test/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImplTest.java (2)

19-20: Move new java.util imports into the main Java import block.

These new Java imports are currently below org.* imports, which breaks the project’s import grouping convention.

♻️ Proposed change
 import java.util.List;
 import java.util.Optional;
+import java.util.Collections;
+import java.util.Map;
 
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.Collections;
-import java.util.Map;
As per coding guidelines: Import grouping follows typical order: `java` → `jakarta`/`javax` → `org` → `lombok` → project packages.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImplTest.java`
around lines 19 - 20, The new imports java.util.Collections and java.util.Map
are placed below org.* imports in LikeUseCaseImplTest.java; move these two
import lines up into the primary Java import block above the org.* imports so
the file follows the project import grouping order (java → jakarta/javax → org →
lombok → project). Locate the import statements for Collections and Map and
reposition them with other java.* imports at the top of the import section.

263-282: Add an explicit commentCount assertion in the popular-sort test.

This test verifies repository interaction, but asserting the mapped commentCount value too would better match the test intent and catch mapping regressions.

♻️ Proposed assertion addition
 			// Then
 			assertThat(result.getContent()).hasSize(1);
+			assertThat(result.getContent().get(0).commentCount()).isZero();
 			verify(likeRepository).findMyLikedCommunitiesPopular(eq(10L), eq(CommunityField.TIP), any());
 			verify(commentRepository).countByCommunityIds(List.of(1L));
 			verify(commentRepository, never()).countByCommunityId(anyLong());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImplTest.java`
around lines 263 - 282, The test popularSort_usesBatchCommentCount currently
verifies repository calls but omits asserting the mapped comment count on the
response; update the test method popularSort_usesBatchCommentCount to also
assert that the returned MyLikedCommunityResponse (from
sut.getMyLikedCommunities(...)) has the expected commentCount (e.g., 0L as
provided by commentRepository.countByCommunityIds(Map.of(1L,0L))). Locate the
Slice<MyLikedCommunityResponse> result, get its single element
(result.getContent().get(0)) and add an assertion against its commentCount
getter to ensure mapping from commentRepository.countByCommunityIds is
validated.
src/main/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImpl.java (1)

89-92: Consider short-circuiting empty ID lists before repository call.

When page is empty, skipping countByCommunityIds avoids an unnecessary call and removes potential empty-IN query edge cases.

♻️ Proposed change
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@
 		List<Long> communityIds = page.getContent().stream()
 			.map(Community::getCommunityId).toList();
-		Map<Long, Long> commentCounts = commentRepository.countByCommunityIds(communityIds);
+		Map<Long, Long> commentCounts = communityIds.isEmpty()
+			? Collections.emptyMap()
+			: commentRepository.countByCommunityIds(communityIds);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImpl.java`
around lines 89 - 92, If page.getContent() is empty, avoid calling
commentRepository.countByCommunityIds on an empty list: check
page.getContent().isEmpty() (or communityIds.isEmpty()) after building
communityIds and return an empty commentCounts map (e.g.,
Collections.emptyMap()) or skip the repository call; update the flow in
LikeUseCaseImpl around the page -> communityIds -> commentCounts sequence so
countByCommunityIds is only invoked when communityIds is non-empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/main/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImpl.java`:
- Around line 89-92: If page.getContent() is empty, avoid calling
commentRepository.countByCommunityIds on an empty list: check
page.getContent().isEmpty() (or communityIds.isEmpty()) after building
communityIds and return an empty commentCounts map (e.g.,
Collections.emptyMap()) or skip the repository call; update the flow in
LikeUseCaseImpl around the page -> communityIds -> commentCounts sequence so
countByCommunityIds is only invoked when communityIds is non-empty.

In
`@src/test/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImplTest.java`:
- Around line 19-20: The new imports java.util.Collections and java.util.Map are
placed below org.* imports in LikeUseCaseImplTest.java; move these two import
lines up into the primary Java import block above the org.* imports so the file
follows the project import grouping order (java → jakarta/javax → org → lombok →
project). Locate the import statements for Collections and Map and reposition
them with other java.* imports at the top of the import section.
- Around line 263-282: The test popularSort_usesBatchCommentCount currently
verifies repository calls but omits asserting the mapped comment count on the
response; update the test method popularSort_usesBatchCommentCount to also
assert that the returned MyLikedCommunityResponse (from
sut.getMyLikedCommunities(...)) has the expected commentCount (e.g., 0L as
provided by commentRepository.countByCommunityIds(Map.of(1L,0L))). Locate the
Slice<MyLikedCommunityResponse> result, get its single element
(result.getContent().get(0)) and add an assertion against its commentCount
getter to ensure mapping from commentRepository.countByCommunityIds is
validated.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0fd3f4 and 7285db9.

📒 Files selected for processing (2)
  • src/main/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImpl.java
  • src/test/java/tave/crezipsa/crezipsa/application/community/usecase/LikeUseCaseImplTest.java

@seokjun01 seokjun01 merged commit 5ff3348 into develop Mar 2, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 9, 2026
3 tasks
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.

1 participant