Skip to content

Conversation

@NCookies
Copy link
Collaborator

@NCookies NCookies commented Jun 25, 2025

작업 내용

  • 토론글 목록 조회 쿼리 최적화

  • 성능 테스트 위해 100만건 데이터 삽입

  • 구현 시 목표

    • 토론글 목록 조회 시 관련된 추가 데이터 조회
      • 추천/비추천수, 댓글수, 유저의 추천 여부
    • 정렬 기준
      • 1차로 유저가 선택한 기준대로 정렬하고, 해당 조건이 동일하다면 2차로 최신순(ID)으로 정렬
      • 1차 : 인기순(upvote-downvote), 추천순(upvote), 최신순 중 하나
      • 2차 : 최신순
      • 기본 정렬은 인기순임
    • 기존의 아키텍처를 유지하면서 쿼리 및 관련 설정만 변경하여 단기간에 성능 개선
  • 성능 테스트 방식

    1. discussion 백만 건 데이터 삽입
    2. 성능 측정 : 실행 시간, 쿼리 개수 등이 지표
    3. 쿼리 최적화
    4. 성능 측정 및 비교
    5. 검증 결과
  • 성능 테스트 결과 및 결론

    • 최초 구현 방식보다 약 67% 성능 향상됨
    • 수 만건의 데이터를 조회하는 상황에서도 개선된 쿼리로 안정적인 성능을 보여줌 (서버 처리 시간 약 120 ms)
    • 커뮤니티 데이터가 몇 백만 단위로 누적되지 않는 이상 현재 방식을 유지할 것 같음
      • 이론 상 추천수, 비추천수, 댓글수 등은 write 시 캐싱하는 방식이 조회 속도가 제일 빠름
      • 그러나 이는 쓰기 과정이 복잡해지는 대신 조회가 간단해지는 것을 트레이드 오프한 것 (데이터 정합성, 동시성 이슈 등 존재)
      • 지금과 같은 소규모의 프로젝트에서는 현재 방식만으로 충분할 것으로 보임

변경 사항

  • 여러 차례에 걸쳐 쿼리 튜닝을 진행함
    • 기존 코드 : 스칼라 서브 쿼리 사용
    • 1차 개선 : 서브 쿼리들을 JOIN + GROUP BY로 대체
    • 2차 개선 : GROUP BY와 ORDER BY에서 많은 리소스를 사용해서 기존 방식보다 성능이 저하됨 -> 쿼리를 분할함

트러블 슈팅


참고 사항


코드 리뷰 전 확인 체크리스트

  • 불필요한 콘솔 로그, 주석 제거
  • 커밋 메시지 컨벤션 준수 (type : )
  • 기능 정상 동작 확인

Summary by CodeRabbit

  • 신규 기능

    • 토론 및 답글 목록에서 생성일, 추천/비추천/답글 수, 사용자별 투표 상태 등 추가 정보 제공.
    • 토론 및 답글 목록 조회 시 정렬(sorting) 및 사용자별 맞춤 정보 지원.
  • 버그 수정

    • 일부 필드명 및 반환 타입 일관성 개선(예: parentId → parentReplyId).
  • 문서화

    • OpenAPI(Swagger) 문서에 토론 및 답글 API 상세 설명 추가.
  • 테스트

    • 대용량 테스트 데이터 생성 도구 및 테스트 클래스 추가.
  • 기타

    • 내부 쿼리 및 리포지토리 구조 개선으로 목록 조회 성능 및 확장성 향상.

NCookies added 10 commits June 26, 2025 00:02
- 추가된 데이터로는 추천수, 댓글수, 추천 여부 등이 있음
- 정렬 기준으로는 베스트(추천수-비추천수), 추천순, 최신순이 있음
- discussion 데이터를 백만 건 삽입하는 코드를 작성함
- 댓글과 추천 데이터는 각각 3배, 8배임
- 기존의 서브쿼리들을 JOIN + GROUP BY 방식으로 변경함
- 그러나 오히려 성능이 떨어짐
- 조회 로직을 ID 조회 + 실제 데이터 조회로 분리함
- ORDER BY와 GROUP BY 과정에서 DB 서버에 훨씬 더 큰 연산 부하를 떠넘겼고, 이 부하가 서브쿼리의 비효율성을 넘어설 정도로 컸다고 추측
- 하나의 쿼리만으로 처리하기 보다는 여러 개로 분리해서 호출하는 방식을 선택함
- 맨 처음 방식에서 쿼리만 분리한 방식
- 성능은 두 번째로 좋음
- 2차 개선 버전으로 쿼리 적용함
- UI에는 적용되지 않더라도 디버깅 시 사용하기 위함
- 댓글은 정렬을 항상 최신순(id 기준 오름차순)으로 사용하므로, 쿼리를 분리하지 않고 JOIN + GROUP BY 만으로 구현함
@NCookies NCookies self-assigned this Jun 25, 2025
@NCookies NCookies added the enhancement New feature or request label Jun 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 25, 2025

