# 관리자 정산 크리에이터 번호 적용 구현 계획 | 문서 항목 | 내용 | |---|---| | 상태 | 구현 완료 | | 작성일 | 2026-08-04 | | 요구사항 기준 | `docs/20260804_관리자정산크리에이터번호적용/prd.md` | | 현재 Phase | Phase 1 완료 | | 현재 활성 Goal | 없음 | ## 목표 이메일이 없는 크리에이터도 관리자 크리에이터별 정산에서 오류 없이 조회되고 `creatorId`로 구분된다. ## 현재 상태 | Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 | |---:|---|---:|---|---| | 1 | 완료 | `2/2` | 없음 | 없음 | ## 범위 ### 포함 - 관리자 라이브·콘텐츠·커뮤니티 크리에이터별 정산 QueryDSL projection 변경 - 정산 query/response DTO의 `email` 제거 및 `creatorId` 추가 - 크리에이터별 정산 엑셀 이메일 컬럼 제거 및 크리에이터 번호 컬럼 추가 - null 이메일 크리에이터 QueryDSL 회귀 테스트와 엑셀 출력 테스트 ### 제외 - 에이전트·채널후원 정산 변경 - `Member.email` 정책 변경 - 정산 공식·집계·필터·정렬·페이지네이션 변경 - dependency 추가와 관련 없는 리팩터링 ## 기술적 제약 - Kotlin, Spring Boot 2.7.14, QueryDSL 5.0.0, JUnit 5의 기존 패턴을 유지한다. - `@QueryProjection` 생성자 변경은 Gradle KAPT가 생성 코드를 갱신하도록 하고 생성 파일은 직접 수정하지 않는다. - 구현은 RED → GREEN → REFACTOR 순서로 진행한다. - Gradle 명령은 QueryDSL 생성 코드 충돌을 피하기 위해 순차 실행한다. ### Phase 1: 정산 projection 및 출력 변경 **Phase 결과:** null 이메일 크리에이터 정산 조회와 creatorId 기반 API·엑셀 출력이 동작한다. **선행조건:** PRD와 본 계획 문서 작성 완료. #### Task 1.1 QueryDSL 정산 응답에 creatorId 적용 **Goal 실행 `P1-T1`:** null 이메일 크리에이터의 세 정산 조회가 creatorId를 반환하도록 한다. - **시작 조건:** `CALC-001`, `CALC-002`, `CALC-004` 확정. - **완료 증거:** RED/GREEN 결과와 focused test 기록. - **범위 밖:** 엑셀 출력 변경은 `P1-T2`에서 처리한다. **Files:** - Create: `src/test/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepositoryTest.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/GetCalculateByCreatorQueryData.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/GetCalculateByCreatorItem.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt` - [x] **RED:** email이 null인 크리에이터의 라이브·콘텐츠·커뮤니티 정산 조회가 `creatorId`를 반환하는 통합 테스트를 작성한다. - [x] **RED 확인:** `./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateQueryRepositoryTest`를 실행해 기존 projection의 null email 생성 오류 또는 미구현 `creatorId` 계약 실패를 확인한다. - [x] **GREEN:** 두 DTO에서 `email`을 `creatorId: Long`으로 교체하고 세 QueryDSL projection에서 `member.id`를 선택한다. - [x] **GREEN 확인:** 같은 focused test가 성공하는지 확인한다. - [x] **REFACTOR:** 이번 Task가 만든 중복만 정리하고 정산 계산 회귀 테스트를 실행한다. #### Task 1.2 엑셀 크리에이터 번호 적용 **Goal 실행 `P1-T2`:** 크리에이터별 정산 엑셀에서 이메일 대신 크리에이터 번호를 출력한다. - **시작 조건:** `P1-T1` 완료. - **완료 증거:** 엑셀 헤더·값 테스트와 focused test 기록. - **범위 밖:** 다른 정산 엑셀 형식 변경. **Files:** - Modify: `src/test/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateServiceTest.kt` - Modify: `src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateService.kt` - [x] **RED:** 크리에이터별 정산 엑셀의 첫 헤더와 값이 크리에이터 번호인지 검증하는 테스트를 작성한다. - [x] **RED 확인:** `./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateServiceTest`를 실행해 남아 있는 `item.email` 계약 때문에 컴파일이 실패하는지 확인한다. - [x] **GREEN:** 엑셀 첫 헤더를 `크리에이터 번호`로 교체하고 `creatorId`를 기록한다. - [x] **GREEN 확인:** 같은 focused test가 성공하는지 확인한다. - [x] **REFACTOR:** 나머지 엑셀 컬럼과 계산 결과가 유지되는지 회귀 확인한다. ### 완료 조건 - [x] `P1-T1`, `P1-T2`의 체크박스와 완료 증거가 충족됐다. - [x] 문서와 구현의 차이가 없다. ### Phase 1 Gate **Goal 실행 `P1-GATE`:** 변경 범위의 기능·컴파일·포맷을 최종 판정한다. ```bash ./gradlew compileKotlin ./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateQueryRepositoryTest --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateServiceTest --tests kr.co.vividnext.sodalive.admin.calculate.ContentSettlementCalculationTest ./gradlew ktlintCheck ./gradlew tasks --all git diff --check ``` **Expected:** 모든 Gradle 명령이 `BUILD SUCCESSFUL`, 테스트가 모두 통과하고 `git diff --check` 출력이 없다. 전체 테스트는 관리자 크리에이터별 정산의 projection/DTO/엑셀 경계로 변경이 제한되고 focused test와 컴파일로 세 쿼리 호출부를 검증하므로 생략한다. ## 실행 순서와 의존성 | 순서 | Goal | 선행조건 | 병행 가능 | 차단 시 다음 행동 | |---:|---|---|---|---| | 1 | `P1-T1` | 문서 작성 완료 | 아니요 | RED 실패 원인 재확인 | | 2 | `P1-T2` | `P1-T1` 완료 | 아니요 | 엑셀 계약 재확인 | | 3 | `P1-GATE` | Phase 1 Task 전체 | 아니요 | 실패 소유 Task에 회귀 수정 기록 | ## 변경 금지 항목 - QueryDSL 생성 파일을 직접 수정하지 않는다. - 정산 계산식과 쿼리 집계 조건을 변경하지 않는다. - 테스트를 삭제·skip·완화하지 않는다. - 요청 범위 밖의 정산 API를 변경하지 않는다. ## Progress ### `P1-T1` 1차 실행 — 2026-08-04 - 상태: 진행 중 - 무엇을: PRD와 구현 계획을 작성하고 QueryDSL 통합 테스트 seam을 확정했다. - 왜: production 변경 전에 요구사항과 TDD 완료 기준을 고정하기 위해서다. - 어떻게: - `docs/sample/sample-prd.md`, `docs/sample/sample-plan-task.md` 확인 — 완료 - `AdminCalculateQueryRepository`와 관련 DTO 호출 경로 확인 — 완료 - `./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateQueryRepositoryTest` — RED 확인, `creatorId` 미구현으로 `compileTestKotlin` 실패 - `./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateServiceTest` — RED 확인, 남아 있는 `item.email` 참조로 `compileKotlin` 실패 - `./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateQueryRepositoryTest --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateServiceTest` — GREEN, `BUILD SUCCESSFUL` - 남은 항목: Phase Gate 검증. - 다음 행동: 컴파일·focused 회귀·ktlint·문서 검증을 순차 실행한다. ### `P1-GATE` 1차 실행 — 2026-08-04 - 상태: 완료 - 무엇을: creatorId projection·응답·엑셀 변경과 null 이메일 회귀 방지를 검증했다. - 왜: `CALC-001`~`CALC-004`의 수용 기준과 기존 계산 불변성을 최종 판정하기 위해서다. - 어떻게: - `./gradlew compileKotlin` — `BUILD SUCCESSFUL` - `./gradlew test --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateQueryRepositoryTest --tests kr.co.vividnext.sodalive.admin.calculate.AdminCalculateServiceTest --tests kr.co.vividnext.sodalive.admin.calculate.ContentSettlementCalculationTest` — 11 tests, failures 0, errors 0 - `./gradlew ktlintCheck` — `BUILD SUCCESSFUL` - `./gradlew build -x test` — `BUILD SUCCESSFUL` - `git diff --check` — 출력 없음 - 독립 Oracle 리뷰 — `APPROVED`, 차단 이슈 없음 - 수동 검증: QueryDSL H2 통합 테스트로 null 이메일 세 정산 조회를 실행하고, 실제 XLSX workbook의 헤더와 numeric creatorId 셀을 확인했다. - 전체 테스트 생략: 변경이 관리자 크리에이터별 정산의 공용 projection/DTO/엑셀 경계에 한정되어 focused 통합·서비스·계산 테스트와 전체 빌드로 직접 영향 범위를 검증했다. - 남은 항목: 없음. - 다음 행동: 없음. ## Decision Log | 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 | |---|---|---|---|---|---| | 2026-08-04 | `DEC-001` | 확정 | email을 제거하고 creatorId를 projection·응답에 추가한다. | PRD `DEC-001` | `P1-T1` | | 2026-08-04 | `DEC-002` | 확정 | 엑셀 이메일 컬럼을 크리에이터 번호로 교체한다. | PRD `DEC-002` | `P1-T2` | ## 발견된 문제 | ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 | |---|---|---|---|---|---| | `ISSUE-001` | High | 확정 | nullable `Member.email`이 non-null QueryProjection 생성자로 전달되어 정산 조회가 실패한다. | `P1-T1` | creatorId로 projection 계약 교체 |