Skip to content

[FEAT] 프로젝트 목록 actionCount/progressPct 실집계 + N+1 제거 - #253

Merged
mosungjin merged 2 commits into
developfrom
feat/mnppi-project-list-action-counts
Aug 9, 2026
Merged

[FEAT] 프로젝트 목록 actionCount/progressPct 실집계 + N+1 제거#253
mosungjin merged 2 commits into
developfrom
feat/mnppi-project-list-action-counts

Conversation

@MNPPI223

@MNPPI223 MNPPI223 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

📌 연관 이슈

  • Closes #

(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)

  • 아키텍처 및 도메인: project가 action 데이터를 읽을 때 기존 ActionQueryPort(action이 선언, project가 호출하는 크로스 BC 조회 포트) 패턴을 그대로 재사용했습니다. findTeamActionsByProjectId 바로 옆에 countActionsByProjectIds를 추가한 형태입니다.
  • 우려되는 부분이나 고민: SpringDataActionRepository.findAllByProjectIdIn이 프로젝션(ProjectActionProjection)만 읽습니다 — CI Semgrep(QUERY_002)이 신규 @Query(COUNT GROUP BY)를 막아서, 집계는 자바 레벨에서 합니다. 프로젝트당 액션 수가 아주 많아지면(수천 건 단위) 이 방식이 부담될 수 있는데, 지금 스케일에선 문제없다고 판단했습니다.

✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 주석 및 콘솔 로그를 제거했습니다.
  • API 기능이 정상 동작하는지 테스트했습니다.
  • 예외(잘못된 값) 상황에 대한 검증 및 테스트를 통과했습니다.

Summary by CodeRabbit

  • 새 기능

    • 프로젝트 목록에서 프로젝트별 전체 액션 수, 완료 액션 수, 진행률을 확인할 수 있습니다.
    • 액션이 없는 프로젝트도 집계값이 0으로 표시됩니다.
    • 여러 프로젝트의 액션 현황이 일괄 집계되어 목록에 반영됩니다.
  • 문서

    • 프로젝트 상세 응답에 첨부파일 목록이 포함되는 방식으로 안내가 업데이트되었습니다.
  • 버그 수정

    • 프로젝트별 팀 정보와 액션 집계가 올바르게 연결되도록 개선했습니다.

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에 인라인으로
포함돼 있었다.
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

프로젝트 목록 조회가 프로젝트별 전체 액션 수와 완료 액션 수를 포함하도록 변경되었습니다. 액션과 팀 데이터를 프로젝트 ID 목록으로 일괄 조회합니다. 프로젝트 요약 응답은 집계값으로 진행률을 계산합니다.

Changes

프로젝트 목록 액션 집계

Layer / File(s) Summary
액션 집계 조회 계약과 구현
src/main/java/com/module06/backend/action/application/port/ActionQueryPort.java, src/main/java/com/module06/backend/action/infrastructure/persistence/*, src/test/java/com/module06/backend/action/infrastructure/persistence/ActionPersistenceAdapterTest.java
countActionsByProjectIdsProjectActionCount를 추가했습니다. 프로젝트 ID 목록으로 액션을 조회하고 DONE 상태 수를 집계합니다. 빈 입력은 빈 결과로 처리합니다.
프로젝트 목록 집계 연결
src/main/java/com/module06/backend/project/application/usecase/GetProjectListUseCase.java, src/main/java/com/module06/backend/project/application/service/ProjectService.java, src/main/java/com/module06/backend/project/infrastructure/persistence/*, src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java
프로젝트 목록 반환 타입을 ProjectListItem으로 변경했습니다. 액션 집계 결과를 프로젝트에 연결하고, 결과가 없는 프로젝트에는 0을 설정합니다. 팀 데이터도 일괄 조회합니다.
프로젝트 요약 응답 변환
src/main/java/com/module06/backend/project/presentation/api/response/ProjectSummaryResponse.java, src/main/java/com/module06/backend/project/presentation/api/ProjectAttachmentController.java, src/test/java/com/module06/backend/project/presentation/api/ProjectControllerTest.java
ProjectListItem에서 액션 수와 진행률을 계산하는 변환을 추가했습니다. 프로젝트 목록 테스트와 첨부파일 설명을 갱신했습니다.

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: 액션 수와 진행률을 포함한 목록 응답
Loading

Possibly related PRs

Suggested reviewers: mosungjin

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 프로젝트 목록의 actionCount와 progressPct 실집계 및 N+1 제거라는 주요 변경 사항을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mnppi-project-list-action-counts

Comment @coderabbitai help to get the list of available commands.

@MNPPI223 MNPPI223 self-assigned this Aug 9, 2026
@MNPPI223 MNPPI223 added the enhancement New feature or request label Aug 9, 2026
@MNPPI223 MNPPI223 added this to the [M1] BE 기능 개발 milestone Aug 9, 2026
@MNPPI223 MNPPI223 added this to Z Aug 9, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Z Aug 9, 2026

@mosungjin mosungjin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다.

…-list-action-counts

# Conflicts:
#	src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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

📥 Commits

Reviewing files that changed from the base of the PR and between 303497c and aa7fa8c.

📒 Files selected for processing (3)
  • src/main/java/com/module06/backend/project/infrastructure/persistence/ProjectPersistenceAdapter.java
  • src/test/java/com/module06/backend/project/application/service/ProjectServiceTest.java
  • src/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

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

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants