58 KiB
메인 홈 추천 응원 크리에이터 스냅샷 수정 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일20일1.15, 111.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_CREATORrefresh 결과가 0건이면targetId = 0marker를 저장해 정상 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.
범위와 전제
- 이번 문서는
docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/prd.md의 구현 계획이다. - 신규 공개 API, 신규 응답 필드, 운영 DDL 추가는 범위에 포함하지 않는다.
- 기존
recommendation_snapshot테이블과RecommendedSectionType.CHEER_CREATOR를 재사용한다. CHEER_CREATOR집계는 현재 구조와 성능 특성을 유지해 DB-side exact scoring을 기본으로 한다. Kotlin 단에는 산식/부스트 근거 테스트용 정책 함수를 둔다.- 집계 기간은 인기 커뮤니티와 동일하게 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_CREATORenum 값과 code는 변경하지 않는다. - 유지: 홈 응원 크리에이터 응답 필드인
creatorId,creatorNickname,creatorProfileImage는 변경하지 않는다. - 유지: 홈 첫 화면 응답은 최대 8명, 스냅샷 후보 조회는 최대 16개를 사용한다.
- 유지: 상세 조회 시점의 활성 크리에이터 필터와 차단 필터는 유지한다.
- 유지: 비회원 조회와 비활성 팔로우 이력이 있는 크리에이터 조회는 기존 동작을 유지한다.
- 유지: 후원 금액은
use_can_calculate.can값을 그대로 합산한다. - 변경: 점수 가중치는 후원 금액 45%, 팬Talk 수 30%, 후원 수 10%로 바꾼다.
- 변경: 후원 수는
UseCanCalculate.useCan기준 distinct count로 계산한다. - 변경: 팬Talk 수는
CreatorCheers.isActive == truerow 수로 계산한다. - 변경: 집계 기간은 인기 커뮤니티와 동일한 최근 7일로 한다.
- 변경: 신규 부스트는 기존 크리에이터 공통 부스트
1.5/1.3/1.2가 아니라CHEER_CREATOR전용1.15/1.10/1.05를 사용한다. - 추가:
CHEER_CREATOR최신 스냅샷이 없을 때 fallback refresh를 실행한다. - 추가:
CHEER_CREATORrefresh 결과 0건이면 empty snapshot marker를 저장한다. - 추가: 인증 회원 본인과 활성 팔로우 중인 크리에이터를
cheerCreators상세 조회에서 제외한다.
실행 명령
- 문서 명령 확인:
./gradlew tasks --all - 산식/부스트 단위 테스트:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationScorePolicyTest - 스냅샷 저장 marker 테스트:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest - 응원 크리에이터 query 통합 테스트:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest - refresh/fallback 조회 테스트:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest - fallback service 테스트:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest - 홈 API 회귀 테스트:
./gradlew test --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest --tests kr.co.vividnext.sodalive.v2.api.home.dto.recommendation.HomeRecommendationResponseTest - 포맷 검증:
./gradlew ktlintCheck - 전체 회귀:
./gradlew test
Phase 1: 문서와 기준 고정
- Task 1.1: PRD 기반 구현 계획 문서 작성
- 파일 경로:
- Verify:
docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/prd.md - Create:
docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md
- Verify:
- RED: 문서 작업은 TDD 예외. TDD 예외 사유: 코드 동작 변경 전 요구사항과 구현 순서를 고정하는 작업이다.
- GREEN: PRD의 산식, 최근 7일 UTC half-open 범위, 후원 수 distinct 기준, empty marker, fallback timeout/lock 정책을 task로 분해한다.
- 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 예외 사유: 문서 링크 정정만 수행하며 런타임 동작을 변경하지 않는다.
-
prd.md의docs/prd/sample-prd.md를 실제 파일인docs/sample/sample-prd.md로 정정한다. -
test -f docs/sample/sample-prd.md와git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/prd.md로 링크 대상과 문서 diff를 확인한다. -
무엇을/왜/어떻게 검증했는지 전체 검증 기록에 누적한다.
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 예외 사유: 현재 작업 상태 문구만 복구하며 런타임 동작을 변경하지 않는다.
-
상태표의 요구사항 기준을 PRD 전체로, Phase 상태를 Phase 1~7 현재 판정으로, 다음 Goal을 실제 미완료 Goal로 맞춘다.
-
git diff --check -- docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md와./gradlew tasks --all로 문서 diff와 명령 유효성을 확인한다. -
완료 후 다음 Goal을
P3-R2로 갱신하고 전체 검증 기록에 무엇을/왜/어떻게 검증했는지 누적한다.
Phase 2: 산식과 부스트 정책
-
Task 2.1: 응원 점수 계산식 계약 테스트 보강
- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScoreSpec.kt - Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicy.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicyTest.kt
- Modify:
- RED:
CHEER_DONATION_AMOUNT_WEIGHT = 0.45,CHEER_FAN_TALK_WEIGHT = 0.30,CHEER_DONATION_COUNT_WEIGHT = 0.10을 기대하는 실패 테스트를 작성한다.calculateCheerScore(...)가 세 입력값과 부스트를 PRD 산식대로 계산하는지도 검증한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationScorePolicyTest - GREEN: 응원 점수 상수를 PRD 값으로 변경하고 기존
calculateCheerScore(...)가 같은 상수를 사용하게 한다. - REFACTOR: 기존 AI/최근 데뷔/인기 커뮤니티 상수와 함수 값은 변경하지 않았는지 같은 테스트 안에서 회귀 assertion을 유지한다.
- 계산식 테스트 케이스:
donationAmount=0,fanTalkCount=0,donationCount=0,newBoost=1.0이면0.0donationAmount=100,fanTalkCount=0,donationCount=0,newBoost=1.0이면45.0donationAmount=0,fanTalkCount=10,donationCount=0,newBoost=1.0이면3.0donationAmount=0,fanTalkCount=0,donationCount=10,newBoost=1.0이면1.0donationAmount=100,fanTalkCount=10,donationCount=10,newBoost=1.0이면49.0donationAmount=100,fanTalkCount=10,donationCount=10,newBoost=1.15이면56.35
- 기대 결과: 응원 점수 산식 근거가 Kotlin 단위 테스트로 고정된다.
- 파일 경로:
-
Task 2.2: 응원 크리에이터 신규 부스트 경계값 테스트 추가
- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScoreSpec.kt - Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicy.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/domain/RecommendationScorePolicyTest.kt
- Modify:
- RED: 응원 크리에이터 전용 신규 부스트가 데뷔일 기준 0일/10일
1.15, 11일/20일1.10, 21일/30일1.05, 31일1.0으로 계산되는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationScorePolicyTest - GREEN:
calculateCheerCreatorNewBoost(debutAt, now)또는 동등한 전용 함수를 추가하고CHEER_NEW_BOOST_*상수를 사용한다. - REFACTOR: 기존
calculateCreatorNewBoost(...)는 최근 데뷔/인기 커뮤니티용 기존 값1.5/1.3/1.2를 유지한다. - 기대 결과:
CHEER_CREATOR만 낮아진 신규 부스트 값을 사용하고 다른 크리에이터 기반 섹션은 기존 부스트를 유지한다.
- 파일 경로:
Phase 3: 최근 7일 집계 window와 DB 스냅샷 query
-
Task 3.1:
CHEER_CREATORrefresh window를 인기 커뮤니티와 동일한 최근 7일로 변경- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshService.kt - Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/port/out/HomeRecommendationQueryPort.kt - 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/application/RecommendationSnapshotRefreshServiceTest.kt
- Modify:
- RED:
refreshCheerCreatorSnapshots(nowUtc)가RecommendationSnapshotWindowPolicy.previousKstSevenDayUtcWindow(nowUtc)로 얻은windowStartUtc,windowEndExclusiveUtc,snapshotAt을 사용하도록 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest - GREEN:
CHEER_CREATOR단일 refresh 경로를 분리하고, 기존 일괄 refresh에서 인기 커뮤니티와 같은 최근 7일 UTC half-open window를 넘긴다. - REFACTOR:
POPULAR_COMMUNITY의 기존 7일 window와 동일한 window를 사용한다.HomeRecommendationQueryPort.findCheerCreatorSnapshots(...)시그니처는windowEndExclusiveUtc의미가 드러나도록 정리한다. - 기대 결과: 스케줄러와 fallback이 같은
CHEER_CREATOR최근 7일 refresh 경로를 호출할 수 있다.
- 파일 경로:
-
Task 3.2: 채널 후원과 일반 후원 금액/후원 수 집계 기준 변경
- 파일 경로:
- 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
- Modify:
- RED: 같은
UseCanCalculate.useCan을 참조하는 여러 row가 있을 때donationAmount는use_can_calculate.can값을 그대로 합산하고,donationCount는 distinctuse_can기준 1건으로 계산되는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest - GREEN: donation stats query에서 금액은 기존
sum(ucc.can)을 유지하고, 후원 수는count(distinct ucc.use_can_id)또는 엔티티 매핑에 맞는 동일 의미 컬럼으로 변경한다. - REFACTOR:
CanUsage.CHANNEL_DONATION과CanUsage.DONATION을 포함하고,status = RECEIVED,is_refund = false등 기존 제외 조건은 유지한다. - 기대 결과: 후원 이벤트 단위 중복 제거가 점수의 후원 수 항목에만 적용된다.
- 파일 경로:
-
Task 3.3: 팬Talk 수와 half-open 시간 조건 적용
- 파일 경로:
- 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
- Modify:
- RED:
CreatorCheers.isActive == truerow만 집계하고,created_at >= windowStartUtc and created_at < windowEndExclusiveUtc조건으로 집계 경계를 검증하는 실패 테스트를 작성한다.windowEndExclusiveUtc와 같은 시각의 row는 제외되어야 한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest - GREEN:
creator_cheers집계 조건을 active row와 half-open time range로 맞춘다. - REFACTOR: 후원 집계 조건도 같은 half-open range를 사용해
<= :snapshotAt방식이 남지 않게 정리한다. - 기대 결과: 최근 7일 KST 경계가 후원과 팬Talk 집계에 동일하게 적용된다.
- 파일 경로:
-
Task 3.4: 응원 크리에이터 DB-side 점수와 부스트 적용
- 파일 경로:
- 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
- Modify:
- RED: DB 스냅샷 query 결과 점수가
RecommendationScorePolicy.calculateCheerScore(...)와 동일하고, 데뷔일 기준 신규 부스트1.15/1.10/1.05/1.0경계값을 반영하는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest - GREEN: native SQL score expression과 boost case expression을
RecommendationScoreSpec의 응원 전용 상수로 변경한다. - REFACTOR:
creator_debut계산은 콘텐츠 첫 공개일과 라이브 첫 진행일 중 빠른 날짜라는 기존 정의를 유지한다. - 기대 결과: DB-side exact scoring과 Kotlin 정책 테스트의 산식 값이 일치한다.
- 파일 경로:
-
Task 3.5: 후보 제외, 정렬, limit 회귀 테스트 보강
- 파일 경로:
- 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
- Modify:
- RED: 후원/팬Talk가 모두 0인 크리에이터 제외, 데뷔일이 없는 크리에이터 제외, 비활성 크리에이터 제외, 점수 내림차순/
randomTieBreaker오름차순/limit 16 동작을 검증하는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest - GREEN: 기존 후보 제외 조건과 정렬/limit을 PRD 기준에 맞게 유지 또는 보정한다.
- 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 통과, 전체 검증 기록 누적.
-
범위 밖: 점수 가중치·집계 기간·스냅샷 저장 수 변경, 랜덤 정책 변경, 다른 추천 섹션 쿼리 수정.
-
RED: 실제 데뷔 이력이 있지만 후원·팬Talk가 모두 0인 후보, 미래 데뷔 이력만 있는 후보, 비활성 크리에이터가 제외되는 테스트를 추가한다.
-
RED: 17개 이상의 점수 후보에서 점수 내림차순 상위 16개만 반환되는 repository 테스트와, 저장된 동점 스냅샷이
randomTieBreaker오름차순으로 조회되는CHEER_CREATORpersistence 테스트를 추가한다. -
GREEN: 새 테스트가 구현 결함을 드러낼 때만 해당 조건·정렬 경로를 최소 수정하고, 테스트 누락뿐이면 프로덕션 코드는 변경하지 않는다.
-
REFACTOR/GATE:
DefaultHomeRecommendationQueryRepositoryTest와RecommendationSnapshotPersistenceAdapterTestfocused 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 변경.
-
RED: 활성 콘텐츠는 없고
channel_name이 있는 종료 라이브와 최근 7일 응원 활동만 있는 활성 크리에이터가CHEER_CREATOR후보에 포함되는 실패 테스트를 추가한다. 빈channel_name의 종료 라이브는 데뷔 이력으로 인정하지 않는 경계를 함께 유지한다. -
RED 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest를 실행해 현재lr.is_active = true조건 때문에 종료 라이브 후보가 누락되는 assertion 실패를 확인한다. -
GREEN:
findCheerCreatorSnapshots(...)의creator_debut라이브 branch에서lr.is_active = true만 제거하고,channel_name is not null,channel_name <> '',begin_date_time <= :snapshotAt조건은 유지한다. -
REFACTOR/GATE: repository focused test,
./gradlew ktlintCheck,git diff --check를 실행하고 점수·window·후보 상한·다른 섹션 쿼리가 변경되지 않았음을 기록한다.
Phase 4: refresh 저장과 empty marker
-
Task 4.1:
CHEER_CREATOR빈 결과 marker 저장 정책 추가- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/RecommendationSnapshotPersistenceAdapter.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/RecommendationSnapshotPersistenceAdapterTest.kt
- Modify:
- RED:
replaceSnapshots(RecommendedSectionType.CHEER_CREATOR, snapshotAt, emptyList())호출 시targetId = 0marker가 저장되고,findLatestSnapshots(CHEER_CREATOR)는 빈 배열이며,existsLatestSnapshot(CHEER_CREATOR)는 true인 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest - GREEN: empty marker 적용 대상을
AI_CHARACTER와CHEER_CREATOR로 제한하는 명시적 상수/함수를 추가한다. - REFACTOR:
POPULAR_COMMUNITY등 다른 섹션으로 marker 정책을 확장하지 않는다. - 기대 결과: 데이터가 없는 날에도
CHEER_CREATORrefresh 완료 상태가 저장된다.
- 파일 경로:
-
Task 4.2: 실제 row 재실행 시 marker 대체 보장
- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/RecommendationSnapshotPersistenceAdapter.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/adapter/out/persistence/RecommendationSnapshotPersistenceAdapterTest.kt
- Modify:
- RED: 같은
sectionType,snapshotAt에 marker가 있는 상태에서 실제 스냅샷 row로replaceSnapshots(...)를 호출하면 marker가 제거되고 실제 row만 남는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest - GREEN: 기존
deleteBySectionTypeAndSnapshotAt후 저장 흐름이 marker 대체를 보장하는지 확인하고, 부족하면 해당 경로만 보정한다. - REFACTOR: 조회 쿼리의
target_id <> 0조건은 유지한다. - 기대 결과: 빈 결과 refresh 후 재실행으로 실제 추천 row가 생겨도 marker가 응답/존재 상태를 오염시키지 않는다.
- 파일 경로:
-
Task 4.3: refresh service가
CHEER_CREATOR저장 수와 marker 상태를 로그로 남김- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshService.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshServiceTest.kt
- Modify:
- RED:
refreshCheerCreatorSnapshots(nowUtc)성공 시event=cheer_creator_recommendation_snapshot_refresh_success,savedCount,windowStartUtc,windowEndExclusiveUtc,snapshotAt이 로그에 남는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest - GREEN: 기존 구조화 로그 관례에 맞춰 section refresh 성공/실패 로그를 추가한다.
- REFACTOR: 전체 일괄 refresh 성공 로그는 유지하되, 섹션별 로그와 중복되어도 검색 가능한 event key를 사용한다.
- 기대 결과: 운영에서
CHEER_CREATORrefresh 결과 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 트랜잭션 정책 변경.
-
RED:
refreshCheerCreatorSnapshots(nowUtc)의 query 또는 저장 실패 시event=cheer_creator_recommendation_snapshot_refresh_failure, window·snapshotAt, 오류 정보가 기록되고 예외는 기존처럼 전파되는 테스트를 추가한다. -
GREEN: 성공 경로를 변경하지 않는 최소
runCatching또는try/catch로그를 추가한 뒤 원래 예외를 다시 던진다. -
REFACTOR/GATE:
RecommendationSnapshotRefreshServiceTest와git diff --check를 실행하고 중복 로그가 의도된 event key로 구분되는지 확인한다. -
무엇을/왜/어떻게 검증했는지 전체 검증 기록에 누적한다.
Phase 5: fallback refresh
-
Task 5.1: 섹션 스냅샷 fallback 공통 runner 도입
- 파일 경로:
- Create:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackService.kt - Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/AiCharacterSnapshotFallbackPort.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackServiceTest.kt
- Create:
- RED: 공통 runner가 section type, lock key, refresh action, offset/limit을 받아 double-check 조회 후 refresh를 실행하는 실패 테스트를 작성한다. 기존 AI 캐릭터 fallback 테스트도 통과해야 한다.
- 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest - GREEN: AI 캐릭터 전용 lock/timeout/single-flight 흐름을 공통 runner로 이동하거나, AI 서비스가 공통 runner를 위임 호출하도록 최소 변경한다.
- REFACTOR: Redisson
RLock획득/해제는 refresh worker thread 안에서 수행한다. 테스트에서는 deterministic executor를 사용해 sleep 기반 테스트를 피한다. - 기대 결과:
CHEER_CREATORfallback을 추가할 때 lock/double-check/timeout 구현을 중복 작성하지 않는다.
- 파일 경로:
-
Task 5.2:
CHEER_CREATORfallback target 추가- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackService.kt - Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotRefreshService.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackServiceTest.kt
- Modify:
- RED: 최신
CHEER_CREATOR스냅샷이 없을 때 lock keylock:recommendation-snapshot-refresh:CHEER_CREATOR로 lock을 얻고,refreshCheerCreatorSnapshots(nowUtc)를 호출한 뒤 저장된 스냅샷을 재조회하는 실패 테스트를 작성한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest - GREEN:
CHEER_CREATORfallback target을 등록하고 lock 대기 300ms, 홈 대기 1,500ms 상수를 PRD 값으로 유지한다. - REFACTOR:
AI_CHARACTERfallback lock key와 상호 간섭하지 않도록 section별 key를 분리한다. - 기대 결과: 스냅샷 없음 상황에서 홈 API가 스케줄러와 동일한
CHEER_CREATORrefresh 로직을 재사용한다.
- 파일 경로:
-
Task 5.3: lock miss, timeout, 실패, empty marker fallback 케이스 검증
- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackService.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/RecommendationSnapshotFallbackServiceTest.kt
- Modify:
- RED: lock 획득 실패 시 중복 refresh를 시작하지 않는 테스트, 홈 대기 1,500ms timeout 시 빈 배열을 반환하되 background refresh를 cancel하지 않는 테스트, refresh 실패 시 빈 배열과 warn log를 남기는 테스트, marker가 있으면 fallback을 반복하지 않는 테스트를 작성한다.
- 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest - GREEN: 공통 runner의 future/single-flight 상태, 예외 처리, timeout 처리, existsLatestSnapshot double-check 흐름을 구현한다.
- REFACTOR: fallback service는 점수 계산이나 상세 DTO 조립을 직접 하지 않고 snapshot 조회와 refresh orchestration만 담당한다.
- 기대 결과: 홈 조회 경로에서 refresh 중복, 장시간 대기, 전체 API 실패가 발생하지 않는다.
- 파일 경로:
-
Task 5.4: 홈 응원 크리에이터 조회에 fallback 연결
- 파일 경로:
- Modify:
src/main/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/HomeRecommendationQueryService.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/recommendation/application/HomeRecommendationQueryServiceTest.kt
- Modify:
- RED:
findCheerCreatorRecommendations(...)가 최신 스냅샷 없음이면CHEER_CREATORfallback을 호출하고, fallback 결과 스냅샷 순서대로 상세를 조립하는 실패 테스트를 작성한다. marker가 있어existsLatestSnapshot(CHEER_CREATOR) == true이면 fallback을 호출하지 않는 테스트도 추가한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest - GREEN: 기존
findAiCharacterSnapshotsWithFallback(...)와 같은 패턴으로CHEER_CREATOR스냅샷 조회에 fallback을 연결한다. - 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 정책 확대.
-
RED: 동일 JVM에서 동시에 들어온 둘 이상의
CHEER_CREATORfallback 요청이 하나의 refresh future만 공유하고refreshCheerCreatorSnapshots(...)를 1회만 호출하는 테스트를 latch 기반으로 추가한다. -
RED: 최초 조회 뒤 lock 획득 전 다른 요청/스케줄러가 대상일 marker 또는 실제 row를 저장하면 lock 안의 double-check가 refresh를 생략하고 최신 상태를 다시 조회하는 테스트를 추가한다.
-
GREEN: 새 테스트가 구현 결함을 드러낼 때만
refreshFutures또는 lock 내부 존재 확인 경로를 최소 수정한다. -
REFACTOR/GATE: sleep 없이
RecommendationSnapshotFallbackServiceTestfocused test와git diff --check를 실행해 결과를 누적한다.
Phase 6: API 회귀와 최종 검증
-
Task 6.1: 홈 API 응답 스키마 회귀 검증
- 파일 경로:
- Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/api/home/HomeRecommendationControllerTest.kt - Test:
src/test/kotlin/kr/co/vividnext/sodalive/v2/api/home/dto/recommendation/HomeRecommendationResponseTest.kt
- Test:
- RED: 홈 통합 조회의
CHEER_CREATOR응답이 기존creatorId,creatorNickname,creatorProfileImage필드만 유지하고 신규 필드를 추가하지 않는 회귀 테스트를 확인/보강한다. - 실패 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest --tests kr.co.vividnext.sodalive.v2.api.home.dto.recommendation.HomeRecommendationResponseTest - GREEN: DTO/Controller 변경 없이 application service 결과가 기존 response로 매핑되게 한다.
- REFACTOR: 공개 API URL과 JSON field name 변경이 없음을 assertion으로 유지한다.
- 기대 결과: 클라이언트 공개 스키마는 변경되지 않는다.
- 파일 경로:
-
Task 6.2: focused regression 실행
- 파일 경로:
- Modify:
docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md
- Modify:
- RED: 구현 task 완료 후 계획 문서에 기록할 focused command 목록을 확정한다.
- 실패 확인: 해당 없음. TDD 예외 사유: 검증 기록 문서화 task다.
- GREEN: 아래 명령을 실행하고 결과를 이 문서 하단 검증 기록에 누적한다.
- REFACTOR: 실패한 명령이 있으면 원인과 재실행 결과를 같은 task 아래에 기록한다.
- 실행 명령:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationScorePolicyTest./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest./gradlew test --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest --tests kr.co.vividnext.sodalive.v2.api.home.dto.recommendation.HomeRecommendationResponseTest
- 기대 결과: 산식/집계/marker/fallback/API 회귀가 최소 명령으로 검증된다.
- 파일 경로:
-
Task 6.3: 전체 회귀와 문서 검증
- 파일 경로:
- Modify:
docs/20260710_메인_홈_추천_응원크리에이터_스냅샷/plan-task.md
- Modify:
- RED: 구현 완료 후 전체 회귀 명령 실행 전에는 검증 기록이 구현 전 상태여야 한다.
- 실패 확인: 해당 없음. TDD 예외 사유: 검증 기록 문서화 task다.
- GREEN:
./gradlew ktlintCheck,./gradlew test,./gradlew tasks --all,git diff --check를 실행하고 결과를 검증 기록에 남긴다. - 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 변경, 다른 홈 섹션 스키마 정리.
-
RED:
cheerCreators에 item을 넣고creatorId,creatorNickname,creatorProfileImage값과 필드 수 3을 검증하는 직렬화 테스트를 추가한다. -
RED: 홈 API 테스트에서 비어 있지 않은
cheerCreatorsitem의 동일 필드 계약을 검증하고 예상 밖 필드가 없음을 확인한다. -
GREEN: 기존 DTO가 테스트를 만족하면 프로덕션 코드는 변경하지 않고, 계약 불일치가 드러날 때만 기존 공개 스키마로 최소 복구한다.
-
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일 때만 본인과 활성 팔로우를 제외하는 상세 조회 계약. -
RED:
DefaultHomeRecommendationQueryRepositoryTest에 조회자 크리에이터, 활성 팔로우 크리에이터, 비활성 팔로우 이력만 있는 크리에이터, 관계가 없는 크리에이터를 준비한다.memberId = viewer.id로 조회했을 때 비활성 팔로우 이력 크리에이터와 관계가 없는 크리에이터만 반환하는shouldExcludeSelfAndActiveFollowedCreatorsFromCheerCreatorDetails테스트를 작성한다.memberId = null은 모든 활성 후보를 유지하는shouldKeepAnonymousCheerCreatorDetailsWithoutMemberFilters테스트도 작성한다. -
RED 확인:
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest를 실행해 현재 조회가 조회자 본인과 활성 팔로우 크리에이터를 함께 반환하여 첫 번째 테스트가 의도한 assertion 실패를 내는지 확인한다. -
GREEN:
findCheerCreatorRecommendationDetails(...)에memberId가 있을 때member.id != memberId를 적용하고, 동일 회원과 후보 크리에이터 사이에CreatorFollowing.isActive == true인 row가 존재하지 않는 조건을 추가한다.memberId == null이면 두 조건은 적용하지 않는다. -
GREEN 확인: 같은 repository focused test를 재실행해 본인·활성 팔로우 제외, 비활성 팔로우·비회원 유지, 기존 양방향 차단 제외 테스트가 모두 통과하는지 확인한다.
-
REFACTOR: 이번 조건에 필요한 QueryDSL alias/helper만 남기고,
HomeRecommendationQueryServiceTest로 기존 16명 후보 조회·필터 후 최대 8명 조립이 유지되는지 확인한다.HomeRecommendationControllerTest와ktlintCheck로 공개 스키마·포맷 회귀를 확인하고 실제 명령·결과를 전체 검증 기록에 남긴다.
Phase 7 완료 조건
P7-T1의 RED·GREEN·REFACTOR 체크박스와 완료 증거가 모두 충족됐다.- 인증 회원 본인·활성 팔로우만 제외되고 비활성 팔로우·비회원·양방향 차단·활성 크리에이터 정책이 조합되는 것이 검증됐다.
- 스냅샷 산식·저장 수·정렬, 응답 DTO, 다른 추천 섹션에 변경이 없다.
Phase 7 Gate
Goal 실행 P7-GATE: cheerCreators 개인화 필터와 직접 영향 회귀를 최종 판정한다.
- 시작 조건:
P7-T1완료. - 완료 증거: 아래 명령 전부 통과,
git diff --check출력 없음, 전체 검증 기록 누적. - 범위 밖: 게이트 통과를 위한 테스트 삭제·완화와 관련 없는 리팩터링.
./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, 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_CREATORempty marker 저장, 조회 제외, 존재 여부 true, marker 기반 fallback 반복 방지를 검증한다. - 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_CREATORrefresh 실패 로그, fallback single-flight·double-check, 공개 응답 3개 필드, 본인·활성 팔로우 제외가 현재 구현과 회귀 테스트에 유지됨을 확인했다. 각 결과는 기존reviews/phase-1-review.mdreviews/phase-7-review.md에 3차 리뷰로 별도 누적했다. 추가 확정 발견 사항이 없어 신규 회귀 Task/Goal은 추가하지 않았다. 사용자 지시에 따라 Gradle compile/test는 실행하지 않았다.git diff --check는 출력 없이 통과했다. 문서 명령 유효성 확인용./gradlew tasks --all은 일반 sandbox에서~/.gradlewrapper 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 판정, 실제 미완료 GoalP3-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.mdreviews/phase-7-review.md에 2차 리뷰로 각각 누적했다. 사용자 지시에 따라 Gradle compile/test는 실행하지 않았다.git diff --check는 출력 없이 통과했고,./gradlew tasks --all은 일반 sandbox에서~/.gradlewrapper 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로cheerCreatorsitem 직렬화와 홈 API 응답의creatorId,creatorNickname,creatorProfileImage3개 필드 계약을 보강했다. 최초 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에서~/.gradlewrapper 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를 실행해shouldExcludeSelfAndActiveFollowedCreatorsFromCheerCreatorDetailsassertion 실패를 확인했고, 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 7P7-T1/P7-GATE에 반영했다. 비활성 팔로우·비회원 유지와 16명 후보 밖 보충 없음을 경계로 고정했다.git diff --check는 출력 없이 통과했다../gradlew tasks --all은 일반 sandbox에서~/.gradlewrapper 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에서~/.gradlewrapper lock 파일 접근 제한으로 실패했고, 권한 상승 재실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-07-10: 구현 RED 확인으로
RecommendationScorePolicyTest는CHEER_NEW_BOOST_*와calculateCheerCreatorNewBoost(...)미구현 컴파일 실패를 확인했고,DefaultHomeRecommendationQueryRepositoryTest는 half-open/distinct 집계 기대값 불일치 실패를 확인했다.RecommendationSnapshotPersistenceAdapterTest는CHEER_CREATORempty marker 미지원 실패를 확인했다. - 2026-07-10: focused regression으로
./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.domain.RecommendationScorePolicyTest --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.RecommendationSnapshotPersistenceAdapterTest --tests kr.co.vividnext.sodalive.v2.recommendation.adapter.out.persistence.DefaultHomeRecommendationQueryRepositoryTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.HomeRecommendationQueryServiceTest --tests kr.co.vividnext.sodalive.v2.api.home.HomeRecommendationControllerTest --tests kr.co.vividnext.sodalive.v2.api.home.dto.recommendation.HomeRecommendationResponseTest를 실행해BUILD SUCCESSFUL로 통과했다. - 2026-07-10: 최종 검증으로
./gradlew ktlintCheck,./gradlew test,./gradlew tasks --all,git diff --check를 실행했다../gradlew test는 최초 300초 timeout 후 600초 timeout으로 재실행해BUILD SUCCESSFUL로 통과했고, 나머지 명령도BUILD SUCCESSFUL또는 출력 없음으로 통과했다. - 2026-07-10: 리뷰 보강으로
CHEER_CREATOR스케줄러 refresh도 section lock을 사용하도록 수정하고, 공통 fallback runner의 기본 worker를 2개 thread로 변경해 AI/응원 섹션 간 fallback 대기 간섭을 줄였다.RecommendationSnapshotFallbackServiceTest에 lock miss, refresh 실패, timeout 후 worker 지속, AI block 중 CHEER fallback 독립 실행 테스트를 추가해 Task 5.3 커버리지와 완료 표시를 맞췄다. 보강 RED 확인 후./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest재실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-07-10: 리뷰 보강 후 순차 검증으로
./gradlew ktlintCheck,./gradlew test --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotRefreshServiceTest --tests kr.co.vividnext.sodalive.v2.recommendation.application.RecommendationSnapshotFallbackServiceTest, focused recommendation/API regression 묶음,git diff --check를 실행했다. 모두BUILD SUCCESSFUL또는 출력 없음으로 통과했다. - 2026-07-10: 추가 리뷰 보강으로
CHEER_CREATOR홈/fallback 조회를 expectedsnapshotAt기준으로 변경해 stale latest snapshot이 fallback을 막지 않게 했다.RecommendationSnapshotPort.findSnapshots(...),existsSnapshot(...)와 adapter/repository exact snapshot 조회 테스트를 추가했고,DefaultHomeRecommendationQueryRepositoryTest의findCheerCreatorSnapshots(...)호출부는 두 번째 인자를windowEndExclusive로 정리했다. 비활성 legacyAiCharacterSnapshotFallbackServiceTest는 삭제하고 AI 핵심 fallback coverage를RecommendationSnapshotFallbackServiceTest로 이관했다. focused regression,./gradlew test,./gradlew ktlintCheck,git diff --check를 재실행해BUILD SUCCESSFUL또는 출력 없음으로 통과했다. - 2026-07-10:
review-work에서 발견한 blocking 이슈를 수정했다.HomeRecommendationQueryService가 expectedsnapshotAt계산에 사용한 같은nowUtc를refreshCheerCreatorIfMissing(...)에 전달하도록 변경했고,RecommendationSnapshotFallbackServiceTest의 기본 현재 시각 의존 호출을 고정nowUtc로 바꿨다.HomeRecommendationQueryServiceTest는 fallback에 전달된nowUtc를 검증한다. 재검증으로 focused recommendation/API regression,./gradlew ktlintCheck,./gradlew test,git diff --check를 실행했고 모두BUILD SUCCESSFUL또는 출력 없음으로 통과했다.