Walkthrough

이 변경 사항은 토론(Discussion) 및 답글(Reply) 응답 DTO에 투표 및 답글 수, 사용자 투표 상태 등 추가 필드를 도입하고, 서비스 및 저장소 레이어를 쿼리 결과 DTO를 활용하도록 대대적으로 리팩토링합니다. 또한 대용량 테스트 데이터 생성 유틸리티와 SQL 쿼리 최적화 리소스가 추가되었습니다.

Changes

파일/경로 그룹 변경 요약
src/main/java/org/ezcode/codetest/application/community/dto/response/DiscussionResponse.java, ReplyResponse.java 토론 및 답글 응답 DTO에 투표 수, 답글 수, 생성일, 사용자 투표 상태 등 필드 추가. 팩토리 메서드 추가 및 기존 필드 일부 변경.
src/main/java/org/ezcode/codetest/domain/community/dto/DiscussionQueryResult.java, ReplyQueryResult.java 토론 및 답글 쿼리 결과용 DTO 신설. 투표 수, 답글 수, 사용자 정보, 투표 상태 등 포함.
src/main/java/org/ezcode/codetest/domain/community/dto/VoteResult.java
.../model/VoteResult.java
VoteResult의 패키지 이동 (modeldto).
src/main/java/org/ezcode/codetest/application/community/service/DiscussionService.java, ReplyService.java 토론/답글 조회 메서드가 쿼리 결과 DTO를 사용하도록 변경, 파라미터에 정렬 기준 및 사용자 ID 추가.
src/main/java/org/ezcode/codetest/domain/community/service/DiscussionDomainService.java, ReplyDomainService.java 쿼리 결과 DTO 반환 및 사용자 ID 인자 추가, 저장소 호출 방식 변경.
src/main/java/org/ezcode/codetest/domain/community/repository/DiscussionRepository.java, ReplyRepository.java 토론/답글 조회 메서드가 쿼리 결과 DTO 및 사용자 ID를 사용하도록 시그니처 변경.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionJpaRepository.java, ReplyJpaRepository.java 기존 커스텀 쿼리 메서드 제거, 쿼리 전용 인터페이스 상속 구조로 변경.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepository.java, DiscussionQueryRepositoryImpl.java 토론 쿼리 전용 인터페이스 및 구현체 신설. 정렬/집계/투표 상태 등 포함한 쿼리 메서드 구현.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyQueryRepository.java, ReplyQueryRepositoryImpl.java 답글 쿼리 전용 인터페이스 및 구현체 신설. 답글/자식 답글/투표 상태 등 포함한 쿼리 메서드 구현.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionRepositoryImpl.java, reply/ReplyRepositoryImpl.java 저장소 구현체가 쿼리 결과 DTO 및 사용자 ID를 사용하도록 변경.
src/main/java/org/ezcode/codetest/presentation/community/DiscussionController.java, ReplyController.java 컨트롤러가 정렬, 인증 사용자 파라미터를 받아 서비스에 전달하도록 변경. OpenAPI 문서화 추가.
src/test/java/org/ezcode/codetest/util/TestDataGenerator.java, TestDataGeneratorForDiscussion.java 대용량 토론/답글/투표 등 테스트 데이터 생성 유틸리티 클래스 신설.
src/test/java/org/ezcode/codetest/util/DummyDataInit.java 테스트 데이터 생성기 실행용 통합 테스트 클래스 신설.
src/test/resources/discussion-query-explain.sql 토론 쿼리 최적화 SQL 및 EXPLAIN 예제 리소스 파일 추가.
src/test/resources/application-test.properties spring.application.name 속성 값 변경.
src/test/java/org/ezcode/codetest/domain/community/service/DiscussionVoteDomainServiceTest.java, ReplyVoteDomainServiceTest.java, concurrency/DiscussionVoteServiceConcurrencyTest.java VoteResult 패키지 경로 수정, 불필요한 import 정리 등 사소한 테스트 코드 정비.

Sequence Diagram(s)

