[FEAT] 프로젝트 목록 actionCount/progressPct 실집계 + N+1 제거 - #253
Merged
Conversation
ProjectSummaryResponse.actionCount/completedActionCount/progressPct가 2026-08-05부터 0 고정 스텁이었는데, action BC가 다 갖춰진 지금 실집계로 채운다. ActionQueryPort에 countActionsByProjectIds 배치 조회 신설(project→action 크로스 BC 조회, 기존 findTeamActionsByProjectId와 같은 포트 패턴) — 목록에 프로젝트가 여러 개여도 쿼리 1회로 끝낸다. ProjectPersistenceAdapter.findAllByCompanyId의 프로젝트별 개별 조회(N+1)도 같이 제거 — project_team 배정을 배치 조회 후 프로젝트 id로 메모리에서 묶는다. GetProjectListUseCase.list()는 List<Project> 대신 List<ProjectListItem> (project+실카운트)을 반환하도록 계약 변경 — GetProjectDetailUseCase의 ProjectDetailResult와 같은 패턴. meetingCount는 여전히 0(meeting Port 미비, 모성진에게 요청함). ProjectAttachmentController의 낡은 TBD 주석도 정리 — 첨부파일 목록은 이미 ProjectDetailResponse에 인라인으로 포함돼 있었다.
📝 WalkthroughWalkthrough프로젝트 목록 조회가 프로젝트별 전체 액션 수와 완료 액션 수를 포함하도록 변경되었습니다. 액션과 팀 데이터를 프로젝트 ID 목록으로 일괄 조회합니다. 프로젝트 요약 응답은 집계값으로 진행률을 계산합니다. Changes프로젝트 목록 액션 집계
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant ProjectService
participant ProjectPersistenceAdapter
participant ActionPersistenceAdapter
participant ProjectSummaryResponse
Client->>ProjectService: 회사별 프로젝트 목록 요청
ProjectService->>ProjectPersistenceAdapter: 프로젝트와 팀 데이터 조회
ProjectPersistenceAdapter-->>ProjectService: 프로젝트 목록 반환
ProjectService->>ActionPersistenceAdapter: 프로젝트 ID 목록으로 액션 집계 요청
ActionPersistenceAdapter-->>ProjectService: ProjectActionCount 목록 반환
ProjectService-->>ProjectSummaryResponse: ProjectListItem 목록 전달
ProjectSummaryResponse-->>Client: 액션 수와 진행률을 포함한 목록 응답
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
4 tasks
…-list-action-counts # Conflicts: # src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java (1)
132-140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win배치 조회 인수와 호출 횟수를 검증하세요.
현재
any()스텁은 프로젝트별 조회로 회귀해도 테스트를 통과시킬 수 있습니다.countActionsByProjectIds(List.of(1L, 2L))를 한 번 호출했는지 검증하세요. 이 검증은 N+1 제거 계약을 보호합니다.수정 예시
- when(actionQueryPort.countActionsByProjectIds(any())).thenReturn(List.of( + when(actionQueryPort.countActionsByProjectIds(List.of(1L, 2L))).thenReturn(List.of( new ProjectActionCount(1L, 5, 2))); List<GetProjectListUseCase.ProjectListItem> result = projectService.list(COMPANY); + verify(actionQueryPort).countActionsByProjectIds(List.of(1L, 2L)); assertThat(result).containsExactly(🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java` around lines 132 - 140, Update the test for ProjectService.list to verify actionQueryPort.countActionsByProjectIds is called exactly once with List.of(1L, 2L), replacing the insufficient any()-only stubbing or supplementing it with explicit argument and invocation-count verification to protect the batch-query contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java`:
- Around line 132-140: Update the test for ProjectService.list to verify
actionQueryPort.countActionsByProjectIds is called exactly once with List.of(1L,
2L), replacing the insufficient any()-only stubbing or supplementing it with
explicit argument and invocation-count verification to protect the batch-query
contract.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 28c40bf7-cdc1-4c3a-85c5-bcd759d49457
📒 Files selected for processing (3)
src/main/java/com/module06/backend/project/infrastructure/persistence/ProjectPersistenceAdapter.javasrc/test/java/com/module06/backend/project/application/service/ProjectServiceTest.javasrc/test/java/com/module06/backend/project/presentation/api/ProjectControllerTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
- src/test/java/com/module06/backend/project/presentation/api/ProjectControllerTest.java
- src/main/java/com/module06/backend/project/infrastructure/persistence/ProjectPersistenceAdapter.java
This was referenced Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📌 연관 이슈
(2026-08-05부터 남아있던 스텁 정리 — 별도 이슈 없음)
📝 작업 내용
ProjectSummaryResponse.actionCount/completedActionCount/progressPct— 그동안 0/0.0 고정이었던 걸 action BC가 갖춰진 지금 실집계로 채움ActionQueryPort.countActionsByProjectIds신규 — project→action 크로스 BC 배치 조회(기존findTeamActionsByProjectId와 같은 포트 패턴), 목록 페이지 하나에 프로젝트가 여러 개여도 쿼리 1회ProjectPersistenceAdapter.findAllByCompanyId의 N+1(프로젝트마다project_team개별 조회)도 같이 제거 — 배치 조회 후 메모리에서 프로젝트 id로 묶음GetProjectListUseCase.list()반환 타입을List<Project>→List<ProjectListItem>(project+실카운트)로 변경 —GetProjectDetailUseCase.ProjectDetailResult와 같은 패턴ProjectAttachmentController의 낡은 TBD 주석 정리 — 첨부파일 목록은 이미ProjectDetailResponse에 인라인 포함돼 있었음(별도 결정 불필요, 코드 확인으로 해소)🖥️ 프론트엔드 연동 가이드 (API 명세)
1. 주요 엔드포인트
GET/api/projects: 응답의actionCount/completedActionCount/progressPct가 이제 실제 값입니다(기존엔 항상 0/0.0).2. 요청 파라미터 (Request)
3. 정상 응답 예시 (200 OK)
응답 JSON 보기 (클릭)
{ "httpStatus": 200, "message": "프로젝트 목록을 조회했습니다.", "data": [ { "id": 1, "tag": "PJ-1", "color": "#16A34A", "name": "프로젝트명", "status": "IN_PROGRESS", "dueDate": "2026-12-31", "teamCount": 2, "actionCount": 5, "completedActionCount": 2, "meetingCount": 0, "progressPct": 40.0 } ] }4.⚠️ 프론트엔드 참고 및 주의사항
progressPct는 0100 스케일입니다(퍼센트 값 그대로, 01 소수 아님).actionCount가 0이면progressPct도 항상 0.0입니다(0으로 나누기 방지).meetingCount는 이번 PR에도 여전히 0 고정입니다 — meeting(D) 도메인 Port가 아직 없어서 별도로 요청해둔 상태입니다.🚨 주요 에러 코드 및 예외 (Exceptions)
💡 백엔드 리뷰 포인트 (Backend Review)
ActionQueryPort(action이 선언, project가 호출하는 크로스 BC 조회 포트) 패턴을 그대로 재사용했습니다.findTeamActionsByProjectId바로 옆에countActionsByProjectIds를 추가한 형태입니다.SpringDataActionRepository.findAllByProjectIdIn이 프로젝션(ProjectActionProjection)만 읽습니다 — CI Semgrep(QUERY_002)이 신규@Query(COUNT GROUP BY)를 막아서, 집계는 자바 레벨에서 합니다. 프로젝트당 액션 수가 아주 많아지면(수천 건 단위) 이 방식이 부담될 수 있는데, 지금 스케일에선 문제없다고 판단했습니다.✅ 체크리스트
Summary by CodeRabbit
새 기능
문서
버그 수정