feat(home): 팔로잉 탭 최근 소식 정책을 보강한다
This commit is contained in:
59
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-1-review.md
Normal file
59
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-1-review.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Phase 1 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 1 / Task 1.1~1.2 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- DTO/domain 모델, 비로그인 응답, controller, Security `permitAll`이 공개 계약과 일치하는지 정적으로 대조했다.
|
||||
- `HomeFollowingTabResponse.kt`, `HomeFollowingController.kt`, `HomeFollowingFacade.kt`, `SecurityConfig.kt`와 대응 테스트를 포함했다.
|
||||
- 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다. `plan-task.md`의 기존 성공 기록은 참고 증거로만 사용했다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `HomeFollowingTabResponse.loginRequired()`는 로그인 필요 상태와 다섯 개 빈 배열을 생성한다.
|
||||
- controller는 nullable 인증 회원을 facade에 전달하고 `ApiResponse.ok(...)`로 감싼다.
|
||||
- `SecurityConfig`는 `GET /api/v2/home/following`을 `permitAll`로 허용한다.
|
||||
- DTO 테스트와 controller 테스트는 비회원/인증 회원 계약 및 nested 최근 소식 변환을 다룬다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | Phase 1 코드·테스트·문서 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유와 기존 기록 분리 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- controller, facade, DTO, Security permitAll과 비로그인 빈 응답 계약을 현재 working tree 기준으로 다시 대조했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 1 완료 판정을 유지한다.
|
||||
|
||||
## 8. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- 공개 endpoint, nullable 인증 principal, `ApiResponse.ok(...)`, 로그인 필요 빈 응답과 nested DTO 변환을 현재 working tree에서 다시 대조했다.
|
||||
- controller·facade·DTO 테스트가 비회원/인증 회원 분기와 공개 응답 계약을 고정하는지 정적으로 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 1 완료 판정을 유지한다.
|
||||
59
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-2-review.md
Normal file
59
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-2-review.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Phase 2 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 2 / Task 2.1~2.2 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, `create-home-following-news-inbox-table.sql` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- inbox entity/JPA repository/adapter와 MySQL DDL의 컬럼·유니크 키·인덱스·비활성화 정책을 정적으로 대조했다.
|
||||
- 중복 충돌 retry, 활성 follower 조회, 테스트 격리와 기존 통합 테스트 범위를 확인했다.
|
||||
- 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- entity와 DDL은 `member_id/news_type/source_key` 유니크 정책, timestamp, 길이, 활성 상태 컬럼이 일치한다.
|
||||
- adapter는 입력 중복 제거 후 기존 수신 회원을 일괄 조회하고 `saveAll`/`flush`하며, unique 충돌 시 새 트랜잭션으로 한 번 재시도한다.
|
||||
- 언팔로우 비활성화 쿼리와 활성 follower 조회 쿼리는 계획의 키 조건을 사용한다.
|
||||
- 통합 테스트는 실제 unique 충돌 후 트랜잭션 사용 가능 여부, 비활성화, 활성 follower 조회를 포함한다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | entity/repository/adapter/DDL/test 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- inbox DDL, Entity, JPA adapter의 중복 방지·retry·비활성화 계약을 현재 working tree 기준으로 다시 대조했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 2 완료 판정을 유지한다.
|
||||
|
||||
## 8. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- DDL·Entity의 컬럼 길이, timestamp, unique/index 정책과 adapter의 중복 제거·충돌 재시도·비활성화 동작을 다시 대조했다.
|
||||
- 활성 follower 조회의 잠금이 Phase 4 팔로우 상태 변경 경계와 연결되고 관련 persistence 테스트가 이를 고정하는지 정적으로 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 2 완료 판정을 유지한다.
|
||||
120
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-3-review.md
Normal file
120
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-3-review.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Phase 3 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 3 / Task 3.1~3.6 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md` Feature B~F, `plan-task.md` Phase 3 |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- 팔로잉 크리에이터, On Air, 월간 스케줄, 최근 소식 repository와 query service를 기존 접근 정책까지 포함해 정적으로 대조했다.
|
||||
- `DefaultHomeFollowingQueryRepository.kt`, `HomeFollowingQueryPort.kt`, `HomeFollowingQueryService.kt`와 대응 테스트를 검토했다.
|
||||
- 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 발견 사항 요약
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P3-001` | High | 확정 | On Air와 라이브 스케줄이 기존 라이브 입장 제한을 적용하지 않는다 | Task 3.7 | `P3-R1` |
|
||||
|
||||
## 4. 발견 사항 상세
|
||||
|
||||
### REV-P3-001 — On Air와 라이브 스케줄이 기존 라이브 입장 제한을 적용하지 않는다
|
||||
|
||||
- **심각도:** High
|
||||
- **상태:** 확정
|
||||
- **관련 요구사항:** PRD Feature C의 성별·크리에이터 입장 제한, Feature E의 기존 채널 스케줄 정책 재사용
|
||||
- **소유 Task:** 신규 Task 3.7 / `P3-R1`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
`HomeFollowingQueryService`는 회원 id와 성인 콘텐츠 허용 여부만 port에 전달한다.
|
||||
`DefaultHomeFollowingQueryRepository.findOnAirLives(...)`와 live schedule 조회는 활성/채널/성인/차단 조건만 적용하며
|
||||
`live_room.gender_restriction`과 크리에이터 회원의 `is_available_join_creator` 조건을 적용하지 않는다.
|
||||
|
||||
반면 기존 `LiveRoomQueryRepositoryImpl.getLiveRoomListNow(...)`와
|
||||
`DefaultCreatorChannelHomeQueryRepository.findCurrentLive/findSchedules(...)`는 effective gender와 크리에이터 입장 제한을 적용한다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `HomeFollowingQueryService.kt:19-30`
|
||||
- 코드: `DefaultHomeFollowingQueryRepository.kt:70-99`, `242-273`
|
||||
- 비교 코드: `LiveRoomRepository.kt:97-115`
|
||||
- 비교 코드: `DefaultCreatorChannelHomeQueryRepository.kt:136-145`, `225-233`
|
||||
- 테스트 공백: `DefaultHomeFollowingQueryRepositoryTest`의 On Air 테스트는 활성/성인/정렬만 검증한다.
|
||||
|
||||
**정적 재현 절차**
|
||||
|
||||
1. 남성 회원이 여성 전용 라이브를 연 팔로잉 크리에이터를 조회한다고 가정한다.
|
||||
2. 라이브를 `isActive=true`, non-empty `channelName`, 비성인으로 두면 현재 팔로잉 조회 조건을 모두 통과한다.
|
||||
3. 실제 라이브 입장 정책은 성별 불일치로 입장을 거부하지만 팔로잉 탭 On Air에는 노출된다.
|
||||
4. 크리에이터 회원이 `isAvailableJoinCreator=false`인 타 크리에이터 라이브를 조회하는 경우도 동일하게 노출된다.
|
||||
|
||||
**영향**
|
||||
|
||||
팔로잉 탭에 터치해도 입장할 수 없는 라이브 또는 스케줄이 노출되어 기존 라이브 접근 정책과 API 결과가 불일치한다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
회원의 본인인증 성별을 우선한 effective gender와 크리에이터 회원 여부를 query service에서 전달하고, 기존 QueryDSL 조건을
|
||||
On Air와 live schedule에 최소 적용한다. 성별 불일치와 크리에이터 입장 불가 회귀 테스트를 먼저 추가한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — PRD와 기존 라이브 조회 구현을 대조해 확정했다. 런타임 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 3에 Task 3.7, `P3-R1`, `P3-R-GATE`를 추가했다.
|
||||
- 기존 Task 3.1~3.6의 완료 체크와 검증 기록은 유지했다.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | Phase 3 repository/service/test 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P3-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 3.7 / `P3-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 필요.
|
||||
|
||||
**남은 항목:** `P3-R1` 구현 후 `P3-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"` 실행 결과 effective gender·creator 여부 port 인자 미반영 컴파일 오류로 `BUILD FAILED`.
|
||||
- 2026-07-30 — `HomeFollowingQueryService`에서 본인인증 성별 우선 effective gender와 크리에이터 회원 여부를 `HomeFollowingQueryPort`에 전달하고, On Air와 라이브 스케줄 QueryDSL에 `genderRestriction` 및 `isAvailableJoinCreator` 조건을 적용했다.
|
||||
- 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
|
||||
|
||||
- `HomeFollowingQueryService`가 본인인증 성별 우선 effective gender와 크리에이터 여부를 On Air·스케줄 조회에 전달하는 현재 코드를 정적으로 재확인했다.
|
||||
- `DefaultHomeFollowingQueryRepository`가 On Air와 라이브 스케줄에 `genderRestriction` 및 `isAvailableJoinCreator` 조건을 적용하는 현재 코드를 정적으로 재확인했다.
|
||||
- 이번 후속 판정에서는 컴파일과 테스트를 재실행하지 않았고, 위 `## 7. 수정 후 검증 기록`의 기존 성공 결과를 근거로 삼았다.
|
||||
|
||||
**최종 결론:** `P3-R1` 및 `P3-R-GATE` 수정 검증 완료.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 9. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- 팔로잉 크리에이터, On Air, 월간 스케줄, 최근 소식 조회 조건과 `P3-R1`의 성별·크리에이터 입장 제한 반영을 현재 working tree에서 다시 추적했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 3 완료 판정을 유지한다.
|
||||
|
||||
## 10. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- 팔로잉 크리에이터·On Air·월간 스케줄·최근 소식의 활성/role/차단/성인/시간 범위/정렬/limit 조건을 PRD와 다시 대조했다.
|
||||
- `P3-R1`의 effective gender와 크리에이터 입장 제한이 service→port→On Air·라이브 스케줄 query에 동일하게 전달되는지 확인했다.
|
||||
- repository/service 테스트가 KST 월간 경계, 동률 정렬, 원천 target 활성 상태와 라이브 접근 제한을 고정하는지 정적으로 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 3 완료 판정을 유지한다.
|
||||
203
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-4-review.md
Normal file
203
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-4-review.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Phase 4 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 4 / Task 4.1~4.5 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md` Feature F, `plan-task.md` Phase 4 |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- source key, follower fan-out, 언팔로우 비활성화, 랭킹/오디오/무료 커뮤니티 발행 연결을 정적으로 추적했다.
|
||||
- 원 트랜잭션 commit 이후 발행, 발행 실패 격리, 공개 시각과 중복 방지 키를 확인했다.
|
||||
- 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- source key는 타입과 원천 id/기간으로 안정적으로 생성된다.
|
||||
- publish service는 활성 follower에게만 record를 만들고 DB 컬럼 길이에 맞춰 title/body를 제한한다.
|
||||
- 언팔로우는 기존 inbox를 비활성화하며 재팔로우가 이를 복구하지 않는다.
|
||||
- 랭킹, 즉시/예약 오디오, 무료 커뮤니티 생성 경로는 commit 이후 publish service를 호출하고 실패를 원 처리와 격리한다.
|
||||
- 관련 단위·서비스 테스트는 발행 성공, 예약 공개, 유료 미발행, 발행 실패 격리를 포함한다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | 발행 경로와 호출부 정적 추적 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
### 리뷰 범위
|
||||
|
||||
- active follower 조회, inbox insert의 트랜잭션 경계와 언팔로우·재팔로우 상태 변경을 함께 정적으로 추적했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
|
||||
### REV-P4-001 — follower 판정 후 언팔로우가 완료되면 stale active inbox가 생성될 수 있음
|
||||
|
||||
**심각도:** Medium
|
||||
|
||||
**상태:** 확정
|
||||
|
||||
**근거**
|
||||
|
||||
- `HomeFollowingNewsPublishService.publishToFollowers(...)`는 active follower id 목록을 먼저 읽고 이후 별도 호출로 inbox row를 insert한다.
|
||||
- `HomeFollowingNewsInboxJpaRepository.findActiveFollowerIds(...)`는 `creator_following`을 잠그지 않는 조회다.
|
||||
- `HomeFollowingNewsInboxPersistenceAdapter.insertIgnoreAll(...)`의 실제 insert는 `REQUIRES_NEW` 경로에서 실행될 수 있어 follower 조회와 하나의 직렬화 경계를 공유하지 않는다.
|
||||
- `MemberService.creatorUnFollow(...)`는 언팔로우 시점에 존재하는 active inbox만 비활성화하고, `creatorFollow(...)`의 재팔로우는 기존 관계 row를 다시 active로 바꾼다.
|
||||
|
||||
**정적 재현 절차**
|
||||
|
||||
1. publish 트랜잭션 A가 active follower인 회원을 읽고 insert 전에 멈춘다.
|
||||
2. 트랜잭션 B가 같은 회원의 언팔로우와 현재 inbox 비활성화를 완료한다.
|
||||
3. A가 기존 follower snapshot으로 active inbox를 뒤늦게 insert한다.
|
||||
4. 언팔로우 중에는 조회의 active-following 조건으로 숨겨지지만, 회원이 다시 팔로우하면 해당 언팔로우 이전 이벤트가 노출 가능해진다.
|
||||
|
||||
**영향**
|
||||
|
||||
재팔로우 시 기존 비활성 row를 복구하지 않고 재팔로우 이후의 새 이벤트만 제공한다는 정책을 우회해, 언팔로우 이전 이벤트가 최근 소식으로 나타날 수 있다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
follower 판정부터 inbox insert까지를 하나의 트랜잭션 경계로 묶고, 해당 `creator_following` row를 팔로우·언팔로우 상태 변경과 같은 순서로 잠근다. publish가 먼저 끝나면 뒤이은 언팔로우가 새 row까지 비활성화하고, 언팔로우가 먼저 끝나면 publish가 row를 생성하지 않는 두 순서를 동시성 테스트로 고정한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — publish/follow/unfollow 호출 흐름과 트랜잭션 경계를 대조해 확정했다. 런타임 테스트는 실행하지 않았다.
|
||||
|
||||
### plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 4에 Task 4.6, `P4-R1`, `P4-R-GATE`를 추가했다.
|
||||
- 기존 Task 4.1~4.5의 완료 체크와 검증 기록은 유지했다.
|
||||
|
||||
### 2차 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | publish/follow/unfollow 트랜잭션 경계 정적 추적 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P4-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 4.6 / `P4-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 필요.
|
||||
|
||||
**남은 항목:** 없음. `P4-R1`과 `P4-R-GATE` 수정 후 검증은 아래 기록에 누적했다.
|
||||
|
||||
## 8. 수정 후 검증 — 2026-07-30
|
||||
|
||||
- RED 확인: `HomeFollowingNewsInboxPersistenceAdapterTest.shouldNotExposeNewsCreatedBeforeUnfollowAfterRefollowWhenPublishIsDelayed` 실행 결과 stale active inbox assertion 실패로 `BUILD FAILED`.
|
||||
- follower 조회와 inbox insert 경계에서 `creator_following` row를 잠그도록 active follower 조회에 `for update`를 적용했고, `creatorFollow(...)`/`creatorUnFollow(...)`도 같은 row를 `PESSIMISTIC_WRITE`로 조회하도록 변경했다.
|
||||
- GREEN 확인: 동시성 단일 테스트 재실행 결과 `BUILD SUCCESSFUL`.
|
||||
- P4-R1 focused test `HomeFollowingNewsInboxPersistenceAdapterTest`, `HomeFollowingNewsPublishServiceTest`, `MemberServiceTest` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- P4-R-GATE E2E `HomeFollowingEndToEndTest` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- P4-R-GATE lint `ktlintCheck` 최초 실행은 테스트 import 정렬 위반으로 `BUILD FAILED`; import 정렬 수정 후 재실행한다.
|
||||
- P4-R-GATE lint `ktlintCheck` 재실행 결과 `BUILD SUCCESSFUL`.
|
||||
- 리뷰 게이트에서 Critical/Important/Minor blocker 없음으로 승인됐다.
|
||||
|
||||
**수정 후 결론:** `REV-P4-001` 보완 완료.
|
||||
|
||||
## 9. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
### 리뷰 범위
|
||||
|
||||
- 공개 팔로우·언팔로우 controller 경로, `MemberService` 상태 전이, inbox 비활성화와 재팔로우 조회 결과를 함께 정적으로 추적했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
|
||||
### REV-P4-002 — 통합 팔로우 API의 `isActive=false` 경로가 기존 inbox를 비활성화하지 않음
|
||||
|
||||
**심각도:** High
|
||||
|
||||
**상태:** 확정
|
||||
|
||||
**관련 요구사항:** PRD Feature F의 언팔로우 시 기존 inbox 비활성화, 재팔로우 시 기존 inbox 미복구
|
||||
|
||||
**소유 Task:** Task 4.7 / `P4-R2`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
`POST /member/creator/follow`는 request의 `isActive=false`를 `MemberService.creatorFollow(...)`에 전달해 팔로우·언팔로우를 함께 처리한다. 관계 row가 없으면 전달된 `isActive`를 반영하지 않은 기본 active 관계를 만들고, 기존 관계 row가 있으면 `creatorFollowing.isActive=false`만 반영한 채 inbox 비활성화를 호출하지 않는다. 이후 같은 API로 다시 활성화하면 기존 inbox row가 계속 active이므로 active-following 조회 조건을 다시 만족해 언팔로우 이전 소식이 노출될 수 있다.
|
||||
|
||||
**근거**
|
||||
|
||||
- `MemberController.creatorFollow(...)`는 nullable `request.isActive`를 기본값과 함께 service에 전달한다.
|
||||
- `CreatorFollowRequest`는 `isActive`를 공개 request 필드로 정의한다.
|
||||
- `MemberService.creatorFollow(...)`의 신규 관계 분기는 기본값이 `isActive=true`인 `CreatorFollowing()`을 저장하고, 기존 관계 분기는 `isNotify`와 `isActive`만 변경한다.
|
||||
- `MemberService.creatorUnFollow(...)`만 `homeFollowingNewsInboxPort.deactivateByMemberIdAndCreatorId(...)`를 호출한다.
|
||||
- `MemberServiceTest`는 전용 `creatorUnFollow(...)` 경로만 검증하며 `creatorFollow(..., isActive=false)` 경로는 검증하지 않는다.
|
||||
|
||||
**정적 재현 절차**
|
||||
|
||||
1. 관계가 없는 회원이 `POST /member/creator/follow`에 `isActive=false`를 보내면 기본 active 관계가 새로 생성된다.
|
||||
2. 별도로 active 팔로우와 active inbox row가 있는 회원이 같은 요청을 보내면 관계 row만 inactive가 되고 inbox는 active로 남는다.
|
||||
3. 같은 API에 `isActive=true`를 보내 재팔로우한다.
|
||||
4. 기존 active inbox가 다시 active-following 조건을 만족해 최근 소식 조회 후보가 된다.
|
||||
|
||||
**영향**
|
||||
|
||||
관계가 없는 회원은 언팔로우 요청으로 오히려 active follower가 될 수 있다. 기존 follower는 언팔로우 경로에 따라 과거 소식 보존 상태가 달라지고, 통합 언팔로우 경로에서 재팔로우 이후 과거 소식 미복구 계약을 위반한다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
관계가 없고 `isActive=false`면 전용 언팔로우 경로와 동일하게 새 active 관계를 만들지 않는다. 기존 관계를 `isActive=false`로 반영하는 같은 트랜잭션에서는 inbox를 비활성화한다. `isActive=true`인 알림 변경·재팔로우는 기존 비활성 inbox를 복구하지 않도록 유지하고, 두 경우를 `MemberServiceTest`로 고정한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — request/controller/service의 공개 `isActive=false` 흐름과 PRD 재팔로우 정책을 정적으로 대조해 확정했다. 런타임 테스트는 실행하지 않았다.
|
||||
|
||||
### plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 4에 Task 4.7, `P4-R2`, `P4-R2-GATE`를 추가했다.
|
||||
- 기존 Task 4.1~4.6의 완료 체크와 검증 기록은 유지했다.
|
||||
|
||||
### 3차 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | 두 공개 상태 변경 경로와 조회 조건 정적 추적 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P4-002` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 4.7 / `P4-R2` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유와 정적 근거 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 필요.
|
||||
|
||||
**남은 항목:** 없음. `P4-R2`와 `P4-R2-GATE` 수정 후 검증은 아래 기록에 누적했다.
|
||||
|
||||
## 10. 3차 수정 후 검증 — 2026-07-30
|
||||
|
||||
- RED 확인: `./gradlew --no-daemon test --tests "kr.co.vividnext.sodalive.member.MemberServiceTest"` 실행 결과 `creatorFollow(..., isActive=false)` 신규 회귀 2건이 assertion 실패로 `BUILD FAILED`.
|
||||
- 관계가 없고 `isActive=false`인 통합 팔로우 요청은 새 active 관계를 만들지 않고, 기존 관계를 inactive로 바꾸는 경로에서는 `homeFollowingNewsInboxPort.deactivateByMemberIdAndCreatorId(...)`를 호출하도록 `MemberService.creatorFollow(...)`만 최소 수정했다.
|
||||
- GREEN 확인: 같은 `MemberServiceTest` 명령 재실행 결과 `BUILD SUCCESSFUL`.
|
||||
- P4-R2-GATE 확인: `./gradlew --no-daemon test --tests "kr.co.vividnext.sodalive.v2.home.following.adapter.out.persistence.HomeFollowingNewsInboxPersistenceAdapterTest" --tests "kr.co.vividnext.sodalive.v2.home.following.application.HomeFollowingNewsPublishServiceTest"` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- P4-R2-GATE lint `./gradlew --no-daemon ktlintCheck` 최초 실행은 신규 테스트 줄 길이 위반으로 `BUILD FAILED`; 포맷 정리 후 `MemberServiceTest`와 `ktlintCheck` 재실행 결과 모두 `BUILD SUCCESSFUL`.
|
||||
- 추가 전체 회귀 확인: `./gradlew --no-daemon test` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- 리뷰 게이트에서 Critical/Important/Minor blocker 없음으로 승인됐다.
|
||||
|
||||
**수정 후 결론:** `REV-P4-002` 보완 완료.
|
||||
|
||||
## 11. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- source key, after-commit 발행, follower fan-out, 랭킹·오디오·무료 커뮤니티 호출부와 발행 실패 격리를 다시 추적했다.
|
||||
- `P4-R1`의 `creator_following` 잠금 순서와 `P4-R2`의 통합 `isActive=false` 경로가 전용 언팔로우와 같은 inbox 최종 상태를 만드는지 확인했다.
|
||||
- persistence·publish·`MemberService` 테스트가 중복 방지, 동시 publish/unfollow, 신규 inactive 요청과 재팔로우 미복구를 고정하는지 정적으로 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. `REV-P4-001`, `REV-P4-002` 수정 완료와 기존 Phase 4 완료 판정을 유지한다.
|
||||
58
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-5-review.md
Normal file
58
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-5-review.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Phase 5 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 5 / Task 5.1~5.2 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md` Feature A·D, `plan-task.md` Phase 5 |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- facade의 비회원 단락, 도메인 조회와 기존 최근 대화 10개 조립, E2E API 표면을 정적으로 대조했다.
|
||||
- `HomeFollowingFacade.kt`, facade/controller/E2E 테스트를 검토했다.
|
||||
- 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- 비회원은 query/chat service 호출 없이 로그인 필요 응답을 받는다.
|
||||
- 로그인 회원은 `ChatRoomListService.getRooms(member, "ALL", null, 10)` 결과를 도메인 조회 결과에 조립한다.
|
||||
- E2E 테스트는 비회원 빈 섹션과 로그인 회원의 다섯 섹션, 최근 소식 JSON surface를 검증한다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | facade/controller/E2E 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- facade의 로그인 분기, 최근 대화 재사용, E2E 조립 범위를 현재 working tree 기준으로 다시 대조했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 5 완료 판정을 유지한다.
|
||||
|
||||
## 8. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- 비회원 조기 반환, 로그인 회원의 도메인 조회와 `ChatRoomListService.getRooms(..., limit = 10)` 조립을 다시 대조했다.
|
||||
- controller/facade/E2E 테스트가 비회원의 조회 생략과 로그인 회원의 다섯 섹션 공개 응답을 고정하는지 정적으로 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 5 완료 판정을 유지한다.
|
||||
60
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-5.5-review.md
Normal file
60
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-5.5-review.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Phase 5.5 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 5.5 / Task 5.5.1~5.5.5 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md` Feature F·G, `plan-task.md` Phase 5.5 |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- 타입별 nullable nested DTO, 원천 target enrichment, 무료 커뮤니티 전용 발행과 E2E JSON 계약을 정적으로 대조했다.
|
||||
- DTO/domain/repository/커뮤니티 호출부와 대응 테스트를 포함했다.
|
||||
- 사용자의 지시에 따라 컴파일과 테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- 최근 소식 최상위는 공통 필드와 다섯 nested 필드만 노출하고 flat 이동/표시 필드를 제거했다.
|
||||
- repository는 랭킹, 활성 오디오, 활성 무료 커뮤니티 원천을 타입별 payload로 조립한다.
|
||||
- 커뮤니티 좋아요와 최상위 댓글은 active row만 집계한다.
|
||||
- 유료 커뮤니티는 발행과 조회 양쪽에서 제외된다.
|
||||
- DTO 및 E2E 테스트는 nested payload의 상호 배타적 null 계약과 제거된 flat 필드를 검증한다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | DTO/repository/call site/test 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- 타입별 nullable nested DTO, 원천 target enrichment, 무료 커뮤니티 발행 제한과 E2E JSON 계약을 현재 working tree 기준으로 다시 대조했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 5.5 완료 판정을 유지한다.
|
||||
|
||||
## 8. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- 최근 소식 공통 필드와 타입별 상호 배타적 nested DTO, CDN/UTC 변환, 원천 오디오·무료 커뮤니티 enrichment를 다시 대조했다.
|
||||
- 유료 커뮤니티와 비활성·성인 원천 target 제외, active like·top-level comment 집계가 repository와 E2E 테스트에 연결되는지 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. 기존 Phase 5.5 완료 판정을 유지한다.
|
||||
185
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-6-review.md
Normal file
185
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-6-review.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Phase 6 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 6 / Task 6.1~6.2 |
|
||||
| 기준 commit 또는 working tree | `e6f56f24` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, DDL, 기존 검증 기록 |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- PRD, 구현 계획, DDL, 실제 공개 DTO의 현재 계약과 Phase별 기존 검증 기록을 정적으로 대조했다.
|
||||
- 현재 compile/test 통과 상태는 사용자 설명과 `plan-task.md` 기존 기록을 근거로 삼았으며 직접 재실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- PRD와 계획의 endpoint, 섹션 limit, nested 최근 소식 필드, 무료 커뮤니티 정책, DDL 유니크 키가 현재 코드와 일치한다.
|
||||
- `plan-task.md`에는 Phase 1~7의 focused/회귀/lint 성공 기록과 실패 후 보완 이력이 누적되어 있다.
|
||||
- 이번 리뷰에서 확정된 후속 결함은 기존 완료 상태를 되돌리지 않고 소유 Phase의 신규 Task로 추가했다.
|
||||
- `./gradlew tasks --all` 최초 실행은 sandbox의 사용자 Gradle cache 접근 제한으로 실패했고, 승인된 권한으로 재실행한 결과
|
||||
`BUILD SUCCESSFUL`이었다. task 목록만 확인했으며 컴파일과 테스트는 실행하지 않았다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음. Phase 3과 Phase 7의 코드 발견 사항은 각 Phase 보고서와 Task에 귀속했다.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | PRD/plan/DDL/code/기존 기록 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | Phase 6 자체 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 다른 Phase 소유 항목은 해당 Phase에 반영 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 테스트 미실행 사유와 기존 기록 분리 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음. Phase 3·7 회귀 Goal과 Phase 6 후속 문서 검증 기록은 이후 섹션에 누적했다.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
### 기존 후속 항목 확인
|
||||
|
||||
- Phase 3의 `P3-R1`과 Phase 7의 `P7-R1`·`P7-R2` 구현 및 기존 Gate 성공 기록이 누적된 것을 확인했다.
|
||||
- 이번 확인에서는 컴파일과 테스트를 재실행하지 않았다.
|
||||
|
||||
### REV-P6-001 — PRD의 `newsId` 형식이 현재 공개 동작과 불일치
|
||||
|
||||
**심각도:** Low
|
||||
|
||||
**상태:** 확정
|
||||
|
||||
**근거**
|
||||
|
||||
- PRD 공통 식별자 설명은 `scheduleId`와 `newsId` 모두 `{TYPE}:{targetId}` 형식을 기본안으로 적고 있다.
|
||||
- `DefaultHomeFollowingQueryRepository`는 `home_following_news_inbox.id`를 문자열로 변환해 `newsId`로 반환한다.
|
||||
- repository test와 E2E도 inbox PK의 10진 문자열을 계약으로 검증한다.
|
||||
- 최근 소식 정렬과 `CONTENT_RANKING` 동률 해소도 inbox `id`를 사용하므로, 현재 동작을 유지한 문서 수정이 가장 작은 정합성 보완이다.
|
||||
|
||||
**영향**
|
||||
|
||||
클라이언트 또는 후속 구현자가 PRD만 보면 `newsId`를 타입·target 기반 식별자로 해석할 수 있어 실제 응답 파싱, 정렬 의미, 동일 target의 여러 소식 식별을 잘못 구현할 수 있다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
runtime 코드와 공개 응답 값은 변경하지 않는다. PRD에서 `scheduleId`는 `{TYPE}:{targetId}`, `newsId`는 `home_following_news_inbox.id`의 10진 문자열이며 같은 노출 시각의 정렬·동률 해소에 쓰인다고 분리해 명시한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — PRD, repository, repository test, E2E를 정적으로 대조해 확정했다. Gradle 명령은 실행하지 않았다.
|
||||
|
||||
### plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 6에 문서 전용 Task 6.3, `P6-R1`, `P6-R-GATE`를 추가했다.
|
||||
- 기존 Task 6.1~6.2의 완료 체크와 검증 기록은 유지했다.
|
||||
|
||||
### 2차 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | PRD/repository/test/E2E 식별자 계약 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P6-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 6.3 / `P6-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | Gradle 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 문서 수정 goal 필요.
|
||||
|
||||
**남은 항목:** 없음. `P6-R1`과 `P6-R-GATE` 수정 후 검증은 아래 기록에 누적했다.
|
||||
|
||||
## 8. 수정 후 검증 — 2026-07-30
|
||||
|
||||
- PRD에서 `scheduleId`와 `newsId` 식별자 설명을 분리했다.
|
||||
- `scheduleId`는 `{TYPE}:{targetId}` 형식, `newsId`는 `home_following_news_inbox.id`의 10진 문자열이며 `visibleFromAtUtc desc`, `newsId desc` 정렬과 `CONTENT_RANKING` 동률 해소에 사용한다고 명시했다.
|
||||
- runtime 코드, 테스트, DDL, 공개 응답 필드는 변경하지 않았다.
|
||||
- 정적 검색 `rg -n "newsId|scheduleId|home_following_news_inbox\.id" ...` 실행 결과 PRD와 repository/test/E2E의 식별자 계약이 일치함을 확인했다.
|
||||
- `git diff --check` 실행 결과 오류 없음.
|
||||
- `./gradlew tasks --all` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- 리뷰 게이트에서 Critical/Important/Minor blocker 없음으로 승인됐다.
|
||||
|
||||
**수정 후 결론:** `REV-P6-001` 문서 보완 완료.
|
||||
|
||||
## 9. 3차 정적 리뷰 — 2026-07-30
|
||||
|
||||
### 리뷰 범위
|
||||
|
||||
- PRD의 최근 소식 최신 배치 정책, Phase 7 회귀 Task, repository 조건과 회귀 테스트를 정적으로 대조했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
|
||||
### REV-P6-002 — PRD의 최신 크리에이터 랭킹 배치 판정 기준이 현재 구현과 불일치
|
||||
|
||||
**심각도:** Low
|
||||
|
||||
**상태:** 확정
|
||||
|
||||
**관련 요구사항:** 최신 공개 `WEEKLY` 배치만 조회하고 빈 최신 배치에서 과거 순위를 보충하지 않는 정책
|
||||
|
||||
**소유 Task:** Task 6.4 / `P6-R2`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
PRD Feature F와 기술 제약은 최신 `CREATOR_RANKING` 배치를 `creator_ranking_snapshot`의 최대 공개 시각으로 판정한다고 기술한다. 그러나 `REV-P7-001` 보완 이후 현재 repository는 적용 가능한 `creator_ranking_snapshot_job`의 최신 `WEEKLY`, `DONE`, `visibleFromAtUtc <= nowUtc` 시각을 우선 사용하고, 해당 job이 전혀 없을 때만 snapshot으로 fallback한다.
|
||||
|
||||
**근거**
|
||||
|
||||
- PRD의 최신 배치 설명 두 곳은 `creator_ranking_snapshot`만 판정 원천으로 명시한다.
|
||||
- `plan-task.md` Task 7.2는 빈 최신 완료 배치를 식별하기 위해 job 우선·legacy snapshot 제한 fallback을 확정했다.
|
||||
- `DefaultHomeFollowingQueryRepository.latestVisibleCreatorRankingBatchCondition(...)`은 최신 DONE job 시각을 우선하고 `hasRankingJob.not()`일 때만 snapshot 시각을 사용한다.
|
||||
- `DefaultHomeFollowingQueryRepositoryTest.shouldExcludeCreatorRankingNewsWhenLatestDoneBatchIsEmpty`는 최신 DONE job의 snapshot row가 0건이어도 과거 소식을 반환하지 않는 현재 계약을 검증한다.
|
||||
|
||||
**영향**
|
||||
|
||||
후속 구현자가 PRD만 따르면 최신 빈 완료 배치에서 과거 snapshot을 다시 최신으로 판정해 이미 수정한 회귀를 재도입할 수 있다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
runtime은 변경하지 않는다. PRD 두 곳을 최신 공개 DONE job 우선으로 동기화하고, 적용 가능한 DONE job이 전혀 없는 legacy/backfill 데이터에서만 snapshot fallback을 허용하며 빈 DONE 배치는 과거 snapshot으로 보충하지 않는다고 명시한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — PRD, Task 7.2, repository, 빈 최신 배치 회귀 테스트를 정적으로 대조해 확정했다. Gradle 명령은 실행하지 않았다.
|
||||
|
||||
### plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 6에 문서 전용 Task 6.4, `P6-R2`, `P6-R2-GATE`를 추가했다.
|
||||
- 기존 Task 6.1~6.3의 완료 체크와 검증 기록은 유지했다.
|
||||
|
||||
### 3차 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | PRD/Task/repository/test 최신 배치 기준 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P6-002` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 6.4 / `P6-R2` |
|
||||
| 검증 명령과 결과 기록 | 충족 | Gradle 미실행 사유와 정적 근거 기록 |
|
||||
|
||||
**최종 결론:** 문서 수정 goal 필요.
|
||||
|
||||
**남은 항목:** 없음. `P6-R2`와 `P6-R2-GATE` 수정 후 검증은 아래 기록에 누적했다.
|
||||
|
||||
## 10. 3차 수정 후 검증 — 2026-07-30
|
||||
|
||||
- PRD Feature F와 최근 소식 Inbox 기술 제약의 `CREATOR_RANKING` 최신 공개 배치 설명을 현재 구현의 `creator_ranking_snapshot_job` 최신 `WEEKLY`, `DONE`, `visibleFromAtUtc <= nowUtc` 우선 기준과 일치하도록 갱신했다.
|
||||
- 적용 가능한 완료 job이 전혀 없는 legacy/backfill 데이터에서만 `creator_ranking_snapshot` fallback을 허용하고, 최신 완료 job의 결과가 0건이면 과거 snapshot으로 보충하지 않는다고 명시했다.
|
||||
- P6-R2-GATE 정적 검색 `rg -n "creator_ranking_snapshot_job|creator_ranking_snapshot|최신 공개 배치|WEEKLY|DONE|legacy|fallback" ...` 실행 결과 PRD, Task 7.2, repository, 빈 최신 배치 회귀 테스트의 기준을 대조했다. 검색 결과의 과거 Task 7.1 snapshot 설명은 Task 7.2에서 superseded된 완료 기록으로 확인했다.
|
||||
- P6-R2-GATE `git diff --check` 실행 결과 오류 없음.
|
||||
- P6-R2-GATE `./gradlew tasks --all` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- 추가 전체 회귀 확인: `./gradlew --no-daemon test` 실행 결과 `BUILD SUCCESSFUL`.
|
||||
- 리뷰 게이트에서 Critical/Important/Minor blocker 없음으로 승인됐다.
|
||||
|
||||
**수정 후 결론:** `REV-P6-002` 문서 보완 완료.
|
||||
|
||||
## 11. 4차 정적 리뷰 — 2026-07-30
|
||||
|
||||
- PRD·plan·DDL·공개 DTO와 현재 repository의 endpoint, limit, 식별자, latest ranking batch, nested 최근 소식 계약을 다시 대조했다.
|
||||
- `newsId`의 inbox PK 문자열 계약과 최신 `WEEKLY`, `DONE` job 우선·legacy snapshot 제한 fallback 설명이 현재 코드·테스트와 일치함을 확인했다.
|
||||
- 컴파일과 테스트는 사용자 지시에 따라 실행하지 않았다.
|
||||
- 신규 확정 발견 사항 없음. `REV-P6-001`, `REV-P6-002` 문서 보완 완료와 기존 Phase 6 완료 판정을 유지한다.
|
||||
162
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-7-review.md
Normal file
162
docs/20260625_메인_홈_팔로잉_탭_API/reviews/phase-7-review.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 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_job`에 `WEEKLY`, `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_RANKING`의 `rank 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-R1` → `P7-R2` → `P7-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-R2` 및 `P7-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 완료 판정을 유지한다.
|
||||
Reference in New Issue
Block a user