Files

9.2 KiB

Phase 7 코드 리뷰 보고서

1. 리뷰 정보

항목 내용
리뷰 대상 Phase 7 / Task 7.1
기준 commit 또는 working tree e6f56f24 + 2026-07-30 working tree
리뷰 일자 2026-07-30
리뷰어 Codex
기준 문서 prd.md Feature F·Decision Log, plan-task.md Phase 7
리뷰 상태 판정 완료

2. 목적과 범위

  • 최신 공개 크리에이터 랭킹 배치 필터와 콘텐츠별 최신 랭킹 중복 제거가 모든 확정 edge case에서 limit 전에 적용되는지 검토했다.
  • 현재 working tree의 repository, repository test, E2E fixture 변경을 포함했다.
  • 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다. 기존 Phase 7 성공 기록은 참고 증거로만 사용했다.

3. 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-P7-001 High 확정 최신 완료 배치가 0건이면 과거 크리에이터 랭킹이 다시 최신으로 판정된다 Task 7.2 P7-R1
REV-P7-002 High 확정 노출 불가능한 최신 콘텐츠 랭킹 row가 이전의 노출 가능한 row를 가린다 Task 7.3 P7-R2

4. 발견 사항 상세

REV-P7-001 — 최신 완료 배치가 0건이면 과거 크리에이터 랭킹이 다시 최신으로 판정된다

  • 심각도: High
  • 상태: 확정
  • 관련 요구사항: 최신 공개 배치만 노출, 최신 배치에 없는 크리에이터의 과거 순위 미보충
  • 소유 Task: 신규 Task 7.2 / P7-R1

관찰 내용

최신 배치 조건은 creator_ranking_snapshot.visibleFromAtUtc.max()만 조회한다. 최신 집계가 성공했지만 점수 조건을 통과한 snapshot이 0건이면 해당 배치를 나타내는 snapshot row가 없으므로 max 값은 직전 배치 시각으로 남는다.

랭킹 job은 결과가 0건이어도 creator_ranking_snapshot_jobWEEKLY, DONE, visibleFromAtUtc를 남기므로 완료 배치를 식별할 근거가 이미 있지만 현재 팔로잉 조회는 이를 사용하지 않는다.

근거

  • 코드: DefaultHomeFollowingQueryRepository.kt:428-451
  • 코드: DefaultCreatorRankingSnapshotRepository.kt:51-65
  • 코드: CreatorRankingSnapshotJobService.kt:52-69
  • 테스트 공백: shouldFindOnlyLatestVisibleCreatorRankingBatchInRecentNews는 최신 배치에 snapshot이 1건 이상인 경우만 검증한다.

정적 재현 절차

  1. 직전 주간 snapshot과 같은 시각의 CREATOR_RANKING inbox를 둔다.
  2. 더 최신 주간 job을 DONE으로 완료하지만 집계 결과 snapshot은 0건으로 둔다.
  3. 최신 job 공개 시각 이후 API를 조회한다.
  4. 요구 결과는 크리에이터 랭킹 0건이지만, 현재 subquery max는 직전 snapshot 시각이므로 과거 inbox가 노출된다.

영향

활동 부족 등으로 최신 배치 결과가 비어 있는 주에 명시적으로 금지된 과거 랭킹 보충이 발생한다.

권장 조치

결과 row와 독립적으로 남는 최신 공개 DONE job을 배치 식별 기준으로 사용하고, 최신 빈 배치에서는 과거 snapshot으로 fallback하지 않는 회귀 테스트를 추가한다.

판정 기록

  • 2026-07-30 — snapshot 교체와 job 완료 흐름을 정적으로 추적해 확정했다.

REV-P7-002 — 노출 불가능한 최신 콘텐츠 랭킹 row가 이전의 노출 가능한 row를 가린다

  • 심각도: High
  • 상태: 확정
  • 관련 요구사항: 같은 콘텐츠의 노출 가능한 row 중 최신 1건 조회
  • 소유 Task: 신규 Task 7.3 / P7-R2

관찰 내용

외부 row는 CONTENT_RANKINGrank is not null, inbox 성인 조건 등으로 노출 가능 여부를 확인한다. 그러나 latestContentRankingNewsCondition(...)의 newer-row subquery는 member/type/target/active/visible/order만 확인하고 rank is not null과 회원별 성인 조건을 확인하지 않는다.

따라서 더 최신 row의 rank가 null이면 최신 row는 외부 조건에서 제외되면서도 이전 정상 row를 subquery에서 가려 결과가 0건이 된다.

근거

  • 코드: DefaultHomeFollowingQueryRepository.kt:154-166
  • 코드: DefaultHomeFollowingQueryRepository.kt:428-438
  • 코드: DefaultHomeFollowingQueryRepository.kt:454-471
  • 테스트 공백: 현재 콘텐츠 중복 테스트는 모든 newer row가 노출 가능한 경우만 검증한다.

정적 재현 절차

  1. 같은 회원·콘텐츠에 rank=2, 노출 가능 시각 09:00인 정상 row를 둔다.
  2. 같은 키에 rank=null, 노출 가능 시각 10:00인 active row를 둔다.
  3. 10:00 이후 조회하면 이전 row는 newer row 존재로 제외되고, newer row는 rank is not null 조건에서 제외된다.
  4. 요구 결과는 노출 가능한 row 중 최신인 09:00 row 1건이지만 실제 결과는 0건이다.