sequenceDiagram
    participant Controller as DiscussionController
    participant Service as DiscussionService
    participant Domain as DiscussionDomainService
    participant Repo as DiscussionQueryRepository

    Controller->>Service: getDiscussions(problemId, sortBy, userId, pageable)
    Service->>Domain: getAllDiscussionsByProblemId(problemId, sortBy, userId, pageable)
    Domain->>Repo: findDiscussionIdsByProblemId(problemId, sortBy, pageable)
    Repo-->>Domain: List<discussionIds>
    Domain->>Repo: findDiscussionsByIds(discussionIds, userId)
    Repo-->>Domain: List<DiscussionQueryResult>
    Domain->>Repo: countByProblemId(problemId)
    Repo-->>Domain: totalCount
    Domain-->>Service: Page<DiscussionQueryResult>
    Service-->>Controller: Page<DiscussionResponse>
Loading
sequenceDiagram
    participant Controller as ReplyController
    participant Service as ReplyService
    participant Domain as ReplyDomainService
    participant Repo as ReplyQueryRepository

    Controller->>Service: getReplies(problemId, discussionId, userId, pageable)
    Service->>Domain: getRepliesByDiscussionId(discussion, userId, pageable)
    Domain->>Repo: findRepliesByDiscussionId(discussionId, userId, pageable)
    Repo-->>Domain: Page<ReplyQueryResult>
    Domain-->>Service: Page<ReplyQueryResult>
    Service-->>Controller: Page<ReplyResponse>
Loading

Possibly related PRs

  • ezcode-my/backend#14: 초기 CRUD 및 간단한 DiscussionResponse 도입. 본 PR의 DTO 확장 및 쿼리 결과 DTO 도입과 직접적으로 연관됨.
  • ezcode-my/backend#69: 투표 처리 로직 및 VoteResult, VoteType 도입. 본 PR의 투표 관련 DTO 확장 및 서비스 변경과 밀접하게 연결됨.
  • ezcode-my/backend#23: 초기 ReplyResponse 및 답글 CRUD 도입. 본 PR의 답글 응답 및 서비스 확장과 직접적으로 연관됨.

Suggested reviewers

  • minjee2758
  • pokerbearkr
  • thezz9
  • chat26666

Poem

🐇
새로운 필드, 쿼리, 응답 DTO
투표와 답글도 한눈에 쏙!
테스트 데이터도 풍성하게
토론의 숲에 바람이 분다
코드의 토끼 춤추네,
"이제 더 빠르고 똑똑하게!"
🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • 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: 5

🔭 Outside diff range comments (1)
src/main/java/org/ezcode/codetest/application/community/service/BaseVoteService.java (1)

20-33: 쓰기 메서드에 트랜잭션 누락
manageVote는 DB 변경을 수반하지만 클래스·메서드에 @Transactional이 없습니다. 호출 스택에 트랜잭션이 없으면 update·delete 후 즉시 count를 호출할 때 Dirty Read / Non-repeatable Read가 발생할 수 있습니다.

