docs(recommendation): 응원 크리에이터 검토 기록을 갱신한다
This commit is contained in:
@@ -1,11 +1,23 @@
|
||||
# 메인 홈 추천 응원 크리에이터 스냅샷 수정 Plan/Task
|
||||
|
||||
## 후속 변경 상태
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 상태 | 3차 Phase별 리뷰 완료 |
|
||||
| 확정일 | 2026-07-31 |
|
||||
| 요구사항 기준 | `docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/prd.md` 전체 |
|
||||
| 현재 Phase | Phase 1~7 3차 리뷰 완료 |
|
||||
| 현재 활성 Goal | 없음 |
|
||||
| 다음 Goal | 없음 |
|
||||
|
||||
## 시나리오 계약
|
||||
- Happy path: 인기 커뮤니티와 동일한 최근 7일 UTC half-open 범위로 `CHEER_CREATOR` 점수를 계산하고, 점수순 상위 16개 스냅샷을 저장한다. Real surface: `DefaultHomeRecommendationQueryRepositoryTest`, `RecommendationSnapshotRefreshServiceTest`.
|
||||
- Score: 응원 점수는 `((donationAmount * 0.45) + (fanTalkCount * 0.30) + (donationCount * 0.10)) * newBoost`다. 후원 금액은 `CHANNEL_DONATION`과 `DONATION`의 `use_can_calculate.can`을 그대로 사용하고, 후원 수는 `UseCanCalculate.useCan` 기준으로 중복 제거한다. Real surface: `RecommendationScorePolicyTest`, `DefaultHomeRecommendationQueryRepositoryTest`.
|
||||
- Boost: 신규 부스트는 크리에이터 데뷔일 기준 0~10일 `1.15`, 11~20일 `1.10`, 21~30일 `1.05`, 31일 이상 `1.0`이다. Real surface: `RecommendationScorePolicyTest`, `DefaultHomeRecommendationQueryRepositoryTest`.
|
||||
- Fallback: 최신 `CHEER_CREATOR` 스냅샷이 없으면 lock, double-check, 동일 refresh 로직 재사용, refresh 후 재조회 순서로 fallback을 실행한다. lock 대기는 최대 300ms, 홈 API refresh 완료 대기는 최대 1,500ms다. Real surface: fallback service test, `HomeRecommendationQueryServiceTest`.
|
||||
- Empty marker: `CHEER_CREATOR` refresh 결과가 0건이면 `targetId = 0` marker를 저장해 정상 refresh 완료 상태를 남기고, 조회 응답에서는 marker를 제외한다. Real surface: `RecommendationSnapshotPersistenceAdapterTest`, `HomeRecommendationQueryServiceTest`.
|
||||
- Personalized filter: 인증 회원의 `cheerCreators`에서 조회자 본인과 `CreatorFollowing.isActive == true`인 팔로우 크리에이터를 제외한다. 비활성 팔로우 이력과 비회원 조회는 기존 동작을 유지한다. Real surface: `DefaultHomeRecommendationQueryRepositoryTest`.
|
||||
- Adjacent regression: 메인 홈 추천 API URL과 `CHEER_CREATOR` 응답 필드는 변경하지 않는다. AI 캐릭터, 인기 커뮤니티, 최근 데뷔 등 다른 섹션 산식과 공개 스키마는 이번 변경으로 바꾸지 않는다. Real surface: 기존 focused tests, `HomeRecommendationControllerTest`.
|
||||
|
||||
## 범위와 전제
|
||||
@@ -16,12 +28,15 @@
|
||||
- 집계 기간은 인기 커뮤니티와 동일하게 KST 전날을 포함한 최근 7일이며, DB 조회에는 UTC half-open window를 사용한다.
|
||||
- fallback orchestration은 AI 캐릭터 전용 구현을 그대로 복사하지 않고, 섹션별 lock key와 refresh action을 받을 수 있는 최소 공통 runner를 우선 적용한다.
|
||||
- 다른 스냅샷 섹션으로 empty marker를 확장하는 작업은 이번 구현 범위에서 제외한다. 단, `CHEER_CREATOR`에 적용할 때 이후 공통화가 가능하도록 조건문/상수명을 명확히 둔다.
|
||||
- 본인·팔로우 제외는 스냅샷 생성이 아닌 `findCheerCreatorRecommendationDetails(...)` 상세 조회 시점에서 기존 `memberId`로 적용한다.
|
||||
- 기존 16명 스냅샷 후보 안에서만 필터링하며, 필터 결과가 8명 미만이어도 하위 후보 조회나 스냅샷 저장 수 확대를 하지 않는다.
|
||||
|
||||
## 기존 CHEER_CREATOR 로직 유지/변경 경계
|
||||
- 유지: `RecommendedSectionType.CHEER_CREATOR` enum 값과 code는 변경하지 않는다.
|
||||
- 유지: 홈 응원 크리에이터 응답 필드인 `creatorId`, `creatorNickname`, `creatorProfileImage`는 변경하지 않는다.
|
||||
- 유지: 홈 첫 화면 응답은 최대 8명, 스냅샷 후보 조회는 최대 16개를 사용한다.
|
||||
- 유지: 상세 조회 시점의 활성 크리에이터 필터와 차단 필터는 유지한다.
|
||||
- 유지: 비회원 조회와 비활성 팔로우 이력이 있는 크리에이터 조회는 기존 동작을 유지한다.
|
||||
- 유지: 후원 금액은 `use_can_calculate.can` 값을 그대로 합산한다.
|
||||
- 변경: 점수 가중치는 후원 금액 45%, 팬Talk 수 30%, 후원 수 10%로 바꾼다.
|
||||
- 변경: 후원 수는 `UseCanCalculate.useCan` 기준 distinct count로 계산한다.
|
||||
@@ -30,6 +45,7 @@
|
||||
- 변경: 신규 부스트는 기존 크리에이터 공통 부스트 `1.5/1.3/1.2`가 아니라 `CHEER_CREATOR` 전용 `1.15/1.10/1.05`를 사용한다.
|
||||
- 추가: `CHEER_CREATOR` 최신 스냅샷이 없을 때 fallback refresh를 실행한다.
|
||||
- 추가: `CHEER_CREATOR` refresh 결과 0건이면 empty snapshot marker를 저장한다.
|
||||
- 추가: 인증 회원 본인과 활성 팔로우 중인 크리에이터를 `cheerCreators` 상세 조회에서 제외한다.
|
||||
|
||||
## 실행 명령
|
||||
- 문서 명령 확인: `./gradlew tasks --all`
|
||||
@@ -55,6 +71,32 @@
|
||||
- REFACTOR: 기존 홈 추천 구현 파일과 테스트 파일 기준으로 task별 수정/검증 경로를 맞춘다.
|
||||
- 기대 결과: 구현 시작 전에 PRD와 plan-task가 같은 디렉터리에 준비된다.
|
||||
|
||||
#### Task R1.1 PRD 관련 문서 경로 정합성 복구
|
||||
|
||||
**Goal 실행 `P1-R1`:** `REV-P1-001`에서 확인한 존재하지 않는 샘플 PRD 경로를 실제 가이드 경로로 정정한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-1-review.md`의 `REV-P1-001` 확정.
|
||||
- **완료 증거:** 문서 경로 정정, 대상 파일 존재 확인, `git diff --check` 통과, 전체 검증 기록 누적.
|
||||
- **범위 밖:** PRD 요구사항·결정 내용 변경, 코드·테스트 변경, 다른 문서의 링크 일괄 정리.
|
||||
- **TDD 예외 사유:** 문서 링크 정정만 수행하며 런타임 동작을 변경하지 않는다.
|
||||
|
||||
- [x] `prd.md`의 `docs/prd/sample-prd.md`를 실제 파일인 `docs/sample/sample-prd.md`로 정정한다.
|
||||
- [x] `test -f docs/sample/sample-prd.md`와 `git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/prd.md`로 링크 대상과 문서 diff를 확인한다.
|
||||
- [x] 무엇을/왜/어떻게 검증했는지 전체 검증 기록에 누적한다.
|
||||
|
||||
#### Task R1.2 후속 변경 상태 정합성 복구
|
||||
|
||||
**Goal 실행 `P1-R2`:** `REV-P1-002`에서 확인한 상단 상태표의 요구사항 범위·Phase·다음 Goal을 현재 계획과 검증 상태에 맞게 정리한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-1-review.md`의 `REV-P1-002` 확정.
|
||||
- **완료 증거:** 상태표가 PRD 전체, Phase 1~7, 미완료 후속 Goal을 정확히 가리킴, `git diff --check`와 문서 명령 유효성 확인, 전체 검증 기록 누적.
|
||||
- **범위 밖:** PRD 요구사항 변경, 코드·테스트 변경, 이전 검증 기록 삭제.
|
||||
- **TDD 예외 사유:** 현재 작업 상태 문구만 복구하며 런타임 동작을 변경하지 않는다.
|
||||
|
||||
- [x] 상태표의 요구사항 기준을 PRD 전체로, Phase 상태를 Phase 1~7 현재 판정으로, 다음 Goal을 실제 미완료 Goal로 맞춘다.
|
||||
- [x] `git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md`와 `./gradlew tasks --all`로 문서 diff와 명령 유효성을 확인한다.
|
||||
- [x] 완료 후 다음 Goal을 `P3-R2`로 갱신하고 전체 검증 기록에 무엇을/왜/어떻게 검증했는지 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: 산식과 부스트 정책
|
||||
@@ -144,6 +186,32 @@
|
||||
- REFACTOR: AI 캐릭터와 인기 커뮤니티 스냅샷 query가 영향받지 않았는지 focused test로 확인한다.
|
||||
- 기대 결과: `CHEER_CREATOR` 스냅샷 저장 후보만 정확히 변경된다.
|
||||
|
||||
#### Task R3.1 `CHEER_CREATOR` 후보 경계·정렬 완료 근거 보강
|
||||
|
||||
**Goal 실행 `P3-R1`:** `REV-P3-001`에서 누락이 확인된 Task 3.5의 후보 제외·상위 16개·저장 정렬 회귀 증거를 테스트로 고정한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-3-review.md`의 `REV-P3-001` 확정.
|
||||
- **완료 증거:** 실패 재현 테스트 작성·확인, 필요한 경우 최소 구현, repository/persistence focused test 통과, 전체 검증 기록 누적.
|
||||
- **범위 밖:** 점수 가중치·집계 기간·스냅샷 저장 수 변경, 랜덤 정책 변경, 다른 추천 섹션 쿼리 수정.
|
||||
|
||||
- [x] **RED:** 실제 데뷔 이력이 있지만 후원·팬Talk가 모두 0인 후보, 미래 데뷔 이력만 있는 후보, 비활성 크리에이터가 제외되는 테스트를 추가한다.
|
||||
- [x] **RED:** 17개 이상의 점수 후보에서 점수 내림차순 상위 16개만 반환되는 repository 테스트와, 저장된 동점 스냅샷이 `randomTieBreaker` 오름차순으로 조회되는 `CHEER_CREATOR` persistence 테스트를 추가한다.
|
||||
- [x] **GREEN:** 새 테스트가 구현 결함을 드러낼 때만 해당 조건·정렬 경로를 최소 수정하고, 테스트 누락뿐이면 프로덕션 코드는 변경하지 않는다.
|
||||
- [x] **REFACTOR/GATE:** `DefaultHomeRecommendationQueryRepositoryTest`와 `RecommendationSnapshotPersistenceAdapterTest` focused test 및 `git diff --check`를 실행해 결과를 누적한다.
|
||||
|
||||
#### Task R3.2 종료된 라이브 데뷔 이력 복구
|
||||
|
||||
**Goal 실행 `P3-R2`:** `REV-P3-002`에서 확인한 `CHEER_CREATOR` 데뷔일 계산이 채널명이 있는 종료 라이브를 제외하는 문제를 수정하고 회귀를 방지한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-3-review.md`의 `REV-P3-002` 확정과 `P1-R2` 완료.
|
||||
- **완료 증거:** 종료 라이브 재현 테스트의 의도한 실패, `CHEER_CREATOR` 데뷔 CTE 최소 수정, repository focused test·`ktlintCheck`·`git diff --check` 통과, 전체 검증 기록 누적.
|
||||
- **범위 밖:** 점수 가중치·집계 window·저장 수·정렬 변경, 다른 추천 섹션의 데뷔 정책 변경, 공개 API 변경.
|
||||
|
||||
- [x] **RED:** 활성 콘텐츠는 없고 `channel_name`이 있는 종료 라이브와 최근 7일 응원 활동만 있는 활성 크리에이터가 `CHEER_CREATOR` 후보에 포함되는 실패 테스트를 추가한다. 빈 `channel_name`의 종료 라이브는 데뷔 이력으로 인정하지 않는 경계를 함께 유지한다.
|
||||
- [x] **RED 확인:** `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest`를 실행해 현재 `lr.is_active = true` 조건 때문에 종료 라이브 후보가 누락되는 assertion 실패를 확인한다.
|
||||
- [x] **GREEN:** `findCheerCreatorSnapshots(...)`의 `creator_debut` 라이브 branch에서 `lr.is_active = true`만 제거하고, `channel_name is not null`, `channel_name <> ''`, `begin_date_time <= :snapshotAt` 조건은 유지한다.
|
||||
- [x] **REFACTOR/GATE:** repository focused test, `./gradlew ktlintCheck`, `git diff --check`를 실행하고 점수·window·후보 상한·다른 섹션 쿼리가 변경되지 않았음을 기록한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: refresh 저장과 empty marker
|
||||
@@ -178,6 +246,19 @@
|
||||
- REFACTOR: 전체 일괄 refresh 성공 로그는 유지하되, 섹션별 로그와 중복되어도 검색 가능한 event key를 사용한다.
|
||||
- 기대 결과: 운영에서 `CHEER_CREATOR` refresh 결과 0건과 실패를 구분할 수 있다.
|
||||
|
||||
#### Task R4.1 `CHEER_CREATOR` refresh 실패 로그 보강
|
||||
|
||||
**Goal 실행 `P4-R1`:** `REV-P4-001`에서 확인한 섹션별 refresh 실패 로그 누락을 보완해 성공·빈 결과·실패를 운영 로그로 구분한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-4-review.md`의 `REV-P4-001` 확정.
|
||||
- **완료 증거:** 실패 로그 RED 테스트, 최소 로그 구현, refresh focused test 통과, 전체 검증 기록 누적.
|
||||
- **범위 밖:** fallback 실패 로그 형식 변경, 로그 수집 인프라·메트릭 시스템 추가, refresh 트랜잭션 정책 변경.
|
||||
|
||||
- [x] **RED:** `refreshCheerCreatorSnapshots(nowUtc)`의 query 또는 저장 실패 시 `event=cheer_creator_recommendation_snapshot_refresh_failure`, window·`snapshotAt`, 오류 정보가 기록되고 예외는 기존처럼 전파되는 테스트를 추가한다.
|
||||
- [x] **GREEN:** 성공 경로를 변경하지 않는 최소 `runCatching` 또는 `try/catch` 로그를 추가한 뒤 원래 예외를 다시 던진다.
|
||||
- [x] **REFACTOR/GATE:** `RecommendationSnapshotRefreshServiceTest`와 `git diff --check`를 실행하고 중복 로그가 의도된 event key로 구분되는지 확인한다.
|
||||
- [x] 무엇을/왜/어떻게 검증했는지 전체 검증 기록에 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: fallback refresh
|
||||
@@ -224,6 +305,19 @@
|
||||
- REFACTOR: 스냅샷 후보 16개 조회, 상세 조회 후 최대 8개 반환, 차단 필터 전달은 기존 동작을 유지한다.
|
||||
- 기대 결과: 홈 통합 조회의 최근 응원이 많은 크리에이터 섹션이 스냅샷 없음 상황을 자체 복구할 수 있다.
|
||||
|
||||
#### Task R5.1 fallback single-flight·double-check 회귀 증거 보강
|
||||
|
||||
**Goal 실행 `P5-R1`:** `REV-P5-001`에서 누락이 확인된 동일 섹션 동시 요청 single-flight와 lock 내부 double-check를 결정적 테스트로 고정한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-5-review.md`의 `REV-P5-001` 확정.
|
||||
- **완료 증거:** 동시성·double-check RED 테스트 작성·확인, 필요한 경우 최소 구현, fallback focused test 통과, 전체 검증 기록 누적.
|
||||
- **범위 밖:** worker 수 조정, timeout·lock 대기 값 변경, 다른 섹션 fallback 정책 확대.
|
||||
|
||||
- [x] **RED:** 동일 JVM에서 동시에 들어온 둘 이상의 `CHEER_CREATOR` fallback 요청이 하나의 refresh future만 공유하고 `refreshCheerCreatorSnapshots(...)`를 1회만 호출하는 테스트를 latch 기반으로 추가한다.
|
||||
- [x] **RED:** 최초 조회 뒤 lock 획득 전 다른 요청/스케줄러가 대상일 marker 또는 실제 row를 저장하면 lock 안의 double-check가 refresh를 생략하고 최신 상태를 다시 조회하는 테스트를 추가한다.
|
||||
- [x] **GREEN:** 새 테스트가 구현 결함을 드러낼 때만 `refreshFutures` 또는 lock 내부 존재 확인 경로를 최소 수정한다.
|
||||
- [x] **REFACTOR/GATE:** sleep 없이 `RecommendationSnapshotFallbackServiceTest` focused test와 `git diff --check`를 실행해 결과를 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: API 회귀와 최종 검증
|
||||
@@ -262,18 +356,104 @@
|
||||
- REFACTOR: 문서와 코드의 산식/timeout/window 값이 다르면 구현 또는 문서를 수정한 뒤 재검증한다.
|
||||
- 기대 결과: 전체 테스트, 포맷, 문서 명령 유효성, diff 공백 검사가 모두 통과한다.
|
||||
|
||||
#### Task R6.1 `cheerCreators` item 공개 스키마 회귀 테스트 보강
|
||||
|
||||
**Goal 실행 `P6-R1`:** `REV-P6-001`에서 확인한 빈 배열 중심 스키마 검증을 실제 item의 정확한 필드 계약 검증으로 보강한다.
|
||||
|
||||
- **시작 조건:** `reviews/phase-6-review.md`의 `REV-P6-001` 확정.
|
||||
- **완료 증거:** 비어 있지 않은 item 직렬화/컨트롤러 테스트, 정확한 필드 집합 검증, API focused test 통과, 전체 검증 기록 누적.
|
||||
- **범위 밖:** DTO 필드 추가·이름 변경, API URL 변경, 다른 홈 섹션 스키마 정리.
|
||||
|
||||
- [x] **RED:** `cheerCreators`에 item을 넣고 `creatorId`, `creatorNickname`, `creatorProfileImage` 값과 필드 수 3을 검증하는 직렬화 테스트를 추가한다.
|
||||
- [x] **RED:** 홈 API 테스트에서 비어 있지 않은 `cheerCreators` item의 동일 필드 계약을 검증하고 예상 밖 필드가 없음을 확인한다.
|
||||
- [x] **GREEN:** 기존 DTO가 테스트를 만족하면 프로덕션 코드는 변경하지 않고, 계약 불일치가 드러날 때만 기존 공개 스키마로 최소 복구한다.
|
||||
- [x] **REFACTOR/GATE:** `HomeRecommendationControllerTest`, `HomeRecommendationResponseTest`, `git diff --check`를 실행해 결과를 누적한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: 본인·팔로우 크리에이터 노출 제외
|
||||
|
||||
**Phase 결과:** 인증 회원의 `cheerCreators`에서 조회자 본인과 활성 팔로우 크리에이터가 제외되고, 비활성 팔로우 이력과 비회원 조회는 기존 동작을 유지한다.
|
||||
|
||||
**선행조건:** Phase 1~6 완료와 PRD Feature D의 2026-07-31 후속 요구사항 확정.
|
||||
|
||||
**Phase 완료 조건:** `P7-T1`과 `P7-GATE` 완료, focused test·영향 범위 회귀·문서 검증 결과 누적.
|
||||
|
||||
#### Task 7.1 `cheerCreators` 상세 조회 개인화 필터 보강
|
||||
|
||||
**Goal 실행 `P7-T1`:** 기존 `memberId` 기반 상세 조회에서 본인과 활성 팔로우 크리에이터만 제외하는 최소 조회 조건을 추가한다.
|
||||
|
||||
- **시작 조건:** PRD Feature D의 본인·활성 팔로우 제외, 비활성 팔로우·비회원 유지, 16명 후보 안에서만 필터링한다는 결정 확정.
|
||||
- **완료 증거:** 아래 체크박스 전체 완료, repository focused test 통과, 직접 영향 회귀 통과, 실행 결과를 이 문서의 전체 검증 기록에 누적.
|
||||
- **범위 밖:** 스냅샷 산식·정렬·저장 수, 16명 밖 하위 후보 보충, 공개 DTO/API, 다른 추천 섹션의 팔로우 필터.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/DefaultHomeRecommendationQueryRepository.kt`
|
||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/DefaultHomeRecommendationQueryRepositoryTest.kt`
|
||||
- Verify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/HomeRecommendationQueryServiceTest.kt`
|
||||
- Verify: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/home/HomeRecommendationControllerTest.kt`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: `HomeRecommendationQueryPort.findCheerCreatorRecommendationDetails(creatorIds: List<Long>, memberId: Long?): List<HomeCheerCreatorRecommendationRecord>`
|
||||
- Produces: 같은 시그니처와 반환 타입을 유지하면서, `memberId != null`일 때만 본인과 활성 팔로우를 제외하는 상세 조회 계약.
|
||||
|
||||
- [x] **RED:** `DefaultHomeRecommendationQueryRepositoryTest`에 조회자 크리에이터, 활성 팔로우 크리에이터, 비활성 팔로우 이력만 있는 크리에이터, 관계가 없는 크리에이터를 준비한다. `memberId = viewer.id`로 조회했을 때 비활성 팔로우 이력 크리에이터와 관계가 없는 크리에이터만 반환하는 `shouldExcludeSelfAndActiveFollowedCreatorsFromCheerCreatorDetails` 테스트를 작성한다. `memberId = null`은 모든 활성 후보를 유지하는 `shouldKeepAnonymousCheerCreatorDetailsWithoutMemberFilters` 테스트도 작성한다.
|
||||
- [x] **RED 확인:** `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest`를 실행해 현재 조회가 조회자 본인과 활성 팔로우 크리에이터를 함께 반환하여 첫 번째 테스트가 의도한 assertion 실패를 내는지 확인한다.
|
||||
- [x] **GREEN:** `findCheerCreatorRecommendationDetails(...)`에 `memberId`가 있을 때 `member.id != memberId`를 적용하고, 동일 회원과 후보 크리에이터 사이에 `CreatorFollowing.isActive == true`인 row가 존재하지 않는 조건을 추가한다. `memberId == null`이면 두 조건은 적용하지 않는다.
|
||||
- [x] **GREEN 확인:** 같은 repository focused test를 재실행해 본인·활성 팔로우 제외, 비활성 팔로우·비회원 유지, 기존 양방향 차단 제외 테스트가 모두 통과하는지 확인한다.
|
||||
- [x] **REFACTOR:** 이번 조건에 필요한 QueryDSL alias/helper만 남기고, `HomeRecommendationQueryServiceTest`로 기존 16명 후보 조회·필터 후 최대 8명 조립이 유지되는지 확인한다. `HomeRecommendationControllerTest`와 `ktlintCheck`로 공개 스키마·포맷 회귀를 확인하고 실제 명령·결과를 전체 검증 기록에 남긴다.
|
||||
|
||||
### Phase 7 완료 조건
|
||||
|
||||
- [x] `P7-T1`의 RED·GREEN·REFACTOR 체크박스와 완료 증거가 모두 충족됐다.
|
||||
- [x] 인증 회원 본인·활성 팔로우만 제외되고 비활성 팔로우·비회원·양방향 차단·활성 크리에이터 정책이 조합되는 것이 검증됐다.
|
||||
- [x] 스냅샷 산식·저장 수·정렬, 응답 DTO, 다른 추천 섹션에 변경이 없다.
|
||||
|
||||
#### Phase 7 Gate
|
||||
|
||||
**Goal 실행 `P7-GATE`:** `cheerCreators` 개인화 필터와 직접 영향 회귀를 최종 판정한다.
|
||||
|
||||
- **시작 조건:** `P7-T1` 완료.
|
||||
- **완료 증거:** 아래 명령 전부 통과, `git diff --check` 출력 없음, 전체 검증 기록 누적.
|
||||
- **범위 밖:** 게이트 통과를 위한 테스트 삭제·완화와 관련 없는 리팩터링.
|
||||
|
||||
```bash
|
||||
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest
|
||||
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest
|
||||
./gradlew ktlintCheck
|
||||
git diff --check
|
||||
```
|
||||
|
||||
**Expected:** 모든 명령이 exit code 0이고, 인증 회원 본인·활성 팔로우는 제외되며 비활성 팔로우·비회원 결과와 기존 API 스키마는 유지된다.
|
||||
|
||||
---
|
||||
|
||||
## Coverage Check
|
||||
- Feature A: Task 2.1, Task 3.2, Task 3.4에서 후원 금액 45%, 팬Talk 30%, 후원 수 10%, 후원 수 distinct 기준을 검증한다.
|
||||
- Feature B: Task 3.1, Task 3.3에서 최근 7일 KST 범위를 UTC half-open 조회 범위로 변환하고 `windowEndExclusiveUtc` 경계를 검증한다.
|
||||
- Feature C: Task 2.2, Task 3.4에서 데뷔일 기준 응원 전용 신규 부스트와 경계값을 검증한다.
|
||||
- Feature D: Task 3.5, Task 5.4, Task 6.1에서 최신 `CHEER_CREATOR` 스냅샷 순서, 후보 16개/응답 8개, 기존 응답 스키마 유지를 검증한다.
|
||||
- Feature D: Task 3.5, Task 5.4, Task 6.1, Task 7.1에서 최신 `CHEER_CREATOR` 스냅샷 순서, 후보 16개/응답 8개, 본인·활성 팔로우 제외, 비활성 팔로우·비회원 유지, 기존 응답 스키마 유지를 검증한다.
|
||||
- Feature E: Task 5.1, Task 5.2, Task 5.3, Task 5.4에서 fallback refresh 재사용, double-check, 300ms lock 대기, 1,500ms 홈 API 대기, timeout 후 background 완료, 중복 refresh 방지를 검증한다.
|
||||
- Feature F: Task 4.1, Task 4.2, Task 5.3에서 `CHEER_CREATOR` empty marker 저장, 조회 제외, 존재 여부 true, marker 기반 fallback 반복 방지를 검증한다.
|
||||
- Non-Goals: Task 4.1, Task 6.1, Task 6.3에서 다른 스냅샷 섹션 marker 확장 없음, 공개 API URL/응답 필드 변경 없음, 신규 DDL 없음, 관리자/ML/A-B 제외를 확인한다.
|
||||
- Non-Goals: Task 4.1, Task 6.1, Task 6.3, Task 7.1에서 다른 스냅샷 섹션 marker 확장 없음, 공개 API URL/응답 필드 변경 없음, 16명 밖 후보 보충 없음, 신규 DDL 없음, 관리자/ML/A-B 제외를 확인한다.
|
||||
|
||||
## 전체 검증 기록
|
||||
- 2026-07-31: Phase 1~7 3차 리뷰로 PRD·plan-task·현재 코드·테스트를 정적으로 대조했다. 이전 리뷰에서 보완한 문서 정합성, 종료 라이브 데뷔 이력, `CHEER_CREATOR` refresh 실패 로그, fallback single-flight·double-check, 공개 응답 3개 필드, 본인·활성 팔로우 제외가 현재 구현과 회귀 테스트에 유지됨을 확인했다. 각 결과는 기존 `reviews/phase-1-review.md`~`reviews/phase-7-review.md`에 3차 리뷰로 별도 누적했다. 추가 확정 발견 사항이 없어 신규 회귀 Task/Goal은 추가하지 않았다. 사용자 지시에 따라 Gradle compile/test는 실행하지 않았다. `git diff --check`는 출력 없이 통과했다. 문서 명령 유효성 확인용 `./gradlew tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 권한으로 실패한 뒤 승인된 동일 명령에서 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 2026-07-31: `P3-R2`로 `CHEER_CREATOR` 데뷔 CTE가 채널명이 있는 종료 라이브를 데뷔 이력으로 인정하도록 복구했다. RED 확인으로 `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest`를 실행해 `shouldIncludeEndedLiveWithChannelNameAsCheerCreatorDebut`가 `AssertionFailedError`로 실패하는 것을 확인했다. 이후 `findCheerCreatorSnapshots(...)` 라이브 branch에서 `lr.is_active = true`만 제거했고, 같은 repository focused test는 `BUILD SUCCESSFUL`로 통과했다. `./gradlew ktlintCheck`도 `BUILD SUCCESSFUL`로 통과했으며, `git diff --check`는 출력 없이 통과했다.
|
||||
- 2026-07-31: `P1-R2`로 상단 후속 변경 상태를 현재 PRD 전체, Phase 1~7 판정, 실제 미완료 Goal `P3-R2` 기준으로 정리했다. 문서 정합성 복구만 수행해 TDD 예외로 처리했다. `git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md`는 출력 없이 통과했고, `./gradlew tasks --all`은 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 2026-07-31: Phase 1~7 2차 리뷰로 PRD·plan-task·현재 코드·테스트·관련 구현 이력을 정적 대조했다. `REV-P1-002`의 상단 상태표 불일치와 `REV-P3-002`의 종료 라이브 데뷔 이력 제외를 확정해 각각 `P1-R2`, `P3-R2` 신규 회귀 Task로 전환했고, Phase 2·4·5·6·7은 추가 확정 발견 사항이 없다. 결과는 기존 `reviews/phase-1-review.md`~`reviews/phase-7-review.md`에 2차 리뷰로 각각 누적했다. 사용자 지시에 따라 Gradle compile/test는 실행하지 않았다. `git diff --check`는 출력 없이 통과했고, `./gradlew tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 권한으로 실패한 후 승인된 동일 명령으로 재실행해 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 2026-07-31: `P1-R1`, `P3-R1`, `P4-R1`, `P5-R1`, `P6-R1` 후속 보완의 최종 focused 회귀로 `./gradlew cleanTest test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest --tests kr.co.vividnext.sodalive.v2.api.home.dto.recommendation.HomeRecommendationResponseTest`를 실행해 `BUILD SUCCESSFUL`로 통과했다. 이어서 `./gradlew ktlintCheck`는 `BUILD SUCCESSFUL`, `git diff --check`는 출력 없이 통과했다. 전체 `./gradlew test`는 테스트 assertion 실패가 아니라 `build/test-results/test/TEST-*.xml` 결과 파일 쓰기 실패로 중단되어 별도 환경/파일시스템 이슈 확인이 필요하다.
|
||||
- 2026-07-31: `P1-R1`로 `prd.md`의 샘플 PRD 링크를 실제 파일 `docs/sample/sample-prd.md`로 정정했다. `test -f docs/sample/sample-prd.md`와 `git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/prd.md`를 실행해 출력 없이 통과했다.
|
||||
- 2026-07-31: `P3-R1`로 `CHEER_CREATOR` 후보 제외·상위 16개·동점 저장 정렬 회귀 테스트를 보강했다. 리뷰 판정처럼 구현 결함은 드러나지 않아 프로덕션 코드는 변경하지 않았다. `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest`는 `BUILD SUCCESSFUL`, 관련 `git diff --check`는 출력 없이 통과했다.
|
||||
- 2026-07-31: `P4-R1`로 `refreshCheerCreatorSnapshots(nowUtc)` 실패 시 `event=cheer_creator_recommendation_snapshot_refresh_failure`와 window·`snapshotAt`·오류 정보를 남기고 원 예외를 전파하도록 보강했다. RED 확인으로 `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest.shouldLogCheerCreatorRefreshFailureWithWindow`가 `AssertionFailedError`로 실패했고, GREEN 후 `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest`는 `BUILD SUCCESSFUL`, 관련 `git diff --check`는 출력 없이 통과했다.
|
||||
- 2026-07-31: `P5-R1`로 동일 섹션 동시 요청 single-flight와 lock 내부 double-check를 sleep 없이 latch 기반 테스트로 보강했다. 리뷰 판정처럼 구현 결함은 드러나지 않아 프로덕션 코드는 변경하지 않았다. `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest`는 `BUILD SUCCESSFUL`, 관련 `git diff --check`는 출력 없이 통과했다.
|
||||
- 2026-07-31: `P6-R1`로 `cheerCreators` item 직렬화와 홈 API 응답의 `creatorId`, `creatorNickname`, `creatorProfileImage` 3개 필드 계약을 보강했다. 최초 controller focused test는 대상일 snapshot fixture 불일치와 테스트 환경 CDN host 기대값 불일치로 실패했고, fixture를 `RecommendationSnapshotWindowPolicy.previousKstDayUtcWindow(LocalDateTime.now(UTC)).snapshotAt` 및 실제 테스트 host 설정에 맞춘 뒤 `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest --tests kr.co.vividnext.sodalive.v2.api.home.dto.recommendation.HomeRecommendationResponseTest`가 `BUILD SUCCESSFUL`로 통과했다. 관련 `git diff --check`는 출력 없이 통과했다.
|
||||
- 2026-07-31: Phase별 리뷰 문서와 회귀 Task 추가 후 `git diff --check`를 실행해 출력 없이 통과했다. 문서 명령 유효성 확인용 `./gradlew tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 권한으로 실패했고, 승인 후 동일 명령을 재실행해 `BUILD SUCCESSFUL`로 통과했다. compile/test task는 실행하지 않았다.
|
||||
- 2026-07-31: Phase 1~7 구현 상태를 PRD·plan-task·현재 코드·테스트와 정적으로 대조했다. 사용자 지시에 따라 Gradle compile/test는 새로 실행하지 않았고 기존 검증 기록을 실행 증거로 참조했다. 확정 항목은 `reviews/phase-1-review.md`부터 `reviews/phase-7-review.md`까지 Phase별로 기록했으며, `REV-P1-001`, `REV-P3-001`, `REV-P4-001`, `REV-P5-001`, `REV-P6-001`을 각각 `P1-R1`, `P3-R1`, `P4-R1`, `P5-R1`, `P6-R1` 신규 회귀 Task로 전환했다. Phase 2와 Phase 7은 확정 발견 사항이 없다.
|
||||
- 2026-07-31: Phase 7 `P7-T1` 구현으로 `findCheerCreatorRecommendationDetails(...)`가 인증 회원 조회 시 조회자 본인과 `CreatorFollowing.isActive == true`인 팔로우 크리에이터를 제외하도록 보강했다. RED 확인으로 `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest`를 실행해 `shouldExcludeSelfAndActiveFollowedCreatorsFromCheerCreatorDetails` assertion 실패를 확인했고, GREEN 후 같은 명령은 `BUILD SUCCESSFUL`로 통과했다. 직접 영향 회귀 `./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest`와 `./gradlew ktlintCheck`도 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 2026-07-31: 후속 요구사항으로 인증 회원 본인과 활성 팔로우 크리에이터를 `cheerCreators` 상세 조회에서 제외하는 정책을 PRD Feature D와 Phase 7 `P7-T1`/`P7-GATE`에 반영했다. 비활성 팔로우·비회원 유지와 16명 후보 밖 보충 없음을 경계로 고정했다. `git diff --check`는 출력 없이 통과했다. `./gradlew tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 파일 권한으로 실패했고, 승인 후 동일 명령을 재실행해 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 2026-07-10: PRD 기반으로 `plan-task.md`를 생성했다. 구현 전 계획 문서 작성 작업이므로 코드 테스트는 아직 실행하지 않았고, 문서 형식/명령 유효성 검증을 진행한다.
|
||||
- 2026-07-10: 문서 검증으로 `git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md`를 실행해 통과했다. `./gradlew tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 파일 접근 제한으로 실패했고, 권한 상승 재실행 결과 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 2026-07-10: 구현 RED 확인으로 `RecommendationScorePolicyTest`는 `CHEER_NEW_BOOST_*`와 `calculateCheerCreatorNewBoost(...)` 미구현 컴파일 실패를 확인했고, `DefaultHomeRecommendationQueryRepositoryTest`는 half-open/distinct 집계 기대값 불일치 실패를 확인했다. `RecommendationSnapshotPersistenceAdapterTest`는 `CHEER_CREATOR` empty marker 미지원 실패를 확인했다.
|
||||
|
||||
Reference in New Issue
Block a user