-
Notifications
You must be signed in to change notification settings - Fork 3
feat : 커뮤니티 쿼리 최적화 및 성능 테스트 #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- 추가된 데이터로는 추천수, 댓글수, 추천 여부 등이 있음 - 정렬 기준으로는 베스트(추천수-비추천수), 추천순, 최신순이 있음
- discussion 데이터를 백만 건 삽입하는 코드를 작성함 - 댓글과 추천 데이터는 각각 3배, 8배임
- 기존의 서브쿼리들을 JOIN + GROUP BY 방식으로 변경함 - 그러나 오히려 성능이 떨어짐
- 조회 로직을 ID 조회 + 실제 데이터 조회로 분리함 - ORDER BY와 GROUP BY 과정에서 DB 서버에 훨씬 더 큰 연산 부하를 떠넘겼고, 이 부하가 서브쿼리의 비효율성을 넘어설 정도로 컸다고 추측 - 하나의 쿼리만으로 처리하기 보다는 여러 개로 분리해서 호출하는 방식을 선택함
- 맨 처음 방식에서 쿼리만 분리한 방식 - 성능은 두 번째로 좋음
- 2차 개선 버전으로 쿼리 적용함
- UI에는 적용되지 않더라도 디버깅 시 사용하기 위함
- 댓글은 정렬을 항상 최신순(id 기준 오름차순)으로 사용하므로, 쿼리를 분리하지 않고 JOIN + GROUP BY 만으로 구현함
Walkthrough이 변경 사항은 토론(Discussion) 및 답글(Reply) 응답 DTO에 투표 및 답글 수, 사용자 투표 상태 등 추가 필드를 도입하고, 서비스 및 저장소 레이어를 쿼리 결과 DTO를 활용하도록 대대적으로 리팩토링합니다. 또한 대용량 테스트 데이터 생성 유틸리티와 SQL 쿼리 최적화 리소스가 추가되었습니다. Changes
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>
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>
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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%)을 달성했습니다. 프로덕션 환경에서 다음 사항을 모니터링하는 것을 권장합니다:
- 실행 계획 분석을 통한 인덱스 활용도 확인
- 슬로우 쿼리 로그 모니터링
- 대량 데이터 환경에서의 응답 시간 추적
필요시 다음과 같은 추가 최적화를 고려할 수 있습니다:
- 읽기 전용 슬레이브 DB 활용
- 자주 변경되지 않는 카운트 값에 대한 캐싱 (Redis 등)
- 배치 처리를 통한 집계 테이블 구성
60-105: 성능 최적화 검토 필요이 쿼리는 여러 테이블을 조인하고 GROUP BY를 사용하는 복잡한 구조입니다. 몇 가지 검토사항이 있습니다:
- 자식 댓글 카운트: childReply 셀프 조인은 대량의 중첩 댓글이 있을 때 성능 문제를 일으킬 수 있습니다.
- 사용자 투표 타입: MAX() 집계 함수 사용은 한 사용자가 여러 투표를 할 수 있음을 시사합니다. 중복 투표를 방지하는 유니크 제약조건이 있는지 확인이 필요합니다.
- 정렬 기준:
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 javasrc/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: 반환 타입 일관성 확인
countByProblemId가Long을 반환하는데, 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 improvementsrc/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
📒 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 레코드 정의, 문제 없습니다
VoteResult를dto패키지로 이동하여 표현 계층과 명확히 분리한 점이 좋습니다.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: 쿼리 최적화를 위한 효과적인 인터페이스 개선입니다.엔티티 대신
ReplyQueryResultDTO를 반환하도록 변경하여 필요한 데이터만 조회할 수 있게 되었습니다.currentUserId파라미터 추가로 사용자별 투표 상태 등의 맞춤 데이터를 효율적으로 조회할 수 있습니다.Also applies to: 16-16, 18-18
src/main/java/org/ezcode/codetest/domain/community/service/ReplyDomainService.java (2)
6-6: 리포지토리 변경사항에 맞춘 적절한 서비스 계층 수정입니다.
ReplyQueryResultDTO 반환과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: 서비스 계층 업데이트가 적절합니다사용자 컨텍스트와 정렬 기능을 지원하는 변경사항이 잘 구현되었습니다.
sortBy와userId파라미터의 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" donesrc/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 구조와 팩토리 메서드 구현이 적절합니다.목록 조회와 단일 조회를 구분하여 필요한 데이터만 포함하도록 설계된 점이 좋습니다.
fromEntity와from메서드로 용도를 명확히 구분한 것도 적절합니다.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를 먼저 조회하고 상세 데이터를 별도로 조회하는 방식은 대용량 데이터 처리 시 리소스 사용을 줄이는 좋은 전략입니다. 사용자별 투표 상태 처리도 적절히 구현되었습니다.
src/main/java/org/ezcode/codetest/application/community/dto/response/ReplyResponse.java
Show resolved
Hide resolved
src/main/java/org/ezcode/codetest/domain/community/service/DiscussionDomainService.java
Show resolved
Hide resolved
src/test/java/org/ezcode/codetest/application/community/QueryPerformanceTest.java
Show resolved
Hide resolved
src/test/java/org/ezcode/codetest/util/TestDataGeneratorForDiscussion.java
Show resolved
Hide resolved
...nfrastructure/persistence/repository/community/discussion/DiscussionQueryRepositoryImpl.java
Show resolved
Hide resolved
- 토끼 피드백 - resultMap.get()이 null을 반환할 수 있습니다. 동시성 이슈로 인해 ID는 조회되었지만 상세 데이터 조회 시점에 삭제된 경우가 있을 수 있으므로 null 필터링하는 로직 추가 - 현재 discussion.id만으로 그룹화하고 있지만, SELECT 절에서 user의 여러 필드를 사용하고 있습니다. 일부 데이터베이스에서는 이로 인해 오류가 발생할 수 있음 - 때문에 GROUP BY 절에 SELECT된 모든 non-aggregate 컬럼을 포함시킴
There was a problem hiding this 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
📒 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();
작업 내용
토론글 목록 조회 쿼리 최적화
성능 테스트 위해 100만건 데이터 삽입
구현 시 목표
성능 테스트 방식
성능 테스트 결과 및 결론
변경 사항
트러블 슈팅
참고 사항
코드 리뷰 전 확인 체크리스트
type :)Summary by CodeRabbit
신규 기능
버그 수정
문서화
테스트
기타