-	protected VoteResponse manageVote(Long voterId, Long targetId, VoteType voteType) {
+	@Transactional
+	protected VoteResponse manageVote(Long voterId, Long targetId, VoteType voteType) {
🧹 Nitpick comments (13)
src/main/java/org/ezcode/codetest/domain/community/service/BaseVoteDomainService.java (2)

29-34: 동일한 voteType일 때 불필요한 DB update 발생
existing.get().getVoteType()와 인자로 전달된 voteType이 동일한 경우에도 voteRepository.update가 호출되어 트래픽과 flush 비용이 낭비됩니다.

-				prevVoteType = vote.getVoteType();
-				voteRepository.update(vote, voteType);
+				prevVoteType = vote.getVoteType();
+				if (prevVoteType != voteType) {
+					voteRepository.update(vote, voteType);
+				}

49-52: 집계 쿼리 2회 호출 → 하나의 GROUP BY 쿼리로 통합 가능
countUpvotesByTargetId / countDownvotesByTargetId 두 번 호출하면서 같은 테이블을 2회 스캔합니다. CASE WHEN을 사용해 한 번에 조회하면 트래픽과 레이턴시를 줄일 수 있습니다.

src/main/java/org/ezcode/codetest/domain/community/dto/ReplyQueryResult.java (1)

59-66: 투표 상태 정규화 로직을 단순화할 수 있습니다.

현재 구현은 올바르게 작동하지만, 더 간결하게 작성할 수 있습니다.

-		if (voteType == null) {
-			this.voteStatus = VoteType.NONE;
-		} else if (voteType == VoteType.UP) {
-			this.voteStatus = VoteType.UP;
-		} else {
-			this.voteStatus = VoteType.DOWN;
-		}
+		this.voteStatus = voteType != null ? voteType : VoteType.NONE;
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyQueryRepositoryImpl.java (3)

102-103: 대용량 데이터 페이지네이션 성능 고려

현재 offset 기반 페이지네이션을 사용하고 있습니다. 대용량 데이터셋에서 높은 페이지 번호로 이동할 때 성능이 저하될 수 있습니다.

대용량 데이터 처리가 예상된다면 커서 기반 페이지네이션을 고려해보세요:

  • 마지막 조회된 reply.id를 기준으로 다음 페이지 조회
  • 일정한 쿼리 성능 보장

35-105: 쿼리 성능 모니터링 권장

PR 설명에 따르면 이미 상당한 성능 개선(67%)을 달성했습니다. 프로덕션 환경에서 다음 사항을 모니터링하는 것을 권장합니다:

  1. 실행 계획 분석을 통한 인덱스 활용도 확인
  2. 슬로우 쿼리 로그 모니터링
  3. 대량 데이터 환경에서의 응답 시간 추적

필요시 다음과 같은 추가 최적화를 고려할 수 있습니다:

  • 읽기 전용 슬레이브 DB 활용
  • 자주 변경되지 않는 카운트 값에 대한 캐싱 (Redis 등)
  • 배치 처리를 통한 집계 테이블 구성

60-105: 성능 최적화 검토 필요

이 쿼리는 여러 테이블을 조인하고 GROUP BY를 사용하는 복잡한 구조입니다. 몇 가지 검토사항이 있습니다:

  1. 자식 댓글 카운트: childReply 셀프 조인은 대량의 중첩 댓글이 있을 때 성능 문제를 일으킬 수 있습니다.
  2. 사용자 투표 타입: MAX() 집계 함수 사용은 한 사용자가 여러 투표를 할 수 있음을 시사합니다. 중복 투표를 방지하는 유니크 제약조건이 있는지 확인이 필요합니다.
  3. 정렬 기준: reply.id.asc()는 최신 댓글이 아래에 표시됩니다. 사용자 경험을 위해 reply.createdAt.desc() 정렬을 고려해보세요.
#!/bin/bash
# Description: ReplyVote 엔티티에 중복 투표 방지를 위한 유니크 제약조건이 있는지 확인

# ReplyVote 엔티티 파일 찾기
fd -e java "ReplyVote.java" | head -5

# ReplyVote 엔티티에서 유니크 제약조건 확인
ast-grep --pattern 'class ReplyVote {
  $$$
}'

# @Table 어노테이션에서 uniqueConstraints 확인
rg -A 5 "@Table.*ReplyVote" --type java
rg -A 3 "@UniqueConstraint" --type java
src/test/resources/discussion-query-explain.sql (1)

1-162: 쿼리 최적화 문서화가 잘 되어 있습니다

서브쿼리 → JOIN → 쿼리 분리의 최적화 과정이 단계별로 잘 정리되어 있습니다. 성능 테스트 참고 자료로 유용합니다.

성능 개선 수치나 실행 시간 비교 결과를 주석으로 추가하면 더욱 유용한 문서가 될 것 같습니다.

# 기존 코드 (실행시간: ~200ms)
# 1차 개선 (실행시간: ~150ms, 25% 개선)  
# 2차 개선 (실행시간: ~120ms, 40% 개선)
src/main/java/org/ezcode/codetest/domain/community/repository/DiscussionRepository.java (1)

25-25: 반환 타입 일관성 확인

countByProblemIdLong을 반환하는데, Spring Data의 일반적인 패턴과 일치하는지 확인이 필요합니다.

Spring Data JPA의 일반적인 컨벤션에 따라 long 프리미티브 타입 사용을 고려해보세요:

-	Long countByProblemId(Long problemId);
+	long countByProblemId(Long problemId);
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepository.java (1)

8-16: QueryDSL 전용 인터페이스 분리가 적절합니다

도메인 계층과 인프라 계층의 관심사 분리가 잘 되어 있습니다.

메서드 시그니처가 DiscussionRepository와 동일한데, 제네릭 인터페이스나 공통 인터페이스 추출을 통해 중복을 줄일 수 있을지 검토해보세요:

public interface DiscussionQueryRepository extends DiscussionQueryOperations {
    // QueryDSL specific implementation details only
}
src/main/java/org/ezcode/codetest/domain/community/dto/DiscussionQueryResult.java (1)

55-61: voteStatus 초기화 로직 개선 제안

현재 로직도 정확하지만, 더 간결하게 표현할 수 있습니다.

다음과 같이 개선할 수 있습니다:

-		if (voteType == null) {
-			this.voteStatus = VoteType.NONE;
-		} else if (voteType == VoteType.UP) {
-			this.voteStatus = VoteType.UP;
-		} else {
-			this.voteStatus = VoteType.DOWN;
-		}
+		this.voteStatus = voteType == null ? VoteType.NONE : voteType;

또는 Optional을 사용한 방식:

this.voteStatus = Optional.ofNullable(voteType).orElse(VoteType.NONE);
src/main/java/org/ezcode/codetest/domain/community/service/DiscussionDomainService.java (1)

48-48: 주석 일관성

코드베이스의 일관성을 위해 주석을 영어로 작성하는 것을 권장합니다.

-		// 성능 향상 위해 쿼리 분리
+		// Split queries for performance improvement
src/main/java/org/ezcode/codetest/presentation/community/DiscussionController.java (1)

70-70: sortBy 파라미터 검증 고려

현재 sortBy에 대한 입력 검증이 없습니다. 잘못된 값이 들어올 경우를 대비한 검증 로직을 추가하는 것을 고려해보세요.

컨트롤러 또는 서비스 레이어에서 허용되는 값(best, upvote, latest)에 대한 검증을 추가할 수 있습니다:

private static final Set<String> ALLOWED_SORT_OPTIONS = Set.of("best", "upvote", "latest");

// 메서드 내에서
if (!ALLOWED_SORT_OPTIONS.contains(sortBy)) {
    throw new IllegalArgumentException("Invalid sort option: " + sortBy);
}
src/test/java/org/ezcode/codetest/application/community/QueryPerformanceTest.java (1)

29-37: 주석 처리된 코드를 정리하거나 사용 목적을 명확히 문서화하세요.

현재 테이블 truncate와 관련된 코드가 주석 처리되어 있어, 언제 사용해야 하는지 불분명합니다. 다음 중 하나를 선택하세요:

  • 필요한 경우 별도의 메서드로 분리
  • 불필요하다면 삭제
  • 필요시 사용 방법을 문서화
+	/**
+	 * 기존 데이터를 모두 삭제하고 새로운 테스트 데이터를 생성합니다.
+	 * 주의: 모든 데이터가 삭제되므로 개발 환경에서만 사용하세요.
+	 */
+	@Test
+	@Disabled("전체 데이터 재생성이 필요한 경우에만 활성화")
+	void regenerateAllData() {
+		jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0");
+		jdbcTemplate.execute("TRUNCATE TABLE discussion_vote");
+		jdbcTemplate.execute("TRUNCATE TABLE reply");
+		jdbcTemplate.execute("TRUNCATE TABLE discussion");
+		jdbcTemplate.execute("TRUNCATE TABLE users");
+		jdbcTemplate.execute("TRUNCATE TABLE problem");
+		jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1");
+		
+		testDataGenerator.generate();
+		testDataGeneratorForDiscussion.generate();
+	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7ecc3a and ccc703e.

📒 Files selected for processing (31)
  • src/main/java/org/ezcode/codetest/application/community/dto/response/DiscussionResponse.java (2 hunks)
  • src/main/java/org/ezcode/codetest/application/community/dto/response/ReplyResponse.java (2 hunks)
  • src/main/java/org/ezcode/codetest/application/community/dto/response/VoteResponse.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/community/service/BaseVoteService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/community/service/DiscussionService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/application/community/service/ReplyService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/dto/DiscussionQueryResult.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/dto/ReplyQueryResult.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/dto/VoteResult.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/repository/DiscussionRepository.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/repository/ReplyRepository.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/service/BaseVoteDomainService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/service/DiscussionDomainService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/community/service/ReplyDomainService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionJpaRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepositoryImpl.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionRepositoryImpl.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyJpaRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyQueryRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyQueryRepositoryImpl.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyRepositoryImpl.java (2 hunks)
  • src/main/java/org/ezcode/codetest/presentation/community/DiscussionController.java (3 hunks)
  • src/main/java/org/ezcode/codetest/presentation/community/ReplyController.java (2 hunks)
  • src/test/java/org/ezcode/codetest/application/community/QueryPerformanceTest.java (1 hunks)
  • src/test/java/org/ezcode/codetest/domain/community/service/DiscussionVoteDomainServiceTest.java (1 hunks)
  • src/test/java/org/ezcode/codetest/domain/community/service/ReplyVoteDomainServiceTest.java (1 hunks)
  • src/test/java/org/ezcode/codetest/util/TestDataGenerator.java (1 hunks)
  • src/test/java/org/ezcode/codetest/util/TestDataGeneratorForDiscussion.java (1 hunks)
  • src/test/resources/application-test.properties (1 hunks)
  • src/test/resources/discussion-query-explain.sql (1 hunks)
🔇 Additional comments (34)
src/main/java/org/ezcode/codetest/domain/community/dto/VoteResult.java (1)

1-16: 패키지 이동 및 DTO 레코드 정의, 문제 없습니다
VoteResultdto 패키지로 이동하여 표현 계층과 명확히 분리한 점이 좋습니다. record 사용으로 불변성을 보장하는 것도 적절합니다.

src/main/java/org/ezcode/codetest/domain/community/service/BaseVoteDomainService.java (1)

5-5: import 변경 OK
dto.VoteResult로의 의존성 수정이 일관성을 유지합니다.

src/main/java/org/ezcode/codetest/application/community/dto/response/VoteResponse.java (1)

5-5: import 변경 확인
DTO 레이어로 이동한 VoteResult를 참조하도록 수정된 부분 이상 없습니다.

src/main/java/org/ezcode/codetest/application/community/service/BaseVoteService.java (1)

4-4: import 경로 업데이트 OK
서비스 레이어에서도 DTO 패키지를 정상적으로 참조하도록 수정되었습니다.

src/test/java/org/ezcode/codetest/domain/community/service/DiscussionVoteDomainServiceTest.java (1)

11-11: 테스트 import 경로 수정 확인
테스트 코드도 DTO 패키지를 참조하도록 일관성 있게 변경되었습니다.

src/test/java/org/ezcode/codetest/domain/community/service/ReplyVoteDomainServiceTest.java (1)

10-10: 적절한 import 경로 변경입니다.

VoteResult가 DTO 패키지로 이동함에 따라 import 경로가 올바르게 업데이트되었습니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyJpaRepository.java (1)

6-6: 쿼리 로직 분리를 위한 좋은 설계입니다.

ReplyQueryRepository를 확장하여 쿼리 로직을 별도로 관리하는 것은 관심사 분리 원칙에 부합하며, 복잡한 쿼리 로직의 유지보수성을 향상시킵니다.

src/main/java/org/ezcode/codetest/presentation/community/ReplyController.java (2)

73-79: 사용자 맞춤 데이터 조회를 위한 적절한 구현입니다.

인증된 사용자의 ID를 추출하여 서비스 계층으로 전달하는 로직이 잘 구현되었습니다. null 체크를 통해 비인증 사용자도 댓글을 조회할 수 있도록 한 것은 좋은 접근입니다.


98-104: 일관된 사용자 컨텍스트 처리입니다.

대댓글 조회에서도 동일한 패턴으로 사용자 ID를 처리하여 일관성을 유지하고 있습니다.

src/main/java/org/ezcode/codetest/domain/community/repository/ReplyRepository.java (1)

5-5: 쿼리 최적화를 위한 효과적인 인터페이스 개선입니다.

엔티티 대신 ReplyQueryResult DTO를 반환하도록 변경하여 필요한 데이터만 조회할 수 있게 되었습니다. currentUserId 파라미터 추가로 사용자별 투표 상태 등의 맞춤 데이터를 효율적으로 조회할 수 있습니다.

Also applies to: 16-16, 18-18

src/main/java/org/ezcode/codetest/domain/community/service/ReplyDomainService.java (2)

6-6: 리포지토리 변경사항에 맞춘 적절한 서비스 계층 수정입니다.

ReplyQueryResult DTO 반환과 currentUserId 파라미터 추가로 사용자별 맞춤 데이터를 효율적으로 조회할 수 있게 되었습니다.

Also applies to: 58-60


63-68: 일관된 패턴으로 구현된 대댓글 조회 메서드입니다.

부모 댓글 조회 메서드에서도 동일한 패턴으로 사용자 컨텍스트를 처리하여 일관성을 유지하고 있습니다.

src/main/java/org/ezcode/codetest/domain/community/dto/ReplyQueryResult.java (1)

1-67: 전반적으로 잘 구현된 DTO 클래스입니다.

  • 불변성을 위한 final 필드 사용이 적절합니다.
  • QueryDSL의 QueryProjection 어노테이션이 올바르게 적용되었습니다.
  • 필요한 모든 필드가 포함되어 있습니다.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyQueryRepository.java (1)

7-19: 잘 설계된 쿼리 repository 인터페이스입니다.

  • 메서드명이 명확하고 일관성 있습니다.
  • 필요한 파라미터들이 적절히 정의되어 있습니다.
  • 페이징과 사용자 컨텍스트를 고려한 설계가 좋습니다.
src/main/java/org/ezcode/codetest/application/community/dto/response/ReplyResponse.java (1)

69-83: ReplyQueryResult를 위한 팩토리 메서드가 잘 구현되었습니다.

새로운 DTO에서 변환하는 로직이 명확하고 모든 필드를 적절히 매핑하고 있습니다.

src/main/java/org/ezcode/codetest/application/community/service/ReplyService.java (2)

62-69: 사용자 컨텍스트를 포함한 댓글 조회 로직이 잘 구현되었습니다.

  • currentUserId 파라미터 추가로 사용자별 투표 상태를 제공할 수 있게 되었습니다.
  • ReplyQueryResult 사용으로 더 풍부한 정보를 제공합니다.

72-86: 대댓글 조회 로직도 일관성 있게 업데이트되었습니다.

getReplies 메서드와 동일한 패턴으로 구현되어 일관성이 좋습니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyRepositoryImpl.java (2)

34-37: Repository 메서드가 새로운 요구사항에 맞게 잘 업데이트되었습니다.

  • ReplyQueryResult 반환 타입으로 변경되어 더 풍부한 정보를 제공합니다.
  • currentUserId 파라미터 추가로 사용자별 컨텍스트를 지원합니다.

40-43: 대댓글 조회 메서드도 일관성 있게 구현되었습니다.

부모 댓글별 대댓글 조회 로직이 동일한 패턴으로 업데이트되어 좋습니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/reply/ReplyQueryRepositoryImpl.java (1)

125-130: 투표 수 계산 로직이 적절합니다

CASE 표현식과 countDistinct()를 사용한 투표 수 계산이 효율적으로 구현되었습니다.

src/test/resources/application-test.properties (1)

4-4: 애플리케이션 이름 변경 승인

테스트 환경에서 애플리케이션 이름을 "ezcode"로 통일하는 변경사항이 적절합니다.

src/main/java/org/ezcode/codetest/domain/community/repository/DiscussionRepository.java (2)

17-19: 2단계 쿼리 접근법이 적절합니다

ID 조회와 상세 데이터 조회를 분리하여 성능을 최적화하는 접근법이 좋습니다. 특히 대용량 데이터에서 정렬 성능이 개선될 것으로 예상됩니다.


17-17: 메서드 파라미터 검증 필요성 확인

sortBy 파라미터가 문자열로 전달되는데, 유효하지 않은 정렬 기준이 전달되는 경우에 대한 검증이 구현 계층에서 필요합니다.

다음 스크립트로 sortBy 파라미터 검증 로직이 구현되어 있는지 확인해보겠습니다:

#!/bin/bash
# sortBy 파라미터 검증 로직 확인
rg -A 10 -B 5 "sortBy.*validation|validate.*sortBy|IllegalArgumentException.*sortBy"
src/main/java/org/ezcode/codetest/application/community/service/DiscussionService.java (2)

44-49: 서비스 계층 업데이트가 적절합니다

사용자 컨텍스트와 정렬 기능을 지원하는 변경사항이 잘 구현되었습니다.

sortByuserId 파라미터의 null 처리가 적절히 되어 있는지 확인이 필요합니다:

#!/bin/bash
# 파라미터 null 처리 검증
rg -A 5 -B 5 "sortBy.*null|userId.*null" --type java

48-48: 다음 스크립트를 실행해 DiscussionResponse 클래스의 from 메서드 시그니처가 DiscussionQueryResult를 파라미터로 받는지 확인해 주세요.

#!/bin/bash
# DiscussionResponse.java 위치 찾기 및 from 메서드 확인
files=$(fd --ignore-case DiscussionResponse.java 2>/dev/null)
if [ -z "$files" ]; then
  echo "⚠️ DiscussionResponse.java 파일을 찾을 수 없습니다."
  exit 1
fi

for file in $files; do
  echo "---- $file ----"
  rg -n "from\s*\(" "$file"
done
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionRepositoryImpl.java (3)

35-38: 적절한 메서드 분리

ID 목록 조회를 별도 메서드로 분리한 것은 쿼리 최적화에 효과적인 접근입니다.


40-44: 쿼리 최적화 구현 확인

상세 데이터 조회를 별도로 분리하여 GROUP BY와 ORDER BY로 인한 리소스 소비를 줄이는 전략이 잘 구현되었습니다.


46-50: 페이징을 위한 카운트 쿼리 추가

전체 개수 조회를 별도 메서드로 분리한 것이 적절합니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionJpaRepository.java (1)

6-7: 인터페이스 책임 분리 개선

JPA 기본 기능과 커스텀 쿼리를 별도 인터페이스로 분리한 것이 좋은 설계입니다.

src/main/java/org/ezcode/codetest/presentation/community/DiscussionController.java (1)

39-66: OpenAPI 문서화 추가

API 문서화가 명확하고 상세하게 작성되었습니다. 특히 sortBy 파라미터의 옵션들을 명시한 것이 좋습니다.

src/main/java/org/ezcode/codetest/application/community/dto/response/DiscussionResponse.java (1)

14-73: DTO 구조와 팩토리 메서드 구현이 적절합니다.

목록 조회와 단일 조회를 구분하여 필요한 데이터만 포함하도록 설계된 점이 좋습니다. fromEntityfrom 메서드로 용도를 명확히 구분한 것도 적절합니다.

src/test/java/org/ezcode/codetest/util/TestDataGeneratorForDiscussion.java (1)

69-69: 하드코딩된 problem_id의 존재 여부를 확인하세요.

모든 discussion이 problem_id = 500으로 설정되어 있습니다. 이 ID가 실제로 데이터베이스에 존재하는지 확인이 필요합니다.

#!/bin/bash
# Description: problem 테이블에서 ID 500이 존재하는지 확인

# TestDataGenerator.java를 확인하여 생성되는 problem의 범위 확인
rg -A 5 "PROBLEM_COUNT = " src/test/java/org/ezcode/codetest/util/
src/test/java/org/ezcode/codetest/util/TestDataGenerator.java (1)

39-57: 포괄적인 테스트 데이터 생성 로직이 잘 구현되었습니다.

모든 엔티티에 대한 데이터 생성이 체계적으로 구성되어 있고, 배치 처리와 트랜잭션 관리가 효율적으로 구현되었습니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepositoryImpl.java (1)

29-140: 쿼리 최적화 전략이 효과적으로 구현되었습니다.

ID를 먼저 조회하고 상세 데이터를 별도로 조회하는 방식은 대용량 데이터 처리 시 리소스 사용을 줄이는 좋은 전략입니다. 사용자별 투표 상태 처리도 적절히 구현되었습니다.

NCookies added 2 commits June 26, 2025 00:28
- 토끼 피드백
- resultMap.get()이 null을 반환할 수 있습니다. 동시성 이슈로 인해 ID는 조회되었지만 상세 데이터 조회 시점에 삭제된 경우가 있을 수 있으므로 null 필터링하는 로직 추가
- 현재 discussion.id만으로 그룹화하고 있지만, SELECT 절에서 user의 여러 필드를 사용하고 있습니다. 일부 데이터베이스에서는 이로 인해 오류가 발생할 수 있음
  - 때문에 GROUP BY 절에 SELECT된 모든 non-aggregate 컬럼을 포함시킴
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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccc703e and af0bd16.

📒 Files selected for processing (4)
  • src/main/java/org/ezcode/codetest/domain/community/service/DiscussionDomainService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepositoryImpl.java (1 hunks)
  • src/test/java/org/ezcode/codetest/concurrency/DiscussionVoteServiceConcurrencyTest.java (2 hunks)
  • src/test/java/org/ezcode/codetest/util/DummyDataInit.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/test/java/org/ezcode/codetest/concurrency/DiscussionVoteServiceConcurrencyTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/ezcode/codetest/domain/community/service/DiscussionDomainService.java
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/community/discussion/DiscussionQueryRepositoryImpl.java
🔇 Additional comments (4)
src/test/java/org/ezcode/codetest/util/DummyDataInit.java (4)

10-13: 테스트 클래스 구조가 적절합니다.

대량 데이터 생성을 위한 유틸리티 클래스의 구조가 잘 설계되었습니다:

  • @Disabled로 실수로 실행되는 것을 방지
  • @ActiveProfiles("test")로 테스트 환경 보장
  • 클래스명이 목적을 명확히 표현

15-22: 의존성 주입이 적절하게 구성되었습니다.

성능 테스트를 위한 데이터 생성에 필요한 컴포넌트들이 적절히 주입되었습니다:

  • 범용 데이터 생성기와 토론 전용 생성기로 유연성 확보
  • JdbcTemplate을 통한 직접적인 DB 조작 가능

24-27: 실행 시간 경고가 적절합니다.

대량 데이터 생성의 특성상 장시간 실행에 대한 명확한 경고가 포함되어 있어 좋습니다.


36-37: 데이터 생성기 선택이 PR 목적에 부합합니다.

토론 최적화에 집중한 현재 PR의 목적에 맞게 토론 전용 데이터 생성기만 활성화한 것이 적절합니다.

명확성을 위해 주석을 추가하는 것을 고려해보세요:

-		// testDataGenerator.generate();
+		// testDataGenerator.generate(); // 현재 PR에서는 토론 데이터만 필요하므로 비활성화
 		testDataGeneratorForDiscussion.generate();

@pokerbearkr pokerbearkr merged commit bcf2f24 into dev Jun 26, 2025
1 check passed
@pokerbearkr pokerbearkr deleted the feature/community-optimize branch June 26, 2025 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants