feat(home): 크리에이터 채널 홈 정책을 보강한다
This commit is contained in:
66
docs/20260612_크리에이터_채널_홈_API/reviews/phase-1-review.md
Normal file
66
docs/20260612_크리에이터_채널_홈_API/reviews/phase-1-review.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Phase 1 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 1 / Task 1.1 |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- `RecommendedActivityType`의 공용 `CreatorActivityType` 이동과 추천 기능 회귀 범위를 코드·테스트·검증 기록과 정적 대조했다.
|
||||
- 후속 패키지 정렬 문서로 승인된 경로 변경은 결함에서 제외했다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았고, 기존 성공 기록은 참고 증거로만 사용했다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `CreatorActivityType.kt`는 `LIVE`, `AUDIO`, `COMMUNITY`, `LIVE_REPLAY`와 각 name 기반 `code`를 제공한다.
|
||||
- 추천 service/port/repository와 대응 테스트의 import와 type은 공용 enum으로 정렬되어 있다.
|
||||
- `rg -n "RecommendedActivityType" src/main/kotlin src/test/kotlin`으로 이전 타입 잔존 여부를 정적 확인했다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | Phase 1 코드·테스트·문서 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
- **대상:** 1차 리뷰 후속 변경이 공용 `CreatorActivityType`과 추천 기능에 만든 영향.
|
||||
- **방법:** 현재 working tree의 import/type 사용처와 추천 회귀 테스트 코드를 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 1 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 현재 working tree의 `CreatorActivityType` 정의와 추천·크리에이터 채널 홈 사용처.
|
||||
- **방법:** enum 값·`code`, import/type 사용처, 대응 테스트와 `plan-task.md` Task 1.1을 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 1 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 공용 `CreatorActivityType` 정의와 추천·홈 스케줄의 현재 사용 경계.
|
||||
- **방법:** enum 값·`code`, 추천 service/port/repository 및 홈 domain/DTO import, 이전 `RecommendedActivityType` 잔존 여부를 Task 1.1과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 1 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
66
docs/20260612_크리에이터_채널_홈_API/reviews/phase-2-review.md
Normal file
66
docs/20260612_크리에이터_채널_홈_API/reviews/phase-2-review.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Phase 2 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 2 / Task 2.1~2.2 |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, 후속 홈 API 구조 정렬 문서, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- 홈 domain/response가 PRD의 13개 상위 섹션을 유지하는지, 순수 정책이 스케줄 제한·정렬·성인 보정·최신 오디오 중복 제거를 보장하는지 정적 검토했다.
|
||||
- 최초 오디오 판정을 repository 계층으로 이동한 후속 정렬은 해당 문서와 현재 repository 테스트를 함께 대조했다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `CreatorChannelHome.kt`, `CreatorChannelHomeResponse.kt`, `CreatorChannelHomeQueryPolicy.kt`
|
||||
- `CreatorChannelHomeQueryPolicyTest.kt`, `CreatorChannelHomeQueryServiceTest.kt`, `DefaultCreatorChannelHomeQueryRepositoryTest.kt`
|
||||
- 최신 오디오 제외와 스케줄 경계값·동시각 LIVE 우선·성인 노출 정책의 테스트 존재를 `rg`로 확인했다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | Phase 2 모델·정책·테스트 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
- **대상:** 1차 리뷰 후속 변경 이후 domain/response/policy의 정렬·제한·null/빈 목록 계약.
|
||||
- **방법:** 모델 변환과 policy 호출부, 대응 단위 테스트를 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 2 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 홈 13개 상위 섹션 domain/response와 스케줄·최신 오디오 제외 순수 정책.
|
||||
- **방법:** `CreatorChannelHome`, `CreatorChannelHomeResponse`, `CreatorChannelHomeQueryPolicy`와 service/controller 테스트를 PRD Feature A~N 및 Task 2.1~2.2와 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 2 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 홈 domain/response 13개 섹션과 스케줄 제한·동시각 정렬·성인 보정·최신 오디오 제외 정책.
|
||||
- **방법:** domain/response factory, `CreatorChannelHomeQueryPolicy`, service 호출부와 대응 단위·응답 계약 테스트를 Task 2.1~2.2와 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 2 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
88
docs/20260612_크리에이터_채널_홈_API/reviews/phase-3-review.md
Normal file
88
docs/20260612_크리에이터_채널_홈_API/reviews/phase-3-review.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Phase 3 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 3 / Task 3.1~3.16 |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, 커뮤니티 좋아요·홈 API 구조 정렬 후속 문서, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- 현재 query port/persistence adapter와 공용 커뮤니티 조회 경계가 creator, 차단, 라이브, 예약, 오디오, 후원, 시리즈, 팬 Talk, 활동, SNS 정책을 PRD와 같은 의미로 구현하는지 정적 검토했다.
|
||||
- 후속 문서로 승인된 `isOwned`/`isRented`, `isLiked`, 패키지 분리는 원 계획과의 단순 차이로 결함 판정하지 않았다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `CreatorChannelHomeQueryPort.kt`, `DefaultCreatorChannelHomeQueryRepository.kt`
|
||||
- `DefaultCreatorChannelHomeQueryRepositoryTest.kt`의 공개 시각/null, 성인, 성별·크리에이터 입장, 구매·비밀 후원, 삭제된 유료 게시물, 시리즈 경계, 데뷔일, KST 월 경계 테스트
|
||||
- 주요 쿼리의 대량 조립이 id 목록 기반 bulk 조회를 유지하고, 추가 N+1을 만들지 않는지 확인했다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
확정 발견 사항 없음.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
전환 항목 없음.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | Phase 3 코드·테스트·후속 계약 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | 후보 없음 |
|
||||
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
### 7.1 리뷰 범위와 방법
|
||||
|
||||
- 1차 리뷰 후속 변경이 반영된 홈 후원 record/domain 변환을 PRD의 “기존 채널 후원 목록과 동일” 계약과 대조했다.
|
||||
- 기존 `ChannelDonationService`와 전용 v2 후원 탭의 삭제 회원 닉네임 처리도 함께 확인했다.
|
||||
- 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
|
||||
### 7.2 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P3-001` | Low | 완료 | 홈 후원에 삭제 회원 닉네임 prefix가 노출된다 | Task 3.17 | `P3-R1` |
|
||||
|
||||
#### REV-P3-001 — 홈 후원에 삭제 회원 닉네임 prefix가 노출된다
|
||||
|
||||
- **관련 요구사항:** PRD Feature E, Task 3.5·3.15
|
||||
- **관찰:** 홈 repository는 후원자의 저장 닉네임을 그대로 projection하고, `CreatorChannelHomeQueryService.toDomain()`도 그대로 복사한다. 기존 `ChannelDonationService`와 `CreatorChannelDonationQueryService`는 `removeDeletedNicknamePrefix()`를 적용한다.
|
||||
- **영향:** 탈퇴한 후원자의 내부 저장 형식인 `deleted_...`가 홈 API에서만 공개되어 기존 채널 후원 목록과 표시 의미가 달라진다.
|
||||
- **권장 조치:** 홈 후원 domain 변환 경계에 기존 extension을 적용하고 삭제 회원 후원자 회귀 테스트를 추가한다.
|
||||
- **판정 기록:** 코드 경로 3개를 정적 대조해 확정했다.
|
||||
- **완료 기록:** 2026-07-30 — `CreatorChannelHomeQueryServiceTest` RED/GREEN으로 확인하고 service 변환 경계에 `removeDeletedNicknamePrefix()`를 적용했다.
|
||||
|
||||
### 7.3 plan·goal 전환과 종료 판정
|
||||
|
||||
- `plan-task.md` Phase 3에 Task 3.17 / `P3-R1`을 추가했다.
|
||||
- **최종 결론:** Low 1건 수정 완료.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 현재 query port/persistence adapter와 공용 커뮤니티 조회 경계의 creator, 라이브·스케줄, 오디오, 후원, 공지·커뮤니티, 시리즈, 팬 Talk, 활동, SNS 정책.
|
||||
- **방법:** projection·bulk 조립, 공개/예약·성인·차단 조건, KST 월 경계, 삭제 회원 닉네임 보정과 repository/service 테스트를 PRD 및 Task 3.1~3.17과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** `REV-P3-001` 수정 반영을 포함해 신규 확정 발견 사항 없음. Phase 3 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** query port/persistence adapter와 공용 커뮤니티 service의 creator·라이브·스케줄·오디오·후원·게시글·시리즈·팬 Talk·활동·SNS 조회 정책.
|
||||
- **방법:** projection/bulk 조회, 공개·예약·성인·성별·차단·구매·KST 월 경계 조건과 repository/service 테스트를 PRD Feature A~N 및 Task 3.1~3.17과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 기존 `REV-P3-001` 보정이 유지되며 신규 확정 발견 사항 없음. Phase 3 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
122
docs/20260612_크리에이터_채널_홈_API/reviews/phase-4-review.md
Normal file
122
docs/20260612_크리에이터_채널_홈_API/reviews/phase-4-review.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Phase 4 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 4 / Task 4.1~4.2 |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, 홈 API 구조 정렬 후속 문서, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- service의 접근 검증, 조회자 context, 섹션 조립, 최종 정책 보정을 PRD와 대조했다.
|
||||
- `CreatorChannelHomeQueryService.kt`, `MemberContentPreferenceService.kt`, 대응 service 테스트를 포함했다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `CreatorChannelHomeQueryService.kt:70-71`은 `getStoredPreference(viewer)` 후 `canViewAdultContent(viewer)`를 이어서 호출한다.
|
||||
- `MemberContentPreferenceService.kt:144-157`에서 `canViewAdultContent`ub294 `getStoredPreference(member).isAdult`를 다시 호출한다.
|
||||
- 두 호출은 같은 요청에서 같은 `ViewerContentPreference`를 사용할 수 있으며, `getStoredPreference`는 `REQUIRES_NEW` transaction이다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P4-001` | Low | 완료 | 홈 조회가 콘텐츠 선호를 같은 요청에서 두 번 조회한다 | Task 4.3 | `P4-R1` |
|
||||
|
||||
### REV-P4-001 — 홈 조회가 콘텐츠 선호를 같은 요청에서 두 번 조회한다
|
||||
|
||||
- **심각도:** Low
|
||||
- **상태:** 확정
|
||||
- **관련 요구사항:** Phase 4 조회자 성인 노출·콘텐츠 타입 context 조립
|
||||
- **소유 Task:** Task 4.3 / `P4-R1`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
service가 이미 받은 `preference.isAdult`를 사용할 수 있지만 `canViewAdultContent(viewer)`를 다시 호출한다. 실제 bean에서는 이 메서드가 `getStoredPreference` 전체 경로를 반복한다.
|
||||
|
||||
**영향**
|
||||
|
||||
공개 응답은 바뀌지 않지만, 홈 조회마다 독립 transaction·선호·국가 context 조회가 중복된다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
`preference.isAdult`를 성인 필터에 재사용하고, service 테스트에서 별도 `canViewAdultContent` 호출이 없음을 고정한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — 두 method의 정적 호출 관계와 transaction annotation으로 확정.
|
||||
- 2026-07-30 — `CreatorChannelHomeQueryServiceTest` RED/GREEN으로 `preference.isAdult` 재사용과 `canViewAdultContent(viewer)` 미호출을 검증하고 완료.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 4에 Task 4.3 / `P4-R1`을 추가했다.
|
||||
- 실행 objective: `REV-P4-001`을 수정하고 service 조립 회귀를 방지한다.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | service·선호 service·테스트 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P4-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 4.3 / `P4-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 완료.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
### 7.1 리뷰 범위와 방법
|
||||
|
||||
- service/facade의 조회자 context 조립과 기본 `now` 생성 지점을 기존 라이브 목록, 홈 Following, UTC 응답·KST 월 경계 계약과 대조했다.
|
||||
- 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
|
||||
### 7.2 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P4-002` | Medium | 완료 | 다른 크리에이터가 조회하면 입장 제한이 적용되지 않는다 | Task 4.4 | `P4-R2` |
|
||||
| `REV-P4-003` | Medium | 완료 | 홈 기본 시각이 JVM timezone에 따라 달라진다 | Task 4.5 | `P4-R3` |
|
||||
|
||||
#### REV-P4-002 — 다른 크리에이터가 조회하면 입장 제한이 적용되지 않는다
|
||||
|
||||
- **관련 요구사항:** PRD Feature B/F의 기존 라이브 목록 동일 정책, Task 4.1
|
||||
- **관찰:** service는 `isViewerCreator = viewerId == creatorId`로 계산한다. 반면 기존 라이브 목록과 `HomeFollowingQueryService`는 조회자의 `MemberRole.CREATOR` 여부를 사용하며, repository는 이 값이 true일 때 `isAvailableJoinCreator` 또는 자기 라이브 예외를 적용한다.
|
||||
- **영향:** 크리에이터가 다른 크리에이터 채널을 볼 때 `isAvailableJoinCreator == false`인 현재/예약 라이브가 홈에 노출될 수 있다.
|
||||
- **권장 조치:** 조회자의 role로 flag를 산출하고 다른 크리에이터 조회 회귀 테스트를 추가한다.
|
||||
- **완료 기록:** 2026-07-30 — 다른 크리에이터/일반 회원/대상 본인 focused test로 `viewer.role == MemberRole.CREATOR` 계산을 검증했다.
|
||||
|
||||
#### REV-P4-003 — 홈 기본 시각이 JVM timezone에 따라 달라진다
|
||||
|
||||
- **관련 요구사항:** UTC 시간 계약, KST 기준 이번 달 후원, Task 4.1·6.4
|
||||
- **관찰:** facade와 service의 기본값은 `LocalDateTime.now()`인 반면 repository는 입력 `now`를 UTC로 간주해 KST 월 경계로 변환하고 DTO도 UTC offset을 붙인다. JVM timezone을 고정하는 설정은 확인되지 않았고, 인접 `HomeFollowingQueryService`는 UTC clock을 명시한다.
|
||||
- **영향:** JVM이 UTC가 아니면 공개/예약 콘텐츠 경계와 이번 달 후원 범위가 timezone offset만큼 이동하고, 응답 문자열도 실제 instant와 다를 수 있다. 현재 E2E fixture도 같은 `LocalDateTime.now()`를 사용해 이 조건을 드러내지 못한다.
|
||||
- **권장 조치:** 인자 없는 조회의 `now`를 UTC로 생성하고 비 UTC JVM timezone 회귀 테스트를 추가한다.
|
||||
- **완료 기록:** 2026-07-30 — facade 기본 호출을 `Asia/Seoul` JVM timezone에서 검증하고 facade/service 기본 `now`를 UTC로 고정했다.
|
||||
|
||||
### 7.3 plan·goal 전환과 종료 판정
|
||||
|
||||
- `plan-task.md` Phase 4에 Task 4.4 / `P4-R2`, Task 4.5 / `P4-R3`을 추가했다.
|
||||
- **최종 결론:** Medium 2건 수정 완료.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 접근 검증 순서, 콘텐츠 선호 단일 조회, 조회자 role/effective gender 전달, facade·service 기본 UTC 시각과 전체 섹션 조립.
|
||||
- **방법:** `CreatorChannelHomeQueryService`, `CreatorChannelHomeFacade`와 대응 service/facade 테스트를 Task 4.1~4.5 및 기존 라이브 목록 정책과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** `REV-P4-001`~`REV-P4-003` 수정 반영을 포함해 신규 확정 발견 사항 없음. Phase 4 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 접근 예외 순서, 콘텐츠 선호 단일 조회, 조회자 role/effective gender, UTC `now` 전달과 전체 섹션 조립.
|
||||
- **방법:** query service/facade의 실제 호출 흐름과 service/facade 테스트를 기존 라이브 목록 정책 및 Task 4.1~4.5와 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 기존 `REV-P4-001`~`REV-P4-003` 보정이 유지되며 신규 확정 발견 사항 없음. Phase 4 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
92
docs/20260612_크리에이터_채널_홈_API/reviews/phase-5-review.md
Normal file
92
docs/20260612_크리에이터_채널_홈_API/reviews/phase-5-review.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Phase 5 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 5 / Task 5.1~5.2 |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, 홈 API 구조 정렬 후속 문서, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- endpoint·인증·`ApiResponse`·DTO mapping·JSON 필드 계약을 controller/DTO 테스트와 정적 대조했다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `CreatorChannelHomeControllerTest.kt:90-148`은 facade가 반환한 모든 섹션이 채워진 응답의 최상위·boolean·비노출 필드를 검증한다.
|
||||
- `plan-task.md` Task 5.2 REFACTOR와 주의사항은 단건이 없으면 `null`, 목록이 없으면 빈 배열을 내려주도록 명시한다.
|
||||
- 현재 controller 테스트에는 모든 nullable/목록 섹션이 빈 fixture의 JSON 직렬화 assertion이 없다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P5-001` | Low | 완료 | 빈 홈 응답의 null/빈 배열 계약이 테스트로 고정되지 않았다 | Task 5.3 | `P5-R1` |
|
||||
|
||||
### REV-P5-001 — 빈 홈 응답의 null/빈 배열 계약이 테스트로 고정되지 않았다
|
||||
|
||||
- **심각도:** Low
|
||||
- **상태:** 확정
|
||||
- **관련 요구사항:** Task 5.2 REFACTOR, 구현 중 주의사항의 빈 섹션 계약
|
||||
- **소유 Task:** Task 5.3 / `P5-R1`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
DTO 타입은 nullability와 `List` 구조를 갖추고 있지만, 실제 Jackson 응답에서 null 필드가 존재하고 목록이 `[]`로 나가는지 검증하는 빈 응답 시나리오가 없다.
|
||||
|
||||
**영향**
|
||||
|
||||
현재 실행 결함을 확인한 것은 아니지만, Jackson 설정·DTO annotation·mapping 변경 시 클라이언트의 빈 화면 계약이 회귀해도 감지하지 못한다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
MockMvc에 최소 빈 fixture 한 개를 추가해 `currentLive`, `latestAudioContent`, `fanTalk.latestFanTalk`과 목록 6개의 JSON 값을 명시적으로 고정한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — 계획의 명시적 계약과 현재 controller 테스트 범위를 대조해 확정.
|
||||
- 2026-07-30 — `CreatorChannelHomeControllerTest`에 빈 홈 응답 null/빈 배열 JSON 계약 테스트를 추가하고 focused test 통과로 완료.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 5에 Task 5.3 / `P5-R1`을 추가했다.
|
||||
- RED가 바로 통과하면 생산 DTO는 변경하지 않고 회귀 테스트만 남기도록 범위를 제한했다.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | controller·DTO·테스트 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P5-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 5.3 / `P5-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 완료.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
- **대상:** 빈 홈 응답 보강 이후 controller 인증·endpoint·JSON 표면 계약.
|
||||
- **방법:** controller, response factory, 채워진/빈 응답 MockMvc assertion을 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 5 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 인증 controller, facade 응답 변환, Boolean 필드명, 내부 필드 비노출과 null/빈 배열 JSON 계약.
|
||||
- **방법:** controller/response DTO와 채워진·빈 응답 MockMvc assertion을 Task 5.1~5.3과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 신규 확정 발견 사항 없음. Phase 5 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 인증 endpoint, `ApiResponse.ok(...)`, facade 응답 변환, Boolean 이름과 null/빈 배열 JSON 계약.
|
||||
- **방법:** controller/response DTO, 채워진·빈 응답 MockMvc assertion과 구조 정렬 후 공용 오디오 응답 필드를 Task 5.1~5.3과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 기존 `REV-P5-001` 보정이 유지되며 신규 확정 발견 사항 없음. Phase 5 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
94
docs/20260612_크리에이터_채널_홈_API/reviews/phase-6-review.md
Normal file
94
docs/20260612_크리에이터_채널_홈_API/reviews/phase-6-review.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Phase 6 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 6 / Task 6.1~6.3 |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, 홈 API 구조 정렬 후속 문서, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- Task 6.1이 요구한 “PRD의 홈 전체 섹션이 한 요청에서 조립” 증거와 실제 테스트 계층/빈 경로를 대조했다.
|
||||
- 추천 enum 회귀와 기존 검증 기록의 존재도 정적 확인했다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `CreatorChannelHomeControllerTest.kt:46-52`는 `@WebMvcTest`와 mock `CreatorChannelHomeFacade`를 사용한다.
|
||||
- `DefaultCreatorChannelHomeQueryRepositoryTest.kt:178-275`의 `shouldFindCreatorChannelHomeIntegratedSections`는 동일 fixture로 repository method를 각각 직접 호출한다.
|
||||
- 해당 repository 통합 시나리오는 후속 구조 정렬에서 공용 커뮤니티 service로 이동한 notices/communities를 포함하지 않는다.
|
||||
- `CreatorChannelHomeEndToEndTest` 또는 같은 역할의 실제 홈 endpoint 통합 테스트는 존재하지 않는다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P6-001` | Low | 완료 | 한 HTTP 요청의 홈 전체 조립을 증명하는 통합 테스트가 없다 | Task 6.4 | `P6-R1` |
|
||||
|
||||
### REV-P6-001 — 한 HTTP 요청의 홈 전체 조립을 증명하는 통합 테스트가 없다
|
||||
|
||||
- **심각도:** Low
|
||||
- **상태:** 확정
|
||||
- **관련 요구사항:** Task 6.1 기대 결과
|
||||
- **소유 Task:** Task 6.4 / `P6-R1`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
현재 증거는 “mock facade가 만든 domain을 controller가 JSON으로 변환”하는 테스트와 “한 fixture에서 repository method를 각각 호출”하는 테스트로 분리되어 있다. 따라서 controller→facade→service→repository/공용 서비스→JSON이 한 요청에서 연결되는지는 직접 증명되지 않는다.
|
||||
|
||||
**영향**
|
||||
|
||||
개별 계층 테스트가 통과해도 bean wiring, facade의 커뮤니티 조회 호출, 시간/context 전달, 전체 mapping 중 누락을 한 번에 검출하지 못한다. 이는 실행 결함 확정이 아니라 Task 6.1 완료 증거의 공백이다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
실제 Spring bean과 DB fixture를 사용해 인증된 단일 홈 요청을 보내고, 13개 상위 섹션의 대표 필드를 검증하는 통합 테스트를 추가한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — 테스트 annotation, mock 경계, repository 호출 범위, E2E 파일 부재를 정적 대조해 확정.
|
||||
- 2026-07-30 — `CreatorChannelHomeEndToEndTest`를 추가해 실제 bean 단일 HTTP 요청으로 전체 대표 섹션 JSON을 검증하고 완료.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 6에 Task 6.4 / `P6-R1`을 추가했다.
|
||||
- 통합 테스트가 바로 통과하면 생산 코드는 변경하지 않도록 범위를 제한했다.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | controller·repository 통합 증거 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P6-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 6.4 / `P6-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 완료.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
- **대상:** 신규 `CreatorChannelHomeEndToEndTest`가 실제 bean 경로와 13개 섹션의 대표 응답을 연결하는지 확인했다.
|
||||
- **방법:** fixture 생성 시각, HTTP 호출, JSON assertion과 기존 focused test 경계를 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** Phase 6 자체의 신규 확정 발견 사항은 없다. E2E가 JVM 기본 timezone과 같은 `LocalDateTime.now()`를 사용해 UTC 오류를 가릴 수 있는 점은 원인 소유 Phase 4의 `REV-P4-003` / Task 4.5로 전환했으며 중복 Task를 만들지 않았다.
|
||||
- **남은 항목:** Phase 6 없음. Task 4.5 완료 후 E2E 직접 영향만 재확인한다.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 실제 controller→facade→service→repository/공용 community 경로의 단일 HTTP 요청 통합 증거와 추천 enum 회귀 범위.
|
||||
- **방법:** `CreatorChannelHomeEndToEndTest`의 UTC fixture·대표 13개 섹션 assertion, repository/service/controller 테스트 경계와 Task 6.1~6.4를 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** `REV-P6-001`과 Phase 4 UTC 후속 수정 반영을 포함해 신규 확정 발견 사항 없음. Phase 6 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 단일 인증 HTTP 요청의 실제 bean 경로, UTC fixture, 홈 13개 섹션 대표 assertion과 추천 enum 회귀 증거.
|
||||
- **방법:** E2E·repository·service·controller 테스트의 계층 경계와 현재 생산 코드 wiring을 Task 6.1~6.4와 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 기존 `REV-P6-001` 및 UTC 보정이 유지되며 신규 확정 발견 사항 없음. Phase 6 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
172
docs/20260612_크리에이터_채널_홈_API/reviews/phase-7-review.md
Normal file
172
docs/20260612_크리에이터_채널_홈_API/reviews/phase-7-review.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Phase 7 코드 리뷰 보고서
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 7 / Task 7.1~7.2 / P7-GATE |
|
||||
| 기준 commit 또는 working tree | `f1c2e6c5` + 2026-07-30 working tree |
|
||||
| 리뷰 일자 | 2026-07-30 |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `prd.md`, `plan-task.md`, `docs/agent-guides/*.md` |
|
||||
| 리뷰 상태 | 판정 완료 |
|
||||
|
||||
## 2. 목적과 범위
|
||||
|
||||
- 활성 고정 9개 상한, 10번째 고정의 최고령 교체, 비활성 재활성화, 홈 오디오 고정 우선 정렬, 최신 오디오 제외을 코드·테스트·문서와 정적 대조했다.
|
||||
- 사용자 지시에 따라 컴파일·테스트는 실행하지 않았고, Phase 7의 기존 Gate 성공 기록은 참고 증거로만 사용했다.
|
||||
|
||||
## 3. 검토 근거
|
||||
|
||||
- `AudioContentService.kt:1230-1261`은 transaction 안에서 현재 고정→활성 목록 순으로 읽고 추가·재활성화하지만 크리에이터 단위 lock을 취하지 않는다.
|
||||
- `PinContent.kt:11-19`에는 `(member_id, content_id)` unique constraint가 없고, `PinContentRepository.kt:15-43`의 조회에도 pessimistic lock이 없다.
|
||||
- `MemberRepository.kt:35-37`에는 이미 회원 행을 잠그는 `findByIdForUpdate` 패턴이 있다.
|
||||
- `AudioContentServiceTest.kt:454-530`은 8/9개, 10번째 교체, 비활성 재활성화를 순차 mock 요청으로 검증하며 동시 요청은 다루지 않는다.
|
||||
|
||||
## 4. 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P7-001` | Medium | 완료 | 동시 고정 요청이 활성 9개 상한을 넘거나 중복 행을 만들 수 있다 | Task 7.3 | `P7-R1` |
|
||||
|
||||
### REV-P7-001 — 동시 고정 요청이 활성 9개 상한을 넘거나 중복 행을 만들 수 있다
|
||||
|
||||
- **심각도:** Medium
|
||||
- **상태:** 확정
|
||||
- **관련 요구사항:** PRD Feature D/H, `DEC-001`, Task 7.1
|
||||
- **소유 Task:** Task 7.3 / `P7-R1`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
활성 고정이 8개일 때 같은 크리에이터의 두 transaction이 동시에 목록을 읽으면 둘 다 `size < 9`로 판단해 서로 다른 행을 추가할 수 있다. 같은 콘텐츠에 대한 두 요청도 둘 다 `findByContentIdAndMemberId == null`을 관찰한 후 중복 행을 추가할 수 있다.
|
||||
|
||||
**정적 재현 절차**
|
||||
|
||||
1. 활성 고정 8개를 준비한다.
|
||||
2. transaction A/B가 각각 서로 다른 콘텐츠에 대해 기존 고정이 없음과 활성 목록 8개를 읽는다.
|
||||
3. A/B가 각각 새 `PinContent`를 저장하면 최종 활성 수는 10개가 된다.
|
||||
4. 현재 코드·lock·constraint 중 이 interleaving을 차단하는 장치가 없다.
|
||||
|
||||
**영향**
|
||||
|
||||
동시 요청이라는 제한 조건에서 크리에이터별 활성 고정 9개 계약이 깨지고, 홈 정렬 join에 중복 행이 생기면 응답 개수·순서도 오염될 수 있다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
고정 상태를 읽기 전 크리에이터 행을 pessimistic write lock으로 직렬화하고, 새 DDL 없이 동시성 통합 테스트로 9개 상한과 콘텐츠 유일성을 고정한다. 홈 repository 테스트에는 9개 초과 fixture에서 반환 개수가 9임을 명시적으로 추가한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-30 — transaction 내 읽기-판단-쓰기 순서, lock 부재, unique constraint 부재를 정적 대조해 확정.
|
||||
- 2026-07-30 — `AudioContentService.pinToTheTop`에 creator member row lock을 추가하고 `AudioContentPinConcurrencyTest`/`AudioContentServiceTest` 통과로 완료. Reviewer gate에서 요구한 홈 목록 9개 상한 assertion은 `DefaultCreatorChannelHomeQueryRepositoryTest`에 추가해 통과 확인.
|
||||
|
||||
## 5. plan·goal 전환
|
||||
|
||||
- `plan-task.md` Phase 7에 Task 7.3 / `P7-R1`을 추가했다.
|
||||
- 기존 Task 7.1~7.2과 P7-GATE의 완료 이력은 유지하고 리뷰 후속 goal만 추가했다.
|
||||
|
||||
## 6. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | 고정 service·entity·repository·테스트 정적 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P7-001` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | Task 7.3 / `P7-R1` |
|
||||
| 검증 명령과 결과 기록 | 충족 | 정적 검토만 실행, 테스트 미실행 사유 기록 |
|
||||
|
||||
**최종 결론:** 수정 goal 완료.
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 7. 2차 리뷰 — 2026-07-30
|
||||
|
||||
### 7.1 리뷰 범위와 방법
|
||||
|
||||
- Task 7.3의 member row lock 호출 순서와 동시성 테스트가 MySQL 운영 격리수준에서도 직렬화를 보장하는지 정적 검토했다.
|
||||
- MySQL 공식 InnoDB `REPEATABLE READ`의 consistent read/locking read 의미를 근거로 대조했다.
|
||||
- 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
|
||||
### 7.2 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P7-002` | Medium | 완료 | 크리에이터 lock 전에 일반 조회가 snapshot을 만들 수 있다 | Task 7.4 | `P7-R2` |
|
||||
|
||||
#### REV-P7-002 — 크리에이터 lock 전에 일반 조회가 snapshot을 만들 수 있다
|
||||
|
||||
- **관련 요구사항:** PRD Feature D/H, Task 7.3
|
||||
- **관찰:** `pinToTheTop`은 `repository.findByIdAndCreatorId` 일반 조회 후 `memberRepository.findByIdForUpdate`를 호출하고, 그 뒤 `PinContent`를 일반 조회한다. MySQL InnoDB 기본 `REPEATABLE READ`에서는 첫 consistent read가 snapshot을 정하고 locking read는 최신 행을 읽으므로 두 방식을 섞으면 후속 일반 조회가 lock 대기 전 snapshot을 계속 사용할 수 있다. [MySQL 8.0 Reference Manual](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html)
|
||||
- **영향:** 두 번째 transaction이 member lock을 기다린 뒤에도 첫 transaction의 최신 고정 변경을 보지 못해 9개 상한이나 콘텐츠 중복 방지 판단이 stale 상태를 기준으로 수행될 수 있다.
|
||||
- **검증 공백:** 현재 단위 테스트는 member lock이 `PinContent` 조회보다 빠른지만 확인하고 콘텐츠 일반 조회는 순서 검증에 포함하지 않는다. 동시성 테스트의 시작 latch도 두 transaction의 고정 목록 읽기 시점을 강제하지 않아 이 interleaving을 보장하지 않는다.
|
||||
- **권장 조치:** transaction의 첫 DB 접근에서 member row lock을 잡고 모든 일반 조회를 그 뒤로 옮기며, 호출 순서를 단위 테스트로 고정한다.
|
||||
- **완료 기록:** 2026-07-30 — `AudioContentServiceTest`의 `inOrder` RED/GREEN으로 lock이 콘텐츠/고정 조회보다 먼저 호출됨을 고정했다.
|
||||
|
||||
### 7.3 plan·goal 전환과 종료 판정
|
||||
|
||||
- `plan-task.md` Phase 7에 Task 7.4 / `P7-R2`를 추가했다.
|
||||
- **최종 결론:** Medium 1건 수정 완료.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 8. 3차 리뷰 — 2026-07-31
|
||||
|
||||
### 8.1 리뷰 범위와 방법
|
||||
|
||||
- Task 7.3~7.4 후속 구현이 같은 크리에이터의 모든 상단 고정 변경을 실제로 직렬화하는지 `pinToTheTop`, `unpinAtTheTop`, member lock, `PinContent` 행 재사용 흐름을 정적 검토했다.
|
||||
- 기존 단위·동시성 테스트가 고정과 해제의 경쟁을 포함하는지도 함께 확인했다.
|
||||
- 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
|
||||
### 8.2 발견 사항
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P7-003` | Medium | 수정 완료 | 고정 해제가 creator lock을 공유하지 않아 새 고정을 소실할 수 있다 | Task 7.5 | `P7-R3` |
|
||||
|
||||
#### REV-P7-003 — 고정 해제가 creator lock을 공유하지 않아 새 고정을 소실할 수 있다
|
||||
|
||||
- **심각도:** Medium
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** PRD Feature H, Task 7.1·7.3·7.4
|
||||
- **소유 Task:** Task 7.5 / `P7-R3`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
`pinToTheTop`은 첫 DB 접근에서 `MemberRepository.findByIdForUpdate`로 크리에이터를 잠근 뒤, 활성 고정이 9개이면 가장 오래된 `PinContent` 행의 `content`를 새 콘텐츠로 바꿔 재사용한다. 반면 `unpinAtTheTop`은 같은 member lock 없이 이전 콘텐츠로 `PinContent`를 조회하고 `isActive = false`로 변경한다.
|
||||
|
||||
**정적 재현 절차**
|
||||
|
||||
1. 활성 고정 9개에서 가장 오래된 고정 콘텐츠를 A, 새 고정 콘텐츠를 B로 둔다.
|
||||
2. 해제 transaction이 A의 `PinContent`를 먼저 읽은 뒤 commit 전 대기한다.
|
||||
3. 고정 transaction이 member lock을 얻고 같은 행을 B의 활성 고정으로 재사용해 commit한다.
|
||||
4. 해제 transaction이 늦게 commit하면 이전 A를 가리키던 stale entity update가 재사용된 행을 다시 비활성화하거나 이전 상태로 덮을 수 있다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `AudioContentService.pinToTheTop`은 member lock과 최고령 `PinContent` 행 재사용을 수행한다.
|
||||
- 코드: `AudioContentService.unpinAtTheTop`은 member lock 없이 `PinContent`를 조회·비활성화한다.
|
||||
- 테스트: `AudioContentPinConcurrencyTest`는 고정 요청 2개의 경쟁만 검증하고 고정/해제 경쟁은 다루지 않는다.
|
||||
|
||||
**영향**
|
||||
|
||||
겹친 두 요청의 순서에 따라 성공한 새 고정 B가 홈 `audioContents`에서 사라지거나 재사용 행 상태가 요청 완료 순서와 다르게 남을 수 있다. 활성 9개 상한 자체를 초과하지는 않지만 상단 고정 상태의 일관성이 깨진다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
`unpinAtTheTop`도 첫 DB 접근에서 `pinToTheTop`과 같은 member row lock을 획득한 뒤 해제 대상을 조회한다. 최소 회귀 테스트로 member lock이 `PinContent` 조회보다 먼저 호출되는 순서를 고정하고 기존 동시 고정·10번째 교체 테스트를 함께 유지한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- 2026-07-31 — 고정 행 재사용, 해제의 lock 부재, 현재 동시성 테스트 범위를 정적 대조해 확정했다.
|
||||
- 2026-07-31 — `unpinAtTheTop`의 creator lock 선행 획득을 RED/GREEN으로 보정하고, Phase 7 직접 영향 단위 테스트와 `ktlintCheck`, `git diff --check` 통과를 확인했다.
|
||||
|
||||
### 8.3 plan·goal 전환과 종료 판정
|
||||
|
||||
- `plan-task.md` Phase 7에 Task 7.5 / `P7-R3`을 추가했다.
|
||||
- **최종 결론:** Medium 1건 수정 완료.
|
||||
- **남은 항목:** 없음.
|
||||
|
||||
## 9. 4차 리뷰 — 2026-07-31
|
||||
|
||||
- **대상:** 활성 고정 9개 상한, 10번째·비활성 재고정, 홈 고정 우선 정렬, 최신 오디오 제외와 고정/해제 creator lock 직렬화.
|
||||
- **방법:** `AudioContentService`, member pessimistic lock, `PinContent` 조회 순서, 홈 repository 정렬과 단위·동시성·repository 테스트를 Task 7.1~7.5 및 기존 `REV-P7-001`~`REV-P7-003`과 정적 대조했다. 사용자 지시에 따라 테스트·컴파일·ktlint은 실행하지 않았다.
|
||||
- **결과:** 기존 동시성 보정이 유지되며 신규 확정 발견 사항 없음. Phase 7 후속 Task를 추가하지 않는다.
|
||||
- **남은 항목:** 없음.
|
||||
Reference in New Issue
Block a user