영향

부분 적재나 정책 변경으로 최신 inbox row가 노출 불가능해지면 유효한 이전 콘텐츠 랭킹까지 사라져 “노출 가능한 row 중 최신” 계약을 위반한다.

권장 조치

newer-row 판정에 외부 조회와 동일한 row별 노출 조건을 적용한다. 최소한 rank is not null과 회원별 inbox 성인 조건의 회귀 테스트를 추가하고 기존 시각/id tie-break 테스트를 유지한다.

판정 기록

  • 2026-07-30 — 외부 where와 correlated subquery 조건을 대조해 확정했다.

5. plan·goal 전환

  • plan-task.md Phase 7에 Task 7.2/7.3, P7-R1, P7-R2, P7-R-GATE를 추가했다.
  • 기존 Task 7.1과 P7-GATE의 완료 기록은 유지했다.

6. 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 working tree repository/test/E2E 정적 대조
후보 항목 판정 완료 충족 REV-P7-001, REV-P7-002 확정
확정 항목 plan 반영 충족 Task 7.2/7.3
검증 명령과 결과 기록 충족 테스트 미실행 사유 기록

최종 결론: 수정 goal 필요.

남은 항목: P7-R1P7-R2P7-R-GATE 실행 후 이 문서에 수정 검증을 누적한다.

7. 수정 후 검증 기록

  • 2026-07-30 — RED 확인: ./gradlew --no-daemon test --tests "kr.co.vividnext.sodalive.v2.home.following.application.HomeFollowingQueryServiceTest" --tests "kr.co.vividnext.sodalive.v2.home.following.adapter.out.persistence.DefaultHomeFollowingQueryRepositoryTest" 실행 결과 최신 빈 배치와 노출 불가 newer row 보완 전 컴파일/회귀 실패를 확인했다.
  • 2026-07-30 — CREATOR_RANKING 최신 배치 기준을 creator_ranking_snapshot_job의 최신 WEEKLY, DONE, visibleFromAtUtc <= nowUtc로 보강해 최신 완료 배치가 비어 있어도 과거 배치를 보충하지 않도록 했다.
  • 2026-07-30 — CONTENT_RANKING newer row 판정에 rank is not null과 회원별 inbox 성인 조건을 적용해 노출 불가 최신 row가 이전 노출 가능 row를 가리지 않도록 했다.
  • 2026-07-30 — GREEN 확인: 같은 focused test 명령 재실행 결과 BUILD SUCCESSFUL.
  • 2026-07-30 — Gate 확인: ./gradlew --no-daemon test --tests "kr.co.vividnext.sodalive.v2.api.home.following.adapter.in.web.HomeFollowingEndToEndTest"./gradlew --no-daemon ktlintCheck 재실행 결과 모두 BUILD SUCCESSFUL.
  • 2026-07-30 — 리뷰 게이트: Oracle reviewer가 Critical/Important blocker 없음으로 승인했다.

8. 후속 종료 판정 — 2026-07-30

  • 최신 공개 크리에이터 랭킹 배치가 빈 결과여도 과거 배치로 fallback하지 않는 현재 job 기준 조건을 정적으로 재확인했다.
  • CONTENT_RANKING newer row 판정에 rank is not null과 회원별 inbox 성인 조건이 적용된 현재 코드를 정적으로 재확인했다.
  • 이번 후속 판정에서는 컴파일과 테스트를 재실행하지 않았고, 위 ## 7. 수정 후 검증 기록의 기존 성공 결과를 근거로 삼았다.

최종 결론: P7-R1, P7-R2P7-R-GATE 수정 검증 완료.

남은 항목: 없음.

9. 3차 정적 리뷰 — 2026-07-30

  • 최신 DONE job 우선의 크리에이터 랭킹 배치 판정, legacy snapshot fallback, 노출 가능한 콘텐츠 랭킹 중복 제거를 현재 working tree에서 다시 추적했다.
  • 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
  • runtime 신규 확정 발견 사항 없음. PRD의 stale 최신 배치 설명은 문서 소유 Phase의 REV-P6-002와 Task 6.4로 전환했으며, 기존 Phase 7 완료 판정을 유지한다.

10. 4차 정적 리뷰 — 2026-07-30

  • 최신 공개 WEEKLY, DONE job 기준과 job 부재 시 snapshot fallback, 최신 빈 배치의 과거 랭킹 미보충을 다시 추적했다.
  • CONTENT_RANKING의 노출 가능한 newer row 판정에 active/rank/성인/visible/id 조건이 적용되고 전체 limit 전에 콘텐츠별 한 건만 남는지 확인했다.
  • repository 테스트가 신규 배치 공개 전후, 빈 최신 배치, rank null·성인 newer row와 동률 id 해소를 고정하는지 정적으로 확인했다.
  • 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
  • 신규 확정 발견 사항 없음. REV-P7-001, REV-P7-002 수정 완료와 기존 Phase 7 완료 판정을 유지한다.