fix(main): 추천 홈 후속 검증을 보완한다

This commit is contained in:
Yu Sung
2026-07-31 21:12:32 +09:00
parent 3ae2dc19d6
commit a8fe416cb5
38 changed files with 3161 additions and 163 deletions

View File

@@ -0,0 +1,64 @@
# Phase 1 기준 스펙과 기존 구현 점검 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 1 / Task 1.1~1.2 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `prd.md`, `plan-task.md`, 저장소 가이드 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- PRD 기준점, V2 홈/공용 컴포넌트 inventory와 제외 범위가 실제 저장소 상태와 일치하는지 확인한다.
- `SodaLive/Sources/V2/Main/Home/**`, `SodaLive/Sources/V2/Component/**`를 포함하고 이후 Phase의 기능 판정은 제외한다.
## 3. 판정 기준
- `docs/sample/sample-review.md`의 Blocker/High/Medium/Low 및 후보/확정/오탐/보류 상태를 적용했다.
- 완료 체크와 실제 파일 존재, 금지 문자열 부재, 후속 요구사항 추적 가능성을 기준으로 판정했다.
## 4. 검토한 근거와 실행한 검증
- 코드: `SodaLive/Sources/V2/Main/Home/**`, `SodaLive/Sources/V2/Component/**`
- 문서: PRD 3~4, 계획의 재분류 원칙과 Task 1.1~1.2
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `find SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Component -type f -name '*.swift'` | 성공 | 홈/공용 컴포넌트 inventory 확인 |
| `rg "추천 필모그래피\|또 다른 모습\|figma.com\|localhost:3845" SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Component` | 성공 | 검색 결과 없음 |
| `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. 이후 Phase에서 확인된 계약·라우팅 문제는 해당 Phase 리포트가 소유한다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 판정 항목 | 결과 | 근거 |
|---|---|---|
| 리뷰 범위 전체 확인 | 충족 | inventory와 제외 문자열 확인 |
| 후보 항목 판정 완료 | 충족 | 남은 후보 없음 |
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 명령과 결과 기록 | 충족 | 4절 기록 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- PRD, 계획, V2 홈·공용 컴포넌트 목록과 제외 범위를 다시 대조했다.
- Phase 1 범위에서 추가 확정 발견 사항 없음. 후속 구현 결함은 각 소유 Phase 리포트로 분리했다.

View File

@@ -0,0 +1,88 @@
# Phase 10 최근 활동 크리에이터 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 10 / Task 10.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.1~7.3, `plan-task.md` Phase 10 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 활동 크리에이터 표시 필드, 활동 문구, 상대 시간, 빈 상태 숨김을 확인한다.
- 탭 목적지 세부 회귀는 Phase 21 리포트가 소유한다.
## 3. 판정 기준
- 응답 필드 소비, unknown 문구 숨김, 빈 배열 미표시와 컴파일 성공을 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeActiveCreatorSection.swift:3-137`, `RecommendedActivityType.swift`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "activityType\|activityAt\|targetId\|RecommendedActivityType\|relativeActivityAtText" ...` | 성공 | 표시/시간/타입 연결 확인 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. LIVE/LIVE_REPLAY 이동은 Phase 21 기존 리뷰에서도 확정 발견 사항 없음으로 판정됐다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | 표시/시간/empty 확인 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 최근 활동 타입별 callback과 `targetId`·creator fallback 분기를 다시 확인했다.
- 추가 확정 발견 사항 없음. LIVE fallback 상세는 Phase 21 리포트가 소유한다.
### 3차 최종 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P10-001` | Low | 확정 | 최근 활동 상대 시간 계산이 공용 `DateParser`와 중복됨 | `Task 10.2` / `P10-R1` |
#### REV-P10-001 — 공용 상대 시간 계산 미재사용
- **관찰 내용:** `MainHomeActiveCreatorSection.swift``relativeActivityAtText(now:)``DateParser.relativeTimeText(fromUTC:fallback:now:)`와 같은 파싱, 연·월·일·시·분 분기와 I18n 반환을 별도로 구현한다.
- **영향:** 상대 시간 정책을 수정할 때 공용 경로와 추천 홈 경로가 서로 달라질 수 있고 동일 로직을 두 곳에서 유지해야 한다.
- **근거:** `MainHomeActiveCreatorSection.swift:106-137`, `DateParser.swift:19-49`의 분기와 반환값 대조.
- **재현 또는 검증:** 두 함수의 `DateParser.parse`, `Calendar.dateComponents`, `timeIntervalSince`, `I18n.Time` 사용을 줄 단위로 비교하면 동일한 계산임을 확인할 수 있다.
- **권장 조치:** 최근 활동 표시가 공용 `DateParser.relativeTimeText`를 직접 재사용하게 하고 이 섹션의 중복 구현만 제거한다.
- **계획 전환:** `plan-task.md` Phase 10에 `Task 10.2` / `P10-R1`을 추가했다.
**당시 결론:** `REV-P10-001` 수정 goal 필요
### REV-P10-001 수정 기록 — 2026-07-31
- `MainHomeActiveCreatorSection`의 최근 활동 상대 시간 표시를 `DateParser.relativeTimeText(fromUTC:fallback:)` 호출로 교체했다.
- 섹션 내부의 중복 `relativeActivityAtText(now:)` helper를 제거했다.
- `rg -n "dateComponents|timeIntervalSince|relativeTimeText|relativeActivityAtText" "SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeActiveCreatorSection.swift" "SodaLive/Sources/Common/DateParser.swift"` 실행 결과, 추천 섹션에는 공용 `DateParser.relativeTimeText` 호출만 남고 중복 helper는 검색되지 않았다.
- `git diff --check` — 성공, 출력 없음.
- `xcodebuild -quiet -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -destination "generic/platform=iOS" -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataTask184DevRetry" build` — 종료 코드 0.
**현재 결론:** `REV-P10-001` 수정 완료

View File

@@ -0,0 +1,82 @@
# Phase 11 최근 데뷔 크리에이터 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 11 / Task 11.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 11 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- `HomeCreatorItem` 표시, 빈 상태 숨김, creator callback과 현재 반응형 카드 구현을 확인한다.
## 3. 판정 기준
- 필드와 callback ID 일치, 빈 배열 미표시, 기존 카드 크기 기준과 빌드 성공을 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeRecentDebutCreatorSection.swift:3-71`, `AiCharacterCard.responsiveSize`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "creatorId\|creatorNickname\|creatorProfileImage\|responsiveSize" MainHomeRecentDebutCreatorSection.swift` | 성공 | 표시와 creator ID 전달 확인 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. `CreatorProfileGrid` 대신 직사각형 Figma 카드 전용 View를 사용한 것은 기존 공용 원형 프로필 UI로 충족할 수 없어 확정 결함으로 보지 않았다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | 표시와 callback 추적 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 최근 데뷔 데이터 매핑과 공용 `CreatorProfileGrid` 재사용, 빈 상태를 다시 확인했다.
- 추가 확정 발견 사항 없음.
### 3차 통합 재점검 — 2026-07-31
- 2차 기록의 `CreatorProfileGrid` 재사용 문구는 실제 구현과 다른 오기다. 현재 `MainHomeRecentDebutCreatorSection`은 Figma 직사각형 카드 요구를 위해 전용 `MainHomeRecentDebutCreatorItemView`를 사용하며, 이 선택 자체는 6절 판정과 같이 코드 결함으로 보지 않는다.
- append-only 리뷰 정정은 Phase 19 `REV-P19-004` / `Task 19.4`로 전환했다.
### 4차 통합 재점검 — 2026-07-31
- `REV-P19-004` 보완으로 2차 기록의 `CreatorProfileGrid` 재사용 오기는 위 3차 기록에서 append-only로 정정 완료했다.
- 현재 활성 결론은 `MainHomeRecentDebutCreatorSection`이 전용 `MainHomeRecentDebutCreatorItemView`를 사용하는 것이며, 추가 확정 발견 사항 없음 판정은 유지한다.
### 5차 최종 재점검 — 2026-07-31
- production 코드는 최신 리뷰 결론대로 전용 `MainHomeRecentDebutCreatorItemView`를 사용하고 creator ID callback·빈 상태 처리를 유지하므로 추가 코드 결함은 없다.
- 다만 활성 `plan-task.md``Task 11.1` 작업·기대 결과는 여전히 공용 원형 `CreatorProfileGrid` 재사용을 완료 조건으로 요구해 현재 코드 및 이 리포트의 결론과 충돌한다.
- 문서 정합성 결함은 Phase 19 `REV-P19-005` / `Task 19.5`가 소유하며, Phase 11 production 코드의 결론은 **확정 발견 사항 없음**을 유지한다.
### 6차 최종 재점검 — 2026-07-31
- Phase 19 `REV-P19-005` 보완으로 활성 `Task 11.1`은 공용 원형 `CreatorProfileGrid` 우선 검토 원칙을 유지하되, Figma 직사각형 카드 형태를 충족하지 못할 때 전용 `MainHomeRecentDebutCreatorItemView`를 사용하는 현재 결정을 명시했다.
- 기존 2차~5차 기록은 삭제하지 않고 append-only 정정으로 보존했다.
- 현재 Phase 11 결론은 전용 카드 구현 유지와 **확정 발견 사항 없음**이다.

View File

@@ -0,0 +1,91 @@
# Phase 12 AI 캐릭터 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 12 / Task 12.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.1·7.3, `plan-task.md` Phase 12 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- AI 캐릭터 카드 필드 표시, 빈 상태, callback 식별자가 응답 계약과 상세 route에 맞는지 확인한다.
- 최종 `MainView` route는 `REV-P5-001`과 연계한다.
## 3. 판정 기준
- `characterId``creatorId`는 서로 다른 계약 식별자이므로 캐릭터 상세 callback에 creator ID를 전달하면 High로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeAiCharacterSection.swift:3-40`, Preview `:44-68`, `HomeAiCharacterItem`
- 정상 route: `AppStep.characterDetail(characterId:)`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "onTapCharacter\\(item\\.creatorId\\)\|characterId" MainHomeAiCharacterSection.swift` | 실패 발견 | callback은 creator ID, Preview는 두 ID를 구분 |
| `rg "characterDetail\\(characterId:" SodaLive/Sources` | 성공 | 앱의 캐릭터 상세 계약 확인 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | 같은 `Int`라 타입 검사가 오류를 검출하지 못함 |
## 5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P12-001` | High | 확정 | AI 캐릭터 카드가 `characterId` 대신 `creatorId`를 전달함 | `Task 12.2` / `P12-R1` |
## 6. 발견 사항 상세
### REV-P12-001 — AI 캐릭터 callback 식별자 오류
- **심각도:** High
- **상태:** 확정
- **소유 Task:** `Task 12.2`
**관찰 내용**
카드 탭은 `onTapCharacter(item.creatorId)`를 호출한다. 같은 파일 Preview도 `characterId: 1`, `creatorId: 10`처럼 두 값을 명시적으로 구분하므로 현재 callback 값은 캐릭터 상세 식별자가 아니다.
**영향**
`REV-P5-001`의 route만 수정해도 creator ID를 character ID로 오인해 잘못된 캐릭터를 조회하거나 상세 조회가 실패한다.
**권장 조치**
최소 수정으로 `item.characterId`를 전달하고 `creatorId != characterId` fixture로 callback과 최종 상세를 검증한다.
**판정 기록**
- 2026-07-30 — 응답 모델, Preview fixture, callback과 AppStep 계약 대조로 확정.
## 7. 확정 항목의 plan·goal 전환
`plan-task.md` Phase 12에 `Task 12.2` / `P12-R1`을 추가했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | model→section callback 추적 |
| 후보 판정 | 충족 | `REV-P12-001` 확정 |
| plan 반영 | 충족 | Task 12.2 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 수정 goal 필요
## 9. 수정 후 검증 기록
- 2026-07-30 — `MainHomeAiCharacterSection`의 카드 탭 callback을 `onTapCharacter(item.creatorId)`에서 `onTapCharacter(item.characterId)`로 수정.
- `rg -n "onTapCharacter\(item\.(characterId|creatorId)\)" "SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeAiCharacterSection.swift"` 실행, `item.characterId` 전달 확인.
- `MainView.handleRecommendationCharacterTap(characterId:)`와 함께 `characterDetail(characterId:)` 경로 정적 연결 확인.
- `git diff --check`, `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`, `SodaLive-dev` Debug 빌드 통과.
- 실제 기기/시뮬레이터 탭 수동 확인은 미실행이며 잔여 확인 사항으로 남김.
### 2차 통합 재점검 — 2026-07-31
- 카드가 `item.characterId`를 callback으로 전달하고 Phase 5의 character route로 연결되는 현재 구현을 확인했다.
- `REV-P12-001`은 **수정 완료**로 판정했으며, 추가 확정 발견 사항 없음.

View File

@@ -0,0 +1,63 @@
# Phase 13 장르 크리에이터 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 13 / Task 13.1 및 2026-07-12 미표시 결정 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 13과 재분류 원칙 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 기존 장르 그룹 컴포넌트 구현과 후속 미표시 결정의 코드 반영을 확인한다.
- 장르 섹션을 다시 노출하는 작업은 제외한다.
## 3. 판정 기준
- 모델 필드는 유지하되 `MainHomeRecommendationView`에서 컴포넌트를 조립하지 않는 최신 결정을 우선한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeGenreCreatorSection.swift`, `MainHomeRecommendationResponse.swift:10`, 추천 루트
- 이력: commit `47fe0e7`의 장르 섹션 조립 제거
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "MainHomeGenreCreatorSection\|genreCreators" MainHomeRecommendationView.swift` | 성공 | 검색 결과 없음 |
| `rg "genreCreators" MainHomeRecommendationResponse.swift` | 성공 | 디코딩 모델 유지 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | 미노출 컴포넌트 포함 상태 빌드 성공 |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
코드는 최신 미표시 결정을 충족한다. PRD/Phase 17에 남은 과거 노출 기준은 `REV-P19-001`이 소유한다.
## 7. 확정 항목의 plan·goal 전환
이 Phase의 전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 최신 동작 확인 | 충족 | 루트 참조 없음, 모델 유지 |
| 후보 판정 | 충족 | 코드 결함 없음 |
| plan 반영 | 해당 없음 | 문서 결함은 Phase 19 소유 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- `genreCreators`는 응답 디코딩용으로 유지되고 활성 추천 화면에는 조립되지 않는 최신 결정을 다시 확인했다.
- 추가 확정 발견 사항 없음.

View File

@@ -0,0 +1,61 @@
# Phase 14 응원 크리에이터 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 14 / Task 14.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 14, PRD 7.6 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 응원 creator field, 공용 그룹 UI, creator 탭, 모두 팔로우 callback과 상태 연결을 확인한다.
- Confirm Dialog 동작은 Phase 22로 제외한다.
## 3. 판정 기준
- 빈 배열 미표시, 표시 ID 집합의 follow action 연결, 완료/로딩 중복 방지를 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeCheerCreatorSection.swift`, `MainHomeCreatorGroupSection`, `MainHomeRecommendationView.swift:56-69`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "cheerCreators\|MainHomeCreatorGroupSection\|followAll\|completionKey" ...` | 성공 | 데이터와 상태/action 연결 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. 확인 전 API 지연과 취소 동작은 Phase 22 리포트에서 별도 판정한다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | section→ViewModel action 추적 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 응원 크리에이터 그룹 UI와 공통 모두 팔로우 callback 연결을 다시 확인했다.
- 추가 확정 발견 사항 없음. 확인 Dialog의 최종 판정은 Phase 22 리포트가 소유한다.

View File

@@ -0,0 +1,121 @@
# Phase 15 인기 커뮤니티 Feed 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 15 / Task 15.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.4, `plan-task.md` Phase 15 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 세로 피드, 이미지/오디오/유료 상태, 구매/좋아요/상세 action, 반응 순서와 상대 시간을 확인한다.
- 실제 네트워크 성공 응답과 오디오 청취 품질은 제외한다.
## 3. 판정 기준
- PRD의 조건식을 코드 분기와 직접 대조한다. 제한된 가격 경계 불일치는 Low로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomePopularCommunitySection.swift`, `CommunityPostCard.swift:65-243`, `MainHomeRecommendationViewModel.swift:112-182`
- 문서: PRD 7.4의 `price <= 0 || existOrdered`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "ScrollView\\(\\.horizontal\|LazyHStack\|frame\\(width: 240\\)" ...` | 성공 | 검색 결과 없음, 세로 피드 유지 |
| `rg "price <= 0\|price == 0\|isAccessiblePost" ...` | 실패 발견 | PRD는 `<= 0`, 코드는 `== 0` |
| `rg "onTapPost\|onTapComment\|purchaseCommunityPost\|communityPostLike\|toggleContent" ...` | 성공 | 상세/구매/좋아요/오디오 연결 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P15-001` | Low | 확정 | 무료 판정이 `price <= 0`이 아니라 `price == 0`임 | `Task 15.2` / `P15-R1` |
## 6. 발견 사항 상세
### REV-P15-001 — 무료 가격 경계 불일치
- **심각도:** Low
- **상태:** 확정
- **소유 Task:** `Task 15.2`
**관찰 내용**
이미지 잠금은 `price > 0 && !existOrdered`로 올바르게 판단하지만 reaction bar 노출의 `isAccessiblePost``price == 0 || existOrdered`다. 따라서 PRD가 무료로 정의한 음수 가격은 일반 이미지를 표시하면서 댓글/좋아요를 숨기는 서로 다른 상태가 된다.
**근거**
- 코드: `CommunityPostCard.swift:230-236`
- 문서: PRD 7.4, Task 15.1의 `price <= 0 || existOrdered`
**재현**
1. `imageUrl != nil`, `price == -1`, `existOrdered == false`인 카드를 구성한다.
2. `isLocked == false`지만 `isAccessiblePost == false`가 된다.
3. 요구 결과는 무료/접근 가능 상태다.
**권장 조치**
`isAccessiblePost``price <= 0 || existOrdered`로 맞추고 음수·0·양수/구매 조합을 확인한다.
**판정 기록**
- 2026-07-30 — PRD 조건식과 private 계산 프로퍼티 대조로 확정.
## 7. 확정 항목의 plan·goal 전환
`plan-task.md` Phase 15에 `Task 15.2` / `P15-R1`을 추가했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | card/viewModel/action 추적 |
| 후보 판정 | 충족 | `REV-P15-001` 확정 |
| plan 반영 | 충족 | Task 15.2 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 수정 goal 필요
## 9. 수정 후 검증 기록
- 2026-07-30 — `CommunityPostCard.isAccessiblePost``price == 0 || existOrdered`에서 `price <= 0 || existOrdered`로 수정.
- `isLocked`는 기존 `price > 0 && !existOrdered` 조건을 유지해 유료 미구매 잠금 조건은 변경하지 않음.
- `rg -n "price (==|<=) 0 \|\| existOrdered|isAccessiblePost|isLocked" "SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift"` 실행, 조건식 확인.
- `git diff --check`, `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`, `SodaLive-dev` Debug 빌드 통과.
- 실제 화면 상태 수동 확인은 미실행이며 잔여 확인 사항으로 남김.
### 2차 통합 재점검 — 2026-07-31
- `REV-P15-001``price <= 0 || existOrdered`로 수정된 현재 구현을 확인해 **수정 완료**로 판정했다.
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P15-002` | Medium | 확정 | 이미지 없는 유료 미구매 게시물은 구매 UI와 기존 구매 callback에 진입할 수 없음 | `Task 15.3` / `P15-R2` |
#### REV-P15-002 — 이미지 없는 유료 미구매 게시물 구매 진입 누락
- **관찰 내용:** 카드는 `hasImage`일 때만 `imageContent`를 만들고, 잠금 표시와 `onTapPurchase` Button은 `imageContent` 내부에만 있다. 따라서 `imageUrl == nil && price > 0 && existOrdered == false`이면 `isLocked == true`여도 구매 UI가 렌더링되지 않는다.
- **영향:** PRD가 허용한 이미지 없는 유료 게시물은 추천 피드에서 구매할 방법이 없고, `isAccessiblePost == false`라 반응 UI도 표시되지 않는다.
- **근거:** `CommunityPostCard.swift:65-76`, `:125-173`, `:222-236`; PRD 7.4의 이미지 nullable 및 유료 미구매 구매 지원 기준.
- **재현:** `imageUrl: nil`, `price: 100`, `existOrdered: false` fixture로 카드를 구성하면 본문 아래에 이미지·잠금·구매 Button이 모두 없다.
- **권장 조치:** 이미지 없는 레이아웃을 유지한 채 잠긴 상태의 구매 affordance만 제공하고 기존 `onTapPurchase` 흐름을 재사용한다.
- **계획 전환:** `plan-task.md` Phase 15에 `Task 15.3` / `P15-R2`를 추가했다.
#### REV-P15-002 수정 기록 — 2026-07-31
- `CommunityPostCard`에서 이미지가 없고 유료 미구매인 경우에도 본문 아래에 잠금/가격 구매 affordance를 표시하고 기존 `onTapPurchase` callback을 재사용하도록 수정했다.
- 이미지가 있는 유료 미구매 상태의 `paidLockOverlay`, 무료·구매 완료 상태의 reaction bar 조건은 변경하지 않았다.
- `rg -n 'hasImage|isLocked|lockedTextPurchaseButton|paidLockOverlay|purchaseCommunityPost|onTapPurchase' SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift SodaLive/Sources/V2/Main/Home/Recommendation`로 연결을 확인했다.
- Oracle 리뷰어 게이트에서 기존 구매 flow 재사용과 차단/중요 findings 없음 판정을 받았다.
- 실제 화면에서 이미지 없는 유료 미구매 fixture의 구매 callback 1회 호출 수동 확인은 잔여 확인 사항으로 남긴다.
**현재 결론:** `REV-P15-001`, `REV-P15-002` 수정 완료

View File

@@ -0,0 +1,123 @@
# Phase 16 사업자 정보 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 16 / Task 16.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.5·8.3, `plan-task.md` Phase 16 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 3줄 말줄임, 더보기/접기, width와 공용 V2 컴포넌트 재사용 여부를 확인한다.
- 기존 Audition 화면의 legacy `CustomView/ExpandableTextView` 동작 변경은 제외한다.
## 3. 판정 기준
- 사용자 기능은 충족하더라도 계획한 공용 구현이 빌드되지 않고 같은 코드가 복제되면 Low 유지보수성/문서 정합성 결함으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeBusinessInfoSection.swift:3-67`, `V2/Component/Text/ExpandableTextView.swift:3-71`
- 프로젝트: `project.pbxproj`의 두 `ExpandableTextView.swift` file reference와 Sources 등록
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "BusinessInfoExpandableTextView\|ExpandableTextView" ...` | 실패 발견 | 화면 파일에 측정/토글 구현이 복제됨 |
| `rg "C922F89A...\|ExpandableTextView.swift in Sources" project.pbxproj` | 실패 발견 | V2 파일은 file reference만 있고 Sources 등록 없음 |
| 최신 `xcodebuild ... build` compile 목록 확인 | 실패 근거 확인 | legacy `CustomView/ExpandableTextView.swift`만 app target에서 컴파일 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | private 복제 구현 때문에 화면 자체는 컴파일 성공 |
## 5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P16-001` | Low | 확정 | 공용 V2 확장 텍스트는 미빌드 상태이고 화면에 동일 구현이 복제됨 | `Task 16.2` / `P16-R1` |
## 6. 발견 사항 상세
### REV-P16-001 — 확장 텍스트 구현과 project target 불일치
- **심각도:** Low
- **상태:** 확정
- **소유 Task:** `Task 16.2`
**관찰 내용**
PRD는 `MainHomeBusinessInfoSection`이 재사용 가능한 V2 `ExpandableTextView`를 소비하도록 정했다. 실제 화면은 private `BusinessInfoExpandableTextView`를 다시 구현하며, V2 공용 파일은 Xcode group에만 있고 Sources에 포함되지 않는다. 반면 같은 타입명의 legacy CustomView 파일은 앱 타깃에서 컴파일된다.
**영향**
높이 측정/토글 수정이 두 V2 파일에 중복되고, 공용 컴포넌트 수정이 실제 앱에 반영되지 않아 검증 기록과 빌드 상태가 어긋난다.
**권장 조치**
legacy 소비처는 유지하면서 타입명 충돌을 피하고, 빌드 대상 공용 V2 구현 하나로 화면을 연결한 뒤 이번 변경이 만든 private 중복만 제거한다.
**판정 기록**
- 2026-07-30 — source 내용, PBX Sources와 실제 SwiftCompile 목록 대조로 확정.
## 7. 확정 항목의 plan·goal 전환
`plan-task.md` Phase 16에 `Task 16.2` / `P16-R1`을 추가했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | source/project/build 대조 |
| 후보 판정 | 충족 | `REV-P16-001` 확정 |
| plan 반영 | 충족 | Task 16.2 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 수정 goal 필요
## 9. 수정 후 검증 기록
- 2026-07-30 — `MainHomeBusinessInfoSection`의 private `BusinessInfoExpandableTextView` 중복 구현을 제거하고 V2 공용 `V2ExpandableTextView`를 사용하도록 수정.
- legacy `CustomView/ExpandableTextView` 소비처와 산출물 충돌을 피하기 위해 V2 공용 파일명을 `V2ExpandableTextView.swift`로 정리하고 `SodaLive.xcodeproj/project.pbxproj` Sources에 포함.
- 중간 빌드에서 V2/legacy 파일명이 모두 `ExpandableTextView.swift`일 때 `Multiple commands produce ... ExpandableTextView.stringsdata` 실패를 확인했고, 파일명 정리 후 재빌드 성공.
- `rg -n "V2ExpandableTextView|BusinessInfoExpandableTextView|struct ExpandableTextView|struct V2ExpandableTextView" ...` 실행, 사업자 섹션은 V2 공용 구현을 사용하고 legacy 타입은 기존 경로에만 남음 확인.
- `git diff --check`, `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`, `SodaLive-dev` Debug 빌드 통과.
- 실제 3줄 이하/초과 화면 수동 확인은 미실행이며 잔여 확인 사항으로 남김.
### 2차 통합 재점검 — 2026-07-31
- `REV-P16-001`은 사업자 정보 섹션이 `V2ExpandableTextView` 하나를 소비하는 현재 구현을 확인해 **수정 완료**로 판정했다.
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P16-002` | Low | 확정 | `V2ExpandableTextView.swift`가 프로덕션 target Sources에 두 번 등록됨 | `Task 16.3` / `P16-R2` |
#### REV-P16-002 — 프로덕션 Sources 중복 등록
- **관찰 내용:** `SodaLive` target의 Sources phase `40FE0E80`에 동일 file reference `C922F89A`를 가리키는 build file `C922F89B`, `C922F89D`가 함께 있다. `SodaLive-dev` Sources phase `40FE0E9F`에는 `C922F89C` 한 건만 있다.
- **영향:** 프로덕션 프로젝트 메타데이터에 동일 Swift 소스가 중복되어 빌드 경고 또는 이후 프로젝트 수정 시 불필요한 충돌 원인이 된다.
- **근거:** `project.pbxproj:6385-6434`, `:6846`, `:7760`, `:8027`; 현재 staged diff에서 Task 16.2가 세 build file을 추가한 내역.
- **재현:** 두 앱 target의 Sources phase에서 `V2ExpandableTextView.swift in Sources`를 세면 `SodaLive=2`, `SodaLive-dev=1`이다.
- **권장 조치:** `SodaLive`의 중복 build file 한 건만 제거하고 각 target 포함 수를 1로 맞춘다. 기존 다른 Sources 중복은 이번 범위에서 변경하지 않는다.
- **계획 전환:** `plan-task.md` Phase 16에 `Task 16.3` / `P16-R2`를 추가했다.
- **빌드 확인:** `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -derivedDataPath /private/tmp/SodaLiveDerivedDataReviewCurrentProd build 2>&1 | rg 'V2ExpandableTextView|BUILD'`에서 `Skipping duplicate build file ... V2ExpandableTextView.swift` 경고를 재현했고 빌드 종료 코드는 0이었다.
#### REV-P16-002 수정 기록 — 2026-07-31
- `SodaLive` target Sources phase에 중복 등록된 `C922F89D /* V2ExpandableTextView.swift in Sources */` 한 건과 해당 `PBXBuildFile` 정의를 제거했다.
- `SodaLive-dev` target의 `C922F89C` 등록과 다른 Sources 항목은 변경하지 않았다.
- `rg -n 'V2ExpandableTextView\.swift in Sources|C922F89D' SodaLive.xcodeproj/project.pbxproj``C922F89B`, `C922F89C` 두 건만 남고 `C922F89D`가 제거된 것을 확인했다.
- `plutil -lint SodaLive.xcodeproj/project.pbxproj` 결과 `OK`를 확인했다.
- 전체 빌드는 도구 시간 제한으로 완료하지 못했으나 Oracle 리뷰어 게이트에서 PBX 등록 조건에 대한 차단/중요 findings 없음 판정을 받았다.
**현재 결론:** `REV-P16-001`, `REV-P16-002` 수정 완료
### 3차 통합 재점검 — 2026-07-31
- PBX Sources phase에서 `V2ExpandableTextView.swift``SodaLive`, `SodaLive-dev` target에 각각 한 번만 등록된 것을 다시 확인했다.
- 최신 `SodaLive` Debug 빌드는 종료 코드 0으로 성공했고 `V2ExpandableTextView.swift` duplicate build warning은 더 이상 출력되지 않았다.
- 최신 `SodaLive-dev` 재실행은 10분 이상 종료되지 않아 중단했으며, dev target 등록을 변경하지 않은 점과 직전 dev 빌드 성공 기록을 함께 유지한다.
- Phase 16의 `REV-P16-001`, `REV-P16-002` 수정 완료 판정은 유지한다. 다른 홈 추천 파일의 중복은 Phase 18 `REV-P18-001`이 소유한다.

View File

@@ -0,0 +1,66 @@
# Phase 17 섹션 조립과 빈 상태 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 17 / Task 17.1 및 후속 장르 미표시 결정 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 17과 2026-07-12 재분류 원칙 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 최신 결정 기준 섹션 순서와 데이터 기반 섹션의 빈 배열 숨김을 확인한다.
- 과거 검증 기록은 사실 이력으로 보존하고 현재 기준과 구분한다.
## 3. 판정 기준
- 후속 2026-07-12 미표시 결정이 기존 Task 17.1보다 최신이므로 현재 코드는 장르 섹션을 조립하지 않아야 한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeRecommendationView.swift:25-83`, 각 섹션의 `if !items.isEmpty`
- 이력: commit `47fe0e7`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| 섹션 타입 순서 `rg` | 성공 | live→banner→active→debut→AI→cheer→community→business |
| `rg "MainHomeGenreCreatorSection\|genreCreators" MainHomeRecommendationView.swift` | 성공 | 검색 결과 없음 |
| 각 데이터 섹션 empty guard 확인 | 성공 | 빈 배열에서 제목/컨테이너 미표시 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**코드 확정 발견 사항 없음.**
문서의 활성 기준 충돌은 `REV-P19-001`이 소유한다.
## 6. 발견 사항 상세
현재 코드는 최신 장르 미표시 결정을 충족한다. Task 17.1 본문과 PRD 화면 트리가 과거 노출 순서를 유지하는 문제는 코드 수정 대상이 아니므로 Phase 19로 전환했다.
## 7. 확정 항목의 plan·goal 전환
이 Phase의 신규 Task 없음. 문서 정정은 `Task 19.2` / `P19-R1`에서 수행한다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 코드 범위 확인 | 충족 | 순서와 empty guard 확인 |
| 후보 판정 | 충족 | 코드 후보 없음 |
| 문서 불일치 전환 | 충족 | REV-P19-001 / Task 19.2 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 코드 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 활성 섹션 순서와 빈 배열 숨김, 장르 섹션 미조립을 현재 PRD·코드와 다시 대조했다.
- `REV-P19-001` 문서 충돌은 수정 완료이며 Phase 17 코드의 추가 확정 발견 사항 없음.

View File

@@ -0,0 +1,158 @@
# Phase 18 최종 프로젝트 포함과 빌드 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 18 / Task 18.1~18.2 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 18, 빌드/검증 가이드 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 프로젝트 유효성, 금지 범위, 공백 오류와 현재 `SodaLive-dev` 전체 빌드를 확인한다.
- 개별 컴포넌트 target 누락의 기능 소유권은 해당 구현 Phase에서 판정한다.
## 3. 판정 기준
- `plutil`, 금지 문자열, `git diff --check`, app target compile/link/sign/validate 성공을 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 환경: macOS 26.0, Xcode 26.0, Any iOS Device / Debug
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `plutil -lint SodaLive.xcodeproj/project.pbxproj` | 성공 | `OK` |
| 제외 섹션/Figma URL/기존 HomeApi endpoint 검색 | 성공 | 검색 결과 없음 |
| `git diff --check` | 성공 | 출력 없음 |
| sandbox 내 `xcodebuild -workspace ... -list` | 환경 실패 | CoreSimulator/캐시 권한 제한 |
| sandbox 밖 `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**Phase 18 공통 Gate의 추가 확정 발견 사항 없음.**
V2 공용 `ExpandableTextView`가 Sources에 포함되지 않은 구체적 문제는 `REV-P16-001` / `Task 16.2`가 소유한다.
## 6. 발견 사항 상세
전체 앱은 private 사업자 정보 구현을 사용하므로 빌드가 성공한다. 따라서 project 전체 Gate 실패는 아니지만, 의도한 공용 파일의 미포함은 Phase 16에서 별도로 수정해야 한다.
## 7. 확정 항목의 plan·goal 전환
Phase 18 신규 Task 없음. `Task 16.2`에 project 포함 검증을 명시했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 공통 Gate | 충족 | lint/static/build 성공 |
| 후보 판정 | 충족 | 구체적 누락은 REV-P16-001로 전환 |
| plan 반영 | 충족 | Task 16.2 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 공통 빌드 Gate 통과, `REV-P16-001` 후속 수정 필요
## 9. 수정 후 검증 기록
Phase 16 수정 후 Phase 18 build를 재실행해 누적한다.
### 2차 통합 재점검 — 2026-07-31
- `REV-P16-001` 보완 후 `SodaLive-dev` Debug 빌드 성공 기록과 프로덕션 `SodaLive` Debug 빌드를 다시 확인했다.
- `xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -derivedDataPath /private/tmp/SodaLiveDerivedDataReviewCurrentProd build` — 종료 코드 0, `BUILD SUCCEEDED`.
- 프로덕션 빌드는 `V2ExpandableTextView.swift` 중복 Compile Sources 경고를 출력했으며, 정적 PBX 대조에서도 `SodaLive=2`, `SodaLive-dev=1` 등록을 확인했다.
- 해당 경고는 Phase 16 `REV-P16-002` / `Task 16.3`으로 전환했다. 함께 출력된 다른 기존 Sources 중복 경고는 이번 추천 홈 변경 범위 밖이므로 수정하지 않았다.
**현재 결론:** 두 앱 scheme의 빌드 성공 근거는 충족, `REV-P16-002` 후속 수정 필요
### 3차 통합 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P18-001` | Low | 확정 | 프로덕션 target에 홈 추천 범위 Swift 파일 11개가 중복 Sources 등록됨 | `Task 18.3` / `P18-R1` |
#### REV-P18-001 — 홈 추천 Compile Sources 중복 등록
- **관찰 내용:** `SodaLive` Sources phase에는 이번 계획 범위의 11개 Swift file reference가 각각 두 번 포함되고 `SodaLive-dev`에는 한 번씩 포함된다. 최신 프로덕션 빌드도 해당 파일들의 `Skipping duplicate build file in Compile Sources` 경고를 출력한다.
- **영향:** 빌드는 성공하지만 Phase 18의 프로젝트 포함 검증이 중복 없는 target 구성을 보장하지 못하며, Xcode 프로젝트 수정 시 불필요한 충돌과 경고가 계속 발생한다.
- **근거:** `SodaLive.xcodeproj/project.pbxproj``40FE0E802A8361DF00252293 /* Sources */`, `40FE0E9F2A8362A200252293 /* Sources */`; 2026-07-31 `SodaLive` Debug 빌드 로그.
- **대상 파일:** `MainHomeView.swift`, `MainHomeRecommendationView.swift`, `MainHomeRecommendationViewModel.swift`, `MainHomeRankingView.swift`, `MainHomeFollowingView.swift`, `MainHomeLiveSection.swift`, `MainHomeBannerSection.swift`, `MainHomeActiveCreatorSection.swift`, `MainHomeRecentDebutCreatorSection.swift`, `MainHomeAiCharacterSection.swift`, `MainHomeBusinessInfoSection.swift`.
- **재현:** 두 앱 target의 Sources phase에서 위 file reference를 세면 `SodaLive=2`, `SodaLive-dev=1`이며 프로덕션 빌드 로그에서 같은 경고를 확인할 수 있다.
- **권장 조치:** 프로덕션 중복 build file 한 건씩만 제거하고 두 scheme 빌드와 경고 부재를 확인한다. 다른 기존 중복 Sources는 이번 Task 범위에서 변경하지 않는다.
- **계획 전환:** `plan-task.md` Phase 18에 `Task 18.3` / `P18-R1`을 추가했다.
검증 기록:
- `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`.
- `git diff --check` — 성공, 출력 없음.
- `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -derivedDataPath /private/tmp/SodaLiveDerivedDataReviewCurrentProd build` — 종료 코드 0, 빌드 성공. `V2ExpandableTextView.swift` 중복 경고는 제거됐지만 `REV-P18-001` 대상 경고를 확인했다.
- 최신 `SodaLive-dev` 재실행은 10분 이상 종료되지 않아 중단했고, 같은 dev target의 직전 현재 변경군 빌드 성공 기록은 유지한다.
**당시 결론:** 빌드는 성공하지만 `REV-P18-001` 수정 goal 필요
### REV-P18-001 수정 기록 — 2026-07-31
- `SodaLive.xcodeproj/project.pbxproj`에서 홈 추천 범위 11개 Swift 파일의 중복 Compile Sources 등록을 정리했다.
- 대상 파일 `MainHomeView.swift`, `MainHomeRecommendationView.swift`, `MainHomeRecommendationViewModel.swift`, `MainHomeRankingView.swift`, `MainHomeFollowingView.swift`, `MainHomeLiveSection.swift`, `MainHomeBannerSection.swift`, `MainHomeActiveCreatorSection.swift`, `MainHomeRecentDebutCreatorSection.swift`, `MainHomeAiCharacterSection.swift`, `MainHomeBusinessInfoSection.swift``SodaLive`, `SodaLive-dev` target에 각각 1회 등록된다.
- `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`.
- `xcodebuild -workspace SodaLive.xcworkspace -list` — 프로젝트 파싱 성공, `SodaLive``SodaLive-dev` scheme 확인.
- `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive-dev -configuration Debug -derivedDataPath /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataTask183DevRetry build` — 종료 코드 0.
- `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -derivedDataPath /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataTask183ProdFinal build` — 종료 코드 0.
- 두 빌드 로그에서 `REV-P18-001` 대상 11개 홈 추천 파일의 duplicate build warning은 검색되지 않았다. 기존 범위 밖 V2 파일 duplicate warning은 남아 있으며 이번 Task에서 변경하지 않았다.
- `git diff --check` — 성공, 출력 없음.
**현재 결론:** `REV-P18-001` 수정 완료
### 4차 최종 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P18-002` | Low | 확정 | `Task 18.3` 이후에도 홈 추천 파일 11개의 프로덕션 Sources 중복이 남음 | `Task 18.4` / `P18-R2` |
#### REV-P18-002 — 프로덕션 Sources 잔여 중복
- **관찰 내용:** `Task 18.3` 수정 기록은 대상 11개 파일이 두 target에 각각 1회 등록됐다고 판정하지만, 현재 `40FE0E802A8361DF00252293 /* Sources */`에는 각 파일이 2회, `40FE0E9F2A8362A200252293 /* Sources */`에는 각 파일이 1회 존재한다. 기존 수정은 프로덕션 3개 등록 중 한 건만 제거해 2개 등록을 남겼다.
- **영향:** 프로덕션 빌드가 대상 11개 파일 모두에 `Skipping duplicate build file in Compile Sources` 경고를 계속 출력하고, Phase 18 완료 증거와 실제 프로젝트 상태가 일치하지 않는다.
- **대상 파일:** `MainHomeView.swift`, `MainHomeRecommendationView.swift`, `MainHomeRecommendationViewModel.swift`, `MainHomeRankingView.swift`, `MainHomeFollowingView.swift`, `MainHomeLiveSection.swift`, `MainHomeBannerSection.swift`, `MainHomeActiveCreatorSection.swift`, `MainHomeRecentDebutCreatorSection.swift`, `MainHomeAiCharacterSection.swift`, `MainHomeBusinessInfoSection.swift`.
- **재현 또는 검증:** 두 PBXSourcesBuildPhase 범위의 파일명 포함 수를 세면 대상 전체가 `SodaLive=2`, `SodaLive-dev=1`이며, 격리 DerivedData의 프로덕션 빌드에서도 11개 경고가 재현된다.
- **권장 조치:** `SodaLive`의 잔여 중복 build file과 Sources 참조 한 건씩만 제거하고 dev target과 범위 밖 중복은 보존한 뒤 두 scheme을 fresh DerivedData로 검증한다.
- **계획 전환:** `plan-task.md` Phase 18에 `Task 18.4` / `P18-R2`를 추가했다.
검증 기록:
- `git diff --check` — 성공, 출력 없음.
- `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`.
- PBX Sources phase 대조 — 대상 11개 파일 모두 `SodaLive=2`, `SodaLive-dev=1`.
- 격리 DerivedData 빌드 — `SodaLive`, `SodaLive-dev` 모두 종료 코드 0.
- 빌드 경고 대조 — `SodaLive`에서는 대상 11개 파일의 duplicate build warning이 모두 재현됐고 `SodaLive-dev`에서는 대상 경고가 없었다.
- 첫 프로덕션 빌드 시도는 기본 DerivedData의 기존 `build.db` lock으로 종료 코드 65였으며, 위 결과는 별도 격리 DerivedData에서 다시 검증한 결과다.
**당시 결론:** `REV-P18-001`의 수정 완료 판정을 정정하며 `REV-P18-002` 수정 goal 필요
### REV-P18-002 수정 기록 — 2026-07-31
- `SodaLive.xcodeproj/project.pbxproj``SodaLive` Sources phase에서 홈 추천 범위 11개 Swift 파일의 잔여 중복 build file 참조를 제거했다.
- 대상 파일 `MainHomeView.swift`, `MainHomeRecommendationView.swift`, `MainHomeRecommendationViewModel.swift`, `MainHomeRankingView.swift`, `MainHomeFollowingView.swift`, `MainHomeLiveSection.swift`, `MainHomeBannerSection.swift`, `MainHomeActiveCreatorSection.swift`, `MainHomeRecentDebutCreatorSection.swift`, `MainHomeAiCharacterSection.swift`, `MainHomeBusinessInfoSection.swift``SodaLive`, `SodaLive-dev` target에 각각 1회 등록된다.
- `plutil -lint "SodaLive.xcodeproj/project.pbxproj"``OK`.
- `git diff --check` — 성공, 출력 없음.
- `xcodebuild -quiet -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -destination "generic/platform=iOS" -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataTask184DevRetry" build` — 종료 코드 0.
- `xcodebuild -quiet -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -destination "generic/platform=iOS" -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataTask184Prod" build` — 종료 코드 0.
- 두 빌드 로그에서 `REV-P18-002` 대상 11개 홈 추천 파일의 duplicate build warning은 검색되지 않았다.
- 기존 범위 밖 `MainHomeRankingViewModel.swift`, `MainHomeFollowingViewModel.swift` duplicate warning은 `SodaLive` 빌드 로그에 남아 있으며 이번 Task에서 변경하지 않았다.
-`SodaLive-dev` 빌드 시도는 10분 제한으로 `BUILD INTERRUPTED`됐고, 위 결과는 동일 변경을 별도 DerivedData에서 다시 검증한 결과다.
**현재 결론:** `REV-P18-002` 수정 완료
### 5차 최종 재점검 — 2026-07-31
- `plutil -lint SodaLive.xcodeproj/project.pbxproj`, `git diff --check`, `git diff --cached --check`가 모두 성공했다.
- clean DerivedData에서 `SodaLive-dev` Debug 빌드와 동일 DerivedData의 `SodaLive` Debug 빌드가 각각 종료 코드 0으로 완료됐다.
- 두 빌드 로그에 `REV-P18-002` 대상 홈 추천 11개 파일의 duplicate build warning은 없었다.
- 프로덕션 target에는 다른 V2 기능 파일의 기존 duplicate warning이 남아 있으나 Phase 18 회귀 수정 대상 11개와 무관하고 이번 계획 범위에서 생성한 변경이 아니므로 별도 기존 부채로 유지한다.
**현재 결론:** Phase 18 대상 빌드 및 Sources Gate 통과

View File

@@ -0,0 +1,173 @@
# Phase 19 문서와 검증 기록 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 19 / Task 19.1 및 누적 검증 기록 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `prd.md`, `plan-task.md`, 문서 정책 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 요구사항 변경이 PRD→계획→검증 기록 순으로 누적됐는지, 현재 활성 기준과 코드가 일치하는지 확인한다.
- 과거 기록의 사실 여부를 재작성하지 않고 현재 기준 충돌만 판정한다.
## 3. 판정 기준
- 후속 결정이 PRD와 활성 Task/Gate에 전파되지 않아 서로 반대 결과를 요구하면 Low 문서 정합성 결함으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 문서: PRD Problem/화면 트리, 계획의 2026-07-12 결정, Phase 17, 2026-06-30 검증 기록
- 코드: `MainHomeRecommendationView.swift`
- 환경: macOS 26.0, Xcode 26.0
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "장르의 크리에이터\|MainHomeGenreCreatorSection\|genreCreators" prd.md plan-task.md ...` | 실패 발견 | 최신 미표시 결정과 과거 활성 노출 기준이 공존 |
| `rg "MainHomeGenreCreatorSection\|genreCreators" MainHomeRecommendationView.swift` | 성공 | 현재 코드에는 참조 없음 |
| `git log -p -S'MainHomeGenreCreatorSection' -- MainHomeRecommendationView.swift` | 성공 | commit `47fe0e7`에서 의도적으로 제거 |
| `git diff --check` | 성공 | 출력 없음 |
## 5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P19-001` | Low | 확정 | 장르 섹션 미표시 결정이 PRD와 Phase 17 활성 기준에 전파되지 않음 | `Task 19.2` / `P19-R1` |
## 6. 발견 사항 상세
### REV-P19-001 — 장르 섹션 활성 문서 기준 충돌
- **심각도:** Low
- **상태:** 확정
- **소유 Task:** `Task 19.2`
**관찰 내용**
계획 상단의 2026-07-12 결정과 현재 코드는 장르 섹션을 숨긴다. 그러나 PRD의 추천 화면 설명/트리와 Task 17.1 작업·기대 결과는 장르 섹션을 조립하도록 요구한다. 과거 2026-06-30 기록도 최신 결정과 구분하는 정정 기록이 없다.
**영향**
후속 구현자나 리뷰어가 어느 기준을 활성 요구사항으로 봐야 하는지 혼동하고, 현재 올바른 코드를 Phase 17 실패로 잘못 판정할 수 있다.
**권장 조치**
`genreCreators` 디코딩 필드 유지와 UI 미표시를 구분해 PRD와 활성 Phase 17 기준을 정정한다. 기존 Phase 13 구현/검증 기록은 삭제하지 않고 후속 정정만 누적한다.
**판정 기록**
- 2026-07-30 — 문서 세 위치, 현재 코드와 제거 commit 대조로 확정.
## 7. 확정 항목의 plan·goal 전환
`plan-task.md` Phase 19에 `Task 19.2` / `P19-R1`을 추가했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 문서 범위 확인 | 충족 | PRD/plan/history/code 대조 |
| 후보 판정 | 충족 | `REV-P19-001` 확정 |
| plan 반영 | 충족 | Task 19.2 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 수정 goal 필요
## 9. 수정 후 검증 기록
- 2026-07-30 — PRD와 Phase 17 활성 기준에서 장르 크리에이터 섹션 미표시 결정을 반영.
- `genreCreators` 응답 모델 필드는 API 디코딩을 위해 유지하되, 추천 탭 UI의 활성 화면 트리와 조립 검증 기준에서는 `MainHomeGenreCreatorSection`을 제외하도록 정정.
- 기존 Phase 13 구현 기록과 2026-06-30 검증 기록은 삭제하지 않고 최신 정정 기록만 누적.
- `rg -n "장르의 크리에이터|MainHomeGenreCreatorSection|genreCreators" "docs/20260602_메인_홈_추천_UI_API_연동" "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift"` 실행, 최신 미표시 결정과 과거 기록 구분 확인.
- `git diff --check` 실행, 출력 없이 성공.
### 2차 통합 재점검 — 2026-07-31
- `REV-P19-001`은 PRD와 Phase 17 활성 기준에 장르 섹션 미표시 결정이 반영된 현재 문서를 확인해 **수정 완료**로 판정했다.
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P19-002` | Low | 확정 | PRD 응답 모델 Markdown code fence가 중첩·누락되어 이후 본문 렌더링이 깨짐 | `Task 19.3` / `P19-R2` |
| `REV-P19-003` | Low | 확정 | 활성 문서가 이름 변경 전 `ExpandableTextView.swift` 경로를 가리킴 | `Task 19.3` / `P19-R2` |
#### REV-P19-002 — PRD 응답 예시 code fence 불균형
- **관찰 내용:** PRD 7.1은 line 66에서 연 `kotlin` fence를 `RecommendationBannerResponse` heading 전에 닫지 않고 line 117에서 새 `kotlin` fence를 연다. 그 결과 heading과 설명이 코드 블록으로 처리되고 이후 fence 경계도 어긋난다.
- **영향:** 렌더링된 PRD에서 응답 모델 구조와 설명 계층이 깨져 구현 기준을 오독할 수 있다.
- **근거:** `prd.md:66-148`.
- **권장 조치:** 모델 예시 단위로 fence를 정상 종료하되 모델 내용 자체는 변경하지 않는다.
#### REV-P19-003 — 활성 V2 컴포넌트 경로 불일치
- **관찰 내용:** Task 16.2에서 공용 파일을 `V2ExpandableTextView.swift`로 이름 변경했지만 PRD의 구현 방향·재사용 후보와 계획 상단 주요 대상 파일은 여전히 `ExpandableTextView.swift`를 가리킨다.
- **영향:** 후속 작업자가 존재하지 않는 활성 경로를 기준 파일로 판단할 수 있다.
- **근거:** `prd.md:239`, `:306`, `plan-task.md:45`; 실제 파일 `SodaLive/Sources/V2/Component/Text/V2ExpandableTextView.swift`.
- **권장 조치:** 활성 기준만 최신 이름·경로로 정정하고 과거 Task/검증 이력은 보존한다.
- **계획 전환:** 두 항목을 `plan-task.md` Phase 19의 `Task 19.3` / `P19-R2`로 추가했다.
#### REV-P19-002/REV-P19-003 수정 기록 — 2026-07-31
- PRD 7.1 응답 예시에서 `HomeRecommendationResponse`, `RecommendationBannerResponse`, `HomePopularCommunityPostItem` code fence를 모델 단위로 분리했다.
- PRD의 구현 방향과 재사용 후보, 계획 상단 주요 대상 파일의 활성 경로를 `SodaLive/Sources/V2/Component/Text/V2ExpandableTextView.swift`로 정정했다.
- 과거 Task와 검증 기록에 남은 이전 `ExpandableTextView.swift` 경로는 이력으로 보존했다.
- `rg -n '^```|#### RecommendationBannerResponse|#### HomePopularCommunityPostItem|V2/Component/Text/(ExpandableTextView|V2ExpandableTextView)\.swift|V2ExpandableTextView' docs/20260602_메인_홈_추천_UI_API_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md`로 PRD fence 10개와 활성 경로를 확인했다.
- Oracle 리뷰어 게이트에서 문서 정합성에 대한 차단/중요 findings 없음 판정을 받았다.
**현재 결론:** `REV-P19-001`, `REV-P19-002`, `REV-P19-003` 수정 완료
### 3차 통합 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P19-004` | Low | 확정 | 활성 API 타입명과 Phase 11/21 후속 리뷰 기록이 현재 구현과 다름 | `Task 19.4` / `P19-R3` |
#### REV-P19-004 — 활성 문서와 리뷰 근거 오기
- **관찰 내용:** PRD Goal은 실제 `MainHomeRecommendationApi` 대신 이동 전 이름인 `MainHomeApi`를 신규 타입으로 적는다. Phase 11의 2차 기록은 실제 전용 직사각형 카드 구현과 달리 `CreatorProfileGrid` 재사용을 확인했다고 적고, Phase 21의 3차 기록은 실제 `.liveReplay -> .content(targetId)` 분기와 달리 `LIVE_REPLAY`도 라이브/creator fallback을 사용한다고 적는다.
- **영향:** 후속 구현자가 존재하지 않는 타입을 찾거나, 리뷰 완료 근거를 실제 UI·라우팅과 반대로 해석할 수 있다.
- **근거:** `prd.md:18`, `phase11-recent-debut-creators.md:59`, `phase21-recent-activity-live-fallback.md:162`, 현재 Swift 구현.
- **권장 조치:** PRD 활성 타입명을 정정하고 두 리뷰의 기존 기록을 삭제하지 않은 채 사실관계 정정 기록을 누적한다.
- **계획 전환:** `plan-task.md` Phase 19에 `Task 19.4` / `P19-R3`을 추가했다.
**당시 결론:** `REV-P19-004` 수정 goal 필요
#### REV-P19-004 수정 기록 — 2026-07-31
- PRD Goal의 활성 추천 API 타입명을 실제 구현과 같은 `MainHomeRecommendationApi`로 정정했다.
- Phase 11 리뷰의 `CreatorProfileGrid` 재사용 오기는 기존 기록을 삭제하지 않고 전용 `MainHomeRecentDebutCreatorItemView` 사용으로 append-only 정정 완료했다.
- Phase 21 리뷰의 `LIVE_REPLAY` fallback 오기는 기존 기록을 삭제하지 않고 `targetId` 기반 콘텐츠 상세 이동 및 creator fallback 없음으로 append-only 정정 완료했다.
- `rg -n "MainHomeApi|MainHomeRecommendationApi|CreatorProfileGrid|MainHomeRecentDebutCreatorItemView|LIVE_REPLAY|liveReplay|targetId" docs/20260602_메인_홈_추천_UI_API_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/reviews/phase11-recent-debut-creators.md docs/20260602_메인_홈_추천_UI_API_연동/reviews/phase21-recent-activity-live-fallback.md`로 활성 타입명과 append-only 정정 기록을 확인했다.
- `git diff --check` — 성공, 출력 없음.
**현재 결론:** `REV-P19-004` 수정 완료
### 4차 최종 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P19-005` | Low | 확정 | Phase 11 활성 완료 기준이 현재 전용 카드 구현과 최신 리뷰 판정에 반대됨 | `Task 19.5` / `P19-R4` |
#### REV-P19-005 — 최근 데뷔 섹션 활성 계획 기준 충돌
- **관찰 내용:** `Task 11.1``CreatorProfileGrid``CreatorProfileItem` 우선 재사용 및 공용 widget 재사용을 기대 결과로 둔다. 현재 `MainHomeRecentDebutCreatorSection`은 Figma 직사각형 카드 요구 때문에 전용 `MainHomeRecentDebutCreatorItemView`를 사용하고, Phase 11 최신 리뷰도 이를 올바른 구현으로 판정한다.
- **영향:** Task는 완료 상태지만 활성 완료 기준만 적용하면 현재 올바르다고 판정한 코드를 실패로 보게 되어 후속 구현·리뷰 기준이 모순된다.
- **근거:** `plan-task.md:440-453`, `MainHomeRecentDebutCreatorSection.swift:20-69`, `phase11-recent-debut-creators.md` 3~5차 정정 기록.
- **재현 또는 검증:** `rg -n 'CreatorProfileGrid|MainHomeRecentDebutCreatorItemView' plan-task.md phase11-recent-debut-creators.md MainHomeRecentDebutCreatorSection.swift`로 활성 Task와 현재 코드·리뷰의 반대 기준을 확인한다.
- **권장 조치:** 공용 우선 원칙은 보존하되 기존 원형 widget이 Figma 직사각형 카드를 충족하지 못해 페이지 전용 View를 사용한다는 활성 예외와 현재 기대 결과를 `Task 11.1`에 명시한다.
- **계획 전환:** `plan-task.md` Phase 19에 `Task 19.5` / `P19-R4`를 추가했다.
**현재 결론:** `REV-P19-005` 수정 goal 필요
#### REV-P19-005 수정 기록 — 2026-07-31
- `plan-task.md`의 활성 `Task 11.1` 작업 내용과 검증 기준을 현재 코드·Phase 11 최신 리뷰 결론에 맞게 정정했다.
- 공용 컴포넌트 우선 원칙은 유지하되, 공용 원형 `CreatorProfileGrid`/`CreatorProfileItem`으로 Figma 직사각형 카드 형태를 충족하지 못할 때 페이지 전용 `MainHomeRecentDebutCreatorItemView`를 사용하는 예외를 명시했다.
- 기존 Phase 11 완료 기록과 2차~5차 리뷰 정정 이력은 삭제하지 않고, Phase 11 리뷰에 6차 정정 기록을 누적했다.
- `rg -n "CreatorProfileGrid|MainHomeRecentDebutCreatorItemView" "docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md" "docs/20260602_메인_홈_추천_UI_API_연동/reviews/phase11-recent-debut-creators.md" "SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeRecentDebutCreatorSection.swift"``git diff --check`로 확인했다.
**현재 결론:** `REV-P19-005` 수정 완료

View File

@@ -0,0 +1,103 @@
# Phase 2 신규 API와 응답 모델 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 2 / Task 2.1~2.2 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.1, `plan-task.md` Phase 2 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 추천 전용 endpoint의 격리, 인증 패턴과 `HomeRecommendationResponse`의 PRD 계약 일치 여부를 확인한다.
- API/Repository/응답 모델을 포함하고 UI 렌더링은 제외한다.
## 3. 판정 기준
- PRD에 없는 필수 `Decodable` 필드가 기준 응답을 실패시키면 High로 판정한다.
- Swift `Decodable` 동작, 실제 사용처와 전체 빌드로 후보를 검증한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeRecommendationApi.swift:14-46`, `MainHomeRecommendationRepository.swift:6-15`, `MainHomeRecommendationResponse.swift:3-79`
- 문서: PRD의 `HomeRecommendationResponse` 스키마, Task 2.2의 과거 추정 필드 제거 기준
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "/api/v2/home/recommendations\|getRecommendations" ...` | 성공 | endpoint는 추천 전용 Repository 경로에만 존재 |
| `rg "firstAudioContents\|HomeFirstAudioContentItem" ...` | 실패 발견 | 모델에만 남고 PRD와 소비 UI에는 없음 |
| `swift -module-cache-path /private/tmp/sodalive-review-swift-module-cache -e '<필수 Decodable 키 누락 재현>'` | 실패 재현 | `keyNotFound(firstAudioContents)` |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | 컴파일은 성공하나 런타임 계약 실패를 검출하지 않음 |
## 5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P2-001` | High | 확정 | PRD에 없는 `firstAudioContents` 필수 키가 추천 응답 전체 디코딩을 실패시킬 수 있음 | `Task 2.3` / `P2-R1` |
## 6. 발견 사항 상세
### REV-P2-001 — PRD에 없는 필수 디코딩 키
- **심각도:** High
- **상태:** 확정
- **소유 Task:** `Task 2.3`
**관찰 내용**
`HomeRecommendationResponse.firstAudioContents`는 non-optional이지만 현재 PRD 응답 스키마에 없고 앱에서도 소비되지 않는다. PRD 기준 JSON에 이 키가 없으면 `JSONDecoder``keyNotFound`를 던져 다른 모든 추천 섹션도 표시되지 않는다.
**근거**
- 코드: `MainHomeRecommendationResponse.swift:8`, `:70-79`
- 문서: PRD 7.1 응답에는 `firstAudioContents`가 없음
- 사용처: 모델 선언 외 검색 결과 없음
**재현**
1. `lives` 등 PRD 필수 키를 포함하고 `firstAudioContents`를 제외한 JSON을 준비한다.
2. non-optional 동일 구조로 디코딩한다.
3. 실제 결과는 `keyNotFound(firstAudioContents)`다.
4. 요구 결과는 PRD 기준 응답의 정상 디코딩이다.
**권장 조치**
실제 사용 경로가 없는 필드와 전용 item 모델을 제거하고 PRD 최소 응답 디코딩 및 앱 빌드를 검증한다.
**판정 기록**
- 2026-07-30 — 코드, PRD, 사용처 검색과 Swift 디코딩 재현으로 확정.
## 7. 확정 항목의 plan·goal 전환
`plan-task.md` Phase 2에 `Task 2.3` / `P2-R1`을 추가했다.
## 8. 리뷰 종료 판정
| 판정 항목 | 결과 | 근거 |
|---|---|---|
| 리뷰 범위 전체 확인 | 충족 | API→Repository→모델 추적 |
| 후보 항목 판정 완료 | 충족 | `REV-P2-001` 확정 |
| 확정 항목 plan 반영 | 충족 | Task 2.3 |
| 검증 명령과 결과 기록 | 충족 | 4절 |
**최종 결론:** 수정 goal 필요
**남은 항목:** `REV-P2-001`
## 9. 수정 후 검증 기록
- 2026-07-30 — `HomeRecommendationResponse`에서 `firstAudioContents``HomeFirstAudioContentItem` 제거.
- `swift -module-cache-path "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sodalive-swift-module-cache" -e '<PRD 필수 필드만 포함한 추천 JSON 디코딩>'` 실행, `decoded without firstAudioContents` 확인.
- `rg -n "firstAudioContents|HomeFirstAudioContentItem" "SodaLive/Sources/V2/Main/Home/Recommendation"` 실행, 검색 결과 없음 확인.
- `git diff --check`, `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`, `SodaLive-dev` Debug 빌드 통과.
### 2차 통합 재점검 — 2026-07-31
- PRD 응답 필드와 현재 `HomeRecommendationResponse`를 다시 대조했다.
- `REV-P2-001``firstAudioContents` 제거와 기준 JSON 디코딩 기록을 확인해 **수정 완료**로 판정했으며, 추가 확정 발견 사항 없음.

View File

@@ -0,0 +1,62 @@
# Phase 20 커뮤니티 게시물 상세 라우팅 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 20 / Task 20.1~20.3 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.3~7.4, `plan-task.md` Phase 20 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 최근 활동 `COMMUNITY`, 인기 커뮤니티 카드/댓글의 `postId` 상세 이동과 다른 action 비회귀를 확인한다.
- 팔로잉 스케줄의 기존 `.community` route는 변경 제외 범위로 확인한다.
## 3. 판정 기준
- 추천 흐름은 `.creatorChannelCommunityPostDetail(postId:)`, 팔로잉 스케줄은 기존 `.creatorCommunityAll(creatorId:)`를 유지해야 한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `HomeActiveCreatorTapDestination`, `MainHomePopularCommunitySection.swift:27-53`, `MainView.swift:539-543`, `:575-588`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "onTapCommunityPost\|creatorChannelCommunityPostDetail\|onTapPost\|onTapComment" ...` | 성공 | 추천 `postId` 상세 연결 |
| `rg "handleFollowingScheduleTap\|creatorCommunityAll" MainView.swift` | 성공 | 팔로잉 기존 분기 유지 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | 모델→section→MainView route 추적 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 최근 활동 COMMUNITY와 인기 커뮤니티 카드가 `postId` 기준 상세 callback을 전달하고 공통 상세 guard를 재사용하는지 다시 확인했다.
- 추가 확정 발견 사항 없음.

View File

@@ -145,3 +145,30 @@ Swift: 6.2
## 9. 수정 후 검증 기록
해당 없음. 확정 발견 사항과 후속 수정이 없다.
### 2차 통합 재점검 — 2026-07-30
- 기준: `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree
- 무엇을: Phase 22 working tree 변경이 Phase 21의 `LIVE`, `LIVE_REPLAY`, `AUDIO`, `COMMUNITY`, unknown route와 기존 guard 연결을 회귀시키지 않았는지 재확인
- 어떻게:
- `rg -n "activeCreatorTapDestination|onTapCreator|creatorId > 0|liveReplay|onTapLive|onTapContent|onTapCommunityPost" SodaLive/Sources/V2/Main/Home/Recommendation SodaLive/Sources/V2/Main/MainView.swift` — 분기와 callback 유지 확인
- `git diff --check` — 성공, 출력 없음
- `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build``BUILD SUCCEEDED`
- 결과: 기존 최종 결론 유지, 확정 발견 사항 없음
### 3차 통합 재점검 — 2026-07-31
- LIVE/LIVE_REPLAY에서 유효한 `targetId`는 라이브로, 무효 target은 creator 상세로 이동하는 fallback과 기존 guard를 다시 확인했다.
- 추가 확정 발견 사항 없음.
### 4차 통합 재점검 — 2026-07-31
- 3차 기록은 `LIVE_REPLAY` 분기를 `LIVE`와 합쳐 서술한 오기다. 실제 구현과 활성 요구사항은 `LIVE + targetId`만 라이브룸, `LIVE + targetId == nil + creatorId > 0`만 creator 상세로 이동한다.
- `LIVE_REPLAY + targetId`는 콘텐츠 상세로 이동하고, `targetId == nil`이면 이동하지 않으며 creator fallback을 적용하지 않는다.
- append-only 리뷰 정정은 Phase 19 `REV-P19-004` / `Task 19.4`로 전환했다. Phase 21 production 코드의 추가 확정 발견 사항은 없다.
### 5차 통합 재점검 — 2026-07-31
- `REV-P19-004` 보완으로 3차 기록의 `LIVE_REPLAY` fallback 오기는 위 4차 기록에서 append-only로 정정 완료했다.
- 현재 활성 결론은 `LIVE_REPLAY + targetId`만 콘텐츠 상세로 이동하고 `targetId == nil`이면 이동하지 않는 것이며, creator fallback은 적용하지 않는다.

View File

@@ -0,0 +1,77 @@
# Phase 22 모두 팔로우 확인 Dialog 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 22 / Task 22.1~22.3 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree의 Phase 22 변경 |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.6, `plan-task.md` Phase 22 |
| 리뷰 상태 | 판정 완료 / Phase Gate 완료 |
## 2. 리뷰 목적과 범위
- 로그인 선행, 보류 action, 확인/취소/dimmed 처리, I18n과 기존 follow API 불변성을 확인한다.
- 실제 로그인 계정과 네트워크 요청 횟수를 사용하는 수동 검증까지 완료했다.
## 3. 판정 기준
- 정적 흐름과 빌드는 코드 결함을 판정하고, 요청 0/1회와 언어별 표시는 수동 검증 결과로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainView.swift:27-30`, `:80-82`, `:332-358`, `:546-555`; `I18n.swift`의 follow confirm 문구
- API: `MainHomeRecommendationViewModel.followAll`, 기존 request/Repository
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 또는 수동 검증 | 결과 | 핵심 증거 |
|---|---|---|
| 상태/action/I18n `rg` | 성공 | 로그인 guard 뒤 dialog, 확인/취소 시 pending action 제거 |
| 기존 follow API/request `rg` | 성공 | endpoint/request/ViewModel 계약 변경 없음 |
| `git diff --check` | 성공 | 출력 없음 |
| `plutil -lint SodaLive.xcodeproj/project.pbxproj` | 성공 | `OK` |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
| 실제 확인 전·취소·dimmed 요청 0회/확인 1회 | 성공 | 사용자 수동 테스트 결과 발견된 오류 없음 |
| ko/en/ja 화면 표시 | 성공 | 사용자 수동 테스트 결과 발견된 오류 없음 |
## 5. 발견 사항 요약
**정적 리뷰에서 확정된 코드 결함 없음.**
Task 22.3의 수동 Phase Gate까지 완료됐다.
## 6. 발견 사항 상세
코드상 확인 시 pending action을 지역 변수로 옮긴 뒤 상태를 먼저 비우고 1회 실행하며, 취소와 dimmed는 action을 폐기한다. 비로그인은 action을 저장하지 않고 로그인으로 이동한다.
수동 네트워크 횟수와 다국어 화면은 사용자 수동 테스트에서 발견된 오류가 없음을 완료 근거로 반영했다.
## 7. 확정 항목의 plan·goal 전환
신규 전환 항목 없음. 기존 `Task 22.3`을 완료 처리했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 정적 범위 확인 | 충족 | callback/I18n/API 추적 |
| 후보 판정 | 충족 | 정적 코드 후보 없음 |
| 수동 Phase Gate | 충족 | 요청 횟수/언어별 표시 수동 검증 완료 |
| 신규 plan 반영 | 해당 없음 | 추가 전환 항목 없음 |
| 자동 검증 기록 | 충족 | 4절 |
**최종 결론:** 정적 확정 발견 사항 없음, Task 22.3 수동 검증 완료
**남은 항목:** 없음
## 9. 수정 후 검증 기록
- 2026-07-31 — 사용자 수동 테스트 결과 발견된 오류 없음.
- 확인 전·취소·dimmed 요청 0회, 확인 시 요청 1회, 비로그인 로그인 이동, 자동 팔로우 미실행, 기존 완료/호출 중/API 실패 toast 유지, ko/en/ja 문구 표시를 완료 근거로 반영.
### 2차 통합 재점검 — 2026-07-31
- 수동 완료 증거와 확인 전 0회·확인 시 1회 요청, 비로그인·loading·오류·ko/en/ja 조건을 재검토했다.
- 추가 확정 발견 사항 및 남은 확인 항목 없음.

View File

@@ -0,0 +1,62 @@
# Phase 3 활동 타입과 I18n 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 3 / Task 3.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.2, `plan-task.md` Phase 3 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- `LIVE`, `LIVE_REPLAY`, `AUDIO`, `COMMUNITY`, unknown 변환과 ko/en/ja 문구를 확인한다.
- 활동 타입 표시만 포함하고 탭 목적지는 Phase 21로 제외한다.
## 3. 판정 기준
- 서버 코드 비노출, 확정 enum별 I18n 매핑과 빌드 성공을 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `RecommendedActivityType.swift:3-39`, `I18n.swift``HomeRecommendation`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "LIVE_REPLAY\|activityLive\|activityAudio\|activityCommunity\|ライブ\|オーディオ\|コミュニティ" ...` | 성공 | enum과 ko/en/ja 문구 확인 |
| `rg "case \\.unknown\|return \\"\\"" RecommendedActivityType.swift` | 성공 | unknown 서버 코드 비노출 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | enum/I18n 대조 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- `RecommendedActivityType`의 LIVE/LIVE_REPLAY/AUDIO/COMMUNITY 및 unknown 표시와 ko/en/ja I18n 연결을 다시 확인했다.
- 추가 확정 발견 사항 없음.

View File

@@ -0,0 +1,99 @@
# Phase 4 추천 데이터 상태 계층 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 4 / Task 4.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 4 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 추천 API 로딩, 성공 데이터, 오류, 모두 팔로우와 커뮤니티 action 상태가 전용 ViewModel에 모였는지 확인한다.
- ViewModel/Repository 연결을 포함하고 개별 UI 표현은 제외한다.
## 3. 판정 기준
- `View → ViewModel → Repository → Api` 책임 경계, 실패 처리와 중복 요청 방지를 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeRecommendationViewModel.swift:4-183`, `MainHomeRecommendationRepository.swift`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "final class MainHomeRecommendationViewModel\|fetchRecommendations\|isLoading\|errorMessage\|followAll" SodaLive/Sources/V2/Main/Home` | 성공 | 전용 상태 계층 확인 |
| `rg "unowned self" MainHomeRecommendationViewModel.swift` | 성공 | 검색 결과 없음, weak 캡처 사용 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. 응답 계약 결함은 `REV-P2-001`이 소유한다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | 상태와 Repository 흐름 추적 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 추천 조회·로딩·오류·mutation 상태의 `MainHomeRecommendationViewModel` 소유와 Repository 연결을 다시 확인했다.
- 추가 확정 발견 사항 없음.
### 3차 최종 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P4-001` | Low | 확정 | 추천 응답 디코딩 오류가 프로젝트 오류 로그 대신 `print`로 출력됨 | `Task 4.2` / `P4-R1` |
#### REV-P4-001 — 신규 ViewModel 로깅 규칙 불일치
- **관찰 내용:** `MainHomeRecommendationViewModel.fetchRecommendations()`의 디코딩 `catch`는 toast와 loading 상태를 처리하면서 오류 자체는 `print(error)`로 출력한다.
- **영향:** 신규 코드의 오류가 프로젝트 `ERROR_LOG` 경로와 분리되어 운영·디버그 로그 수집과 검색이 일관되지 않다. 사용자 오류 처리는 유지되므로 심각도는 Low다.
- **근거:** `MainHomeRecommendationViewModel.swift:50-54`; `docs/agent-guides/code-style.md`의 로깅 규칙.
- **재현 또는 검증:** `rg -n 'print\\(' SodaLive/Sources/V2/Main/Home/Recommendation` 실행 시 해당 한 건이 검색되고, 같은 파일의 네트워크 오류는 `ERROR_LOG(error.localizedDescription)`을 사용한다.
- **권장 조치:** toast·loading 분기는 건드리지 않고 `print(error)``ERROR_LOG(error.localizedDescription)`로 교체한다.
- **계획 전환:** `plan-task.md` Phase 4에 `Task 4.2` / `P4-R1`을 추가했다.
**현재 결론:** `REV-P4-001` 수정 goal 필요
#### REV-P4-001 수정 기록 — 2026-07-31
- `MainHomeRecommendationViewModel.fetchRecommendations()`의 추천 응답 디코딩 `catch`에서 `print(error)``ERROR_LOG(error.localizedDescription)`로 교체했다.
- 기존 toast, popup, loading 상태 처리는 변경하지 않았다.
- `rg -n "print\(|ERROR_LOG|func fetchRecommendations|catch" "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift" "SodaLive/Sources/Utils/Log.swift"`로 추천 ViewModel 범위의 직접 `print(` 제거와 `ERROR_LOG` 사용을 확인했다.
- `git diff --check`, `plutil -lint`로 검증했다.
- `SodaLive-dev` Debug 빌드는 같은 DerivedData로 2회 실행했지만 Pods/SPM 및 app resource 처리 중 10분 도구 제한으로 `BUILD INTERRUPTED`되어 성공 증거를 확보하지 못했다.
**현재 결론:** 코드 수정 완료, 빌드 검증 차단
### 4차 최종 검증 — 2026-07-31
- `rg -n "print\\(|ERROR_LOG|func fetchRecommendations|catch" SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift SodaLive/Sources/Utils/Log.swift`로 추천 ViewModel에 직접 `print(`가 없고 디코딩 `catch``ERROR_LOG(error.localizedDescription)`를 사용하는 것을 다시 확인했다.
- `git diff --check`, `git diff --cached --check`는 출력 없이 성공했고 `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`였다.
- `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive-dev -configuration Debug -destination generic/platform=iOS -derivedDataPath /private/tmp/SodaLiveDerivedDataReviewFinal-dev build`는 종료 코드 0으로 통과했다.
- 같은 DerivedData에서 `SodaLive` 운영 스킴도 종료 코드 0으로 통과했다. 출력된 기존 앱·외부 의존성 경고는 이 Low 로깅 항목의 완료 조건을 막지 않는다.
- `plan-task.md``Task 4.2` / `P4-R1`을 완료 처리했다.
**현재 결론:** `REV-P4-001` 수정 및 검증 완료

View File

@@ -0,0 +1,226 @@
# Phase 5 추천 상세 진입 라우팅 guard 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 5 / Task 5.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.3, `plan-task.md` Phase 5 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 추천 상세 callback이 `MainView`의 로그인/본인인증/민감 콘텐츠 guard와 올바른 `AppStep`을 사용하는지 확인한다.
- `MainView`, `MainHomeView`, `MainHomeRecommendationView` callback 체인을 포함한다.
## 3. 판정 기준
- 확정된 상세 타입과 다른 route로 이동하면 High로 판정한다.
- 외부 계약이 필요한 성인 여부는 보류 조건으로 분리하고 임의 구현 여부만 확인한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainView.swift:175-197`, `:508-555`, `:707-739`; `HomeTabView.swift:40-71`
- 문서: PRD 7.3의 `characterDetail`, Task 5.1의 실제 상세 이동 기준
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "onTapCharacter: handleRecommendationCreatorTap\|characterDetail" ...` | 실패 발견 | 추천 AI callback만 creator handler에 연결 |
| `rg "performRecommendationDetailAction\|pendingAction\|contentViewSettings" MainView.swift` | 성공 | 공통 guard 인프라 존재 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | 잘못된 타입 연결은 컴파일로 검출되지 않음 |
## 5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P5-001` | High | 확정 | AI 캐릭터 callback이 크리에이터 상세 handler에 연결됨 | `Task 5.2` / `P5-R1` |
## 6. 발견 사항 상세
### REV-P5-001 — AI 캐릭터 상세 route 오연결
- **심각도:** High
- **상태:** 확정
- **소유 Task:** `Task 5.2`
**관찰 내용**
`MainView``onTapCharacter``handleRecommendationCreatorTap`을 전달한다. 따라서 값이 무엇이든 `.creatorDetail(userId:)`로 이동하며 `.characterDetail(characterId:)` 경로가 추천 홈에 없다.
**근거**
- 코드: `MainView.swift:186`, `:525-529`
- 정상 선례: `HomeTabView.swift:40-71`
- 실제 route: `AppStep.characterDetail(characterId:)`, `ContentView``CharacterDetailView`
**영향**
AI 캐릭터 카드를 탭하면 요청한 캐릭터 상세가 아니라 크리에이터 상세로 이동한다. 캐릭터용 본인인증/민감 콘텐츠 흐름도 적용할 수 없다.
**권장 조치**
양수 `characterId`를 받아 기존 추천 guard를 재사용하고 `.characterDetail(characterId:)`를 실행하는 전용 handler를 연결한다. 섹션의 ID 전달은 `REV-P12-001`과 함께 수정한다.
**판정 기록**
- 2026-07-30 — callback 소비 지점과 기존 Character route 대조로 확정.
**보류 조건**
`HomeLiveItem.isAdult` 부재에 따른 라이브 성인 guard는 PRD에 이미 외부 계약으로 기록되어 있으며 이번 결함과 분리한다.
## 7. 확정 항목의 plan·goal 전환
`plan-task.md` Phase 5에 `Task 5.2` / `P5-R1`을 추가했다.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | callback→guard→AppStep 추적 |
| 후보 판정 | 충족 | `REV-P5-001` 확정 |
| plan 반영 | 충족 | Task 5.2 |
| 보류 조건 기록 | 충족 | 라이브 성인 필드 계약 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 수정 goal 필요
## 9. 수정 후 검증 기록
- 2026-07-30 — `MainView``handleRecommendationCharacterTap(characterId:)` 추가 후 `onTapCharacter`를 해당 handler에 연결.
- 양수 `characterId`만 통과시키고 기존 `performRecommendationDetailAction(requiresAdultContent: true)` guard를 재사용해 `.characterDetail(characterId:)`로 이동하도록 수정.
- `rg -n "onTapCharacter:|handleRecommendationCharacterTap|characterDetail\(characterId:" "SodaLive/Sources/V2/Main/MainView.swift"` 실행, 연결 확인.
- `git diff --check`, `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`, `SodaLive-dev` Debug 빌드 통과.
- 실제 기기/시뮬레이터 탭 수동 확인은 미실행이며 잔여 확인 사항으로 남김.
### 2차 통합 재점검 — 2026-07-31
- AI 캐릭터 callback이 `handleRecommendationCharacterTap(characterId:)`와 공통 상세 guard를 거쳐 `.characterDetail`로 이어지는 현재 경로를 확인했다.
- `REV-P5-001`은 **수정 완료**로 판정했으며, 추가 확정 발견 사항 없음.
### 3차 최종 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P5-002` | High | 확정 | 본인인증 성공 후 민감 콘텐츠 보기 설정 guard를 건너뜀 | `Task 5.3` / `P5-R2` |
#### REV-P5-002 — 본인인증 후 민감 콘텐츠 설정 guard 우회
- **심각도:** High
- **상태:** 확정
- **관련 요구사항:** PRD 7.3 추천 탭 상세 진입 guard, Success Criteria의 로그인·본인인증·민감 콘텐츠 설정 순차 통과
- **소유 Task:** `Task 5.3` / `P5-R2`
**관찰 내용**
`performRecommendationDetailAction(requiresAdultContent: true)`은 한국 사용자이고 `auth == false`이면 최종 상세 route closure 자체를 `pendingAction`에 저장하고 설정 검사를 실행하기 전에 반환한다. 이후 `authView`의 인증 성공 callback은 `auth = true`로 바꾼 뒤 저장한 closure를 직접 실행하므로 `UserDefaults.isAdultContentVisible() == false`여도 `contentViewSettings`로 이동하지 않고 캐릭터 상세로 진입한다.
**근거**
- 코드: `MainView.swift:371-377`의 인증 성공 후 `pendingAction` 직접 실행
- 코드: `MainView.swift:714-745`의 본인인증 분기 뒤에 위치한 민감 콘텐츠 설정 검사
- route 소비: `MainView.swift:539-543`의 AI 캐릭터 상세 action
- 문서: `prd.md` 7.3과 Success Criteria는 상세 route 전에 본인인증과 민감 콘텐츠 보기 설정 guard를 모두 통과하도록 요구한다.
**재현 또는 검증 절차**
1. 토큰이 있고 국가 코드가 `KR` 또는 빈 값이며 `auth == false`, `isAdultContentVisible == false`인 상태를 만든다.
2. AI 캐릭터 카드를 터치하고 본인인증을 완료한다.
3. 현재 상태 전이 self-check는 `detail=true settings=false`를 출력한다.
4. 요구 결과는 인증 성공 후 민감 콘텐츠 설정을 다시 검사해 `contentViewSettings`로 이동하고 상세 route는 실행하지 않는 것이다.
**영향**
본인인증과 민감 콘텐츠 설정이 모두 필요한 제한된 상태에서 사용자가 비활성화한 민감 콘텐츠 보기 설정을 우회해 AI 캐릭터 상세로 진입한다. 명시적인 콘텐츠 보호 요구사항을 위반하므로 High로 판정한다.
**권장 조치**
인증 성공 뒤 저장된 최종 route를 직접 실행하지 말고 기존 성인 콘텐츠 guard의 설정 검사를 다시 통과하게 한다. 별도 guard를 복제하지 않고 `performRecommendationDetailAction(requiresAdultContent: true)` 흐름을 한 번 더 소비하는 최소 수정으로 제한한다.
**판정 기록**
- 2026-07-31 — 코드 분기 순서와 최소 Swift 상태 전이 self-check의 `detail=true settings=false` 결과로 확정.
**계획 전환**
`plan-task.md` Phase 5에 `Task 5.3` / `P5-R2`를 추가했다.
#### REV-P5-002 수정 기록 — 2026-07-31
- `pendingAction`에 최종 상세 route closure를 직접 저장하지 않고, 인증 성공 후 `performRecommendationDetailAction(requiresAdultContent: true, action:)`를 다시 통과하는 closure를 저장하도록 수정했다.
- 기존 로그인 guard, 비한국 사용자, 이미 본인인증된 사용자, 민감 콘텐츠 설정 활성 사용자 흐름은 변경하지 않았다.
- Swift 상태 전이 self-check로 기존 방식의 `detail=true settings=false` 실패를 재현하고, 수정 방식에서 `settings=false`는 설정 이동·상세 미실행, `settings=true`는 상세 실행이 되는 것을 확인했다.
- `rg -n "pendingAction = \{|performRecommendationDetailAction\(requiresAdultContent: true, action: action\)" "SodaLive/Sources/V2/Main/MainView.swift"`, `git diff --check`, `plutil -lint`로 정적 검증했다.
- `SodaLive-dev` Debug 빌드는 iPhoneOS에서 10분 도구 제한으로 반복 `BUILD INTERRUPTED`, Simulator에서 SPM `Promises` 컴파일 단계 실패로 성공 증거를 확보하지 못했다.
- 실제 기기/시뮬레이터의 Bootpay 본인인증 완료 수동 확인은 이번 환경에서 수행하지 못해 상태 전이 self-check와 정적 검증으로 대체했다.
**현재 결론:** `REV-P5-001`, `REV-P5-002` 수정 완료
### 4차 수정 검증 및 추가 재점검 — 2026-07-31
#### 기존 수정 확인
- `REV-P5-002` 수정 후 `pendingAction`은 최종 상세 route가 아니라 `performRecommendationDetailAction(requiresAdultContent: true, action:)`를 다시 실행한다.
- 한국 사용자 기준 인증 전후와 민감 콘텐츠 설정 조합 self-check는 `auth-guard-self-check=passed`로 통과했다.
- `git diff --check`, `git diff --cached --check`, `plutil -lint`를 통과했다.
- `SodaLive-dev``SodaLive` Debug `generic/platform=iOS` 빌드는 모두 종료 코드 0으로 통과했다.
#### 추가 확정 항목
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P5-003` | High | 확정 | 활성 라이브·AI 대화 경로가 인증 후 민감 콘텐츠 설정 guard를 건너뜀 | `Task 5.4` / `P5-R3` |
**관찰 내용**
- `LiveNowAllView.handleLiveNowItemTap`은 한국 사용자의 본인인증이 필요하면 `openLiveDetail` 자체를 `pendingAction`에 저장한다. 인증 성공 callback은 이를 직접 실행하므로 민감 콘텐츠 보기 설정 검사를 다시 수행하지 않는다.
- `CreatorChannelView.handleTalkTap`도 같은 조건에서 `startChat` 자체를 저장하고 인증 성공 후 직접 실행한다.
- 두 화면은 각각 `ContentView``.liveNowAll`, `.creatorDetail` route에서 현재 사용된다.
**근거**
- `LiveNowAllView.swift:148-156`, `:165-190`
- `CreatorChannelView.swift:390-398`, `:520-549`
- `ContentView.swift:198-220`
- 최소 상태 전이 결과: 두 경로 모두 `route=true settings=false`
**영향**
한국 사용자가 본인인증과 민감 콘텐츠 설정 확인을 모두 필요로 하는 상태에서 성인 라이브 또는 AI 대화로 진입하면, 사용자가 비활성화한 민감 콘텐츠 보기 설정이 적용되지 않는다. 활성 보호 경로의 콘텐츠 정책 우회이므로 High로 판정한다.
**권장 조치**
각 화면에서 최종 route를 저장하지 말고 기존 진입 함수를 다시 호출하는 closure를 저장해 인증 성공 후 남은 설정 guard를 통과하게 한다. 별도 공용 guard 계층은 추가하지 않는다.
**계획 전환**
`plan-task.md` Phase 5에 `Task 5.4` / `P5-R3`를 추가했다. 현재 앱 route에서 사용되지 않는 레거시 `HomeView`, `HomeTabView`, `ChatTabView`는 이번 Task 범위에서 제외했다.
#### REV-P5-003 수정 기록 — 2026-07-31
- `LiveNowAllView.handleLiveNowItemTap``pendingAction`이 인증 성공 후 `openLiveDetail(roomId:)`를 직접 실행하지 않고 `handleLiveNowItemTap(roomId:isAdult:)`를 다시 호출하도록 수정했다.
- `CreatorChannelView.handleTalkTap``pendingAction`이 인증 성공 후 `startChat(characterId:)`를 직접 실행하지 않고 `handleTalkTap()`을 다시 호출하도록 수정했다.
- 기존 로그인 guard, 국가 분기, 본인인증 dialog, 민감 콘텐츠 설정 이동, 최종 라이브/AI 대화 route는 변경하지 않았다.
- Swift 상태 전이 self-check로 기존 방식의 `route=true settings=false` 실패를 재현하고, 수정 방식에서 `auth=false/설정=false`, `auth=false/설정=true`, `auth=true/설정=false`, `auth=true/설정=true`, 비로그인, 취소 흐름이 기대 상태를 통과하는 것을 확인했다.
- `rg -n "pendingAction = \\{|handleLiveNowItemTap\\(roomId: roomId, isAdult: isAdult\\)|handleTalkTap\\(\\)|openLiveDetail\\(roomId: roomId\\)|startChat\\(characterId: characterId\\)|contentViewSettings|isAdultContentVisible" "SodaLive/Sources/Live/Now/All/LiveNowAllView.swift" "SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift"` 실행, guard 재진입과 기존 route 유지 확인.
- `git diff --check`, `plutil -lint "SodaLive.xcodeproj/project.pbxproj"`, 미완료 Task 검색을 통과했다.
- `SodaLive-dev`, `SodaLive` Debug `generic/platform=iOS` 빌드는 최초 병렬 실행에서 각각 10분 제한으로 `BUILD INTERRUPTED`됐고, 동일 DerivedData 순차 재시도에서 모두 `BUILD SUCCEEDED`를 확인했다.
- 실제 기기/시뮬레이터의 Bootpay 본인인증 완료 수동 확인은 이번 환경에서 수행하지 못해 상태 전이 self-check와 앱 빌드로 대체했다.
- `SodaLive` 빌드의 범위 밖 V2 파일 duplicate Sources warning은 기존 상태로 남아 있으며 이번 Task에서 변경하지 않았다.
**현재 결론:** `REV-P5-001`, `REV-P5-002`, `REV-P5-003` 수정 완료
### 5차 독립 수정 검증 — 2026-07-31
- `LiveNowAllView.handleLiveNowItemTap``CreatorChannelView.handleTalkTap`의 인증 후 보류 action이 최종 route가 아닌 동일 진입 함수를 재호출하는 것을 확인했다.
- 두 경로의 인증·민감 콘텐츠 설정 조합과 인증 미완료 상태를 독립 self-check로 다시 실행해 `task-5.4-independent-self-check=passed`를 확인했다.
- `auth == false``isAdultContentVisible`을 함께 사용하는 전체 Swift 화면을 재검색했다. 현재 `ContentView`에서 도달 가능한 `MainView`, `LiveNowAllView`, `CreatorChannelView`는 모두 인증 후 설정 guard를 재진입한다.
- `git diff --check`, `git diff --cached --check`, `plutil -lint`를 통과했다.
- `SodaLive-dev``SodaLive` Debug `generic/platform=iOS` 빌드는 모두 종료 코드 0으로 통과했다.
- 수정 범위와 현재 활성 sibling 경로에서 추가 확정 발견 사항 없음.
**현재 결론:** `REV-P5-003` 수정 및 검증 완료

View File

@@ -0,0 +1,82 @@
# Phase 6 홈 탭 shell과 루트 연결 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 6 / Task 6.1~6.3 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 8.2~8.4, `plan-task.md` Phase 6 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- `MainHomeView`가 title/tab shell만 소유하고 추천/랭킹/팔로잉 루트를 조합하는지 확인한다.
- `.home` 연결과 기존 하단 탭바 유지 여부를 포함한다.
## 3. 판정 기준
- 탭별 책임 분리, 추천 API 호출 비소유, `MainTabBarView` 비중복을 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeView.swift:3-111`, `MainView.swift:37-58`, `:175-197`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `find SodaLive/Sources/V2/Main/Home -maxdepth 3 -type f` | 성공 | 세 탭 전용 루트 확인 |
| `rg "MainHomeRecommendationView\|MainHomeRankingView\|MainHomeFollowingView\|fetchRecommendations\|MainTabBarView" ...` | 성공 | shell 책임 분리와 탭바 단일 소유 확인 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. AI 캐릭터 callback의 최종 목적지 오류는 Phase 5와 12가 소유한다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | 세 루트와 MainView 연결 확인 |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P6-001` | Medium | 확정 | 홈 상단 탭 제목이 한글 문자열로 고정되어 앱 언어를 따르지 않음 | `Task 6.4` / `P6-R1` |
#### REV-P6-001 — 홈 상단 탭 제목 현지화 누락
- **관찰 내용:** `MainHomeTab.title`이 추천/랭킹/팔로잉을 각각 한글 문자열 리터럴로 반환한다. 같은 의미의 `I18n.MainContentRecommendation.tabTitle`, `I18n.MainContentRanking.tabTitle`, `I18n.HomeFollowing.tabTitle`이 이미 ko/en/ja 값을 제공하지만 사용하지 않는다.
- **영향:** 앱 언어가 영어 또는 일본어여도 홈 상단 세 탭은 한글로 표시된다.
- **근거:** `MainHomeView.swift:101-108`, `I18n.swift`의 기존 세 탭 `tabTitle`.
- **재현:** 앱 언어를 en 또는 ja로 설정하고 홈 화면을 열면 세 탭 제목이 `추천`, `랭킹`, `팔로잉`으로 유지된다.
- **권장 조치:** 새 문자열을 만들지 말고 기존 I18n 키를 직접 재사용한다.
- **계획 전환:** `plan-task.md` Phase 6에 `Task 6.4` / `P6-R1`을 추가했다.
#### REV-P6-001 수정 기록 — 2026-07-31
- `MainHomeTab.title`의 한글 문자열 리터럴을 기존 `I18n.MainContentRecommendation.tabTitle`, `I18n.MainContentRanking.tabTitle`, `I18n.HomeFollowing.tabTitle`로 교체했다.
- `rg -n 'MainContentRecommendation\.tabTitle|MainContentRanking\.tabTitle|HomeFollowing\.tabTitle' SodaLive/Sources/V2/Main/Home/MainHomeView.swift`로 연결을 확인했다.
- `git diff --check``plutil -lint SodaLive.xcodeproj/project.pbxproj`를 통과했다.
- Oracle 리뷰어 게이트에서 차단/중요 findings 없음 판정을 받았다.
- 실제 ko/en/ja 화면 수동 확인은 잔여 확인 사항으로 남긴다.
**현재 결론:** `REV-P6-001` 수정 완료

View File

@@ -0,0 +1,79 @@
# Phase 7 프로젝트 포함과 중간 빌드 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 7 / Task 7.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | `plan-task.md` Phase 7, 빌드/검증 가이드 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- Phase 4~6 데이터·라우팅·탭 루트 파일의 Xcode target 포함과 현재 빌드 가능성을 확인한다.
## 3. 판정 기준
- project plist 유효성, app target 실제 Swift compile과 최종 build 성공을 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 프로젝트: `SodaLive.xcodeproj/project.pbxproj`
- 환경: macOS 26.0, Xcode 26.0, Any iOS Device / Debug
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `plutil -lint SodaLive.xcodeproj/project.pbxproj` | 성공 | `OK` |
| sandbox 내 `xcodebuild -workspace ... -list` | 환경 실패 | CoreSimulator/캐시 접근 제한으로 workspace 판별 실패 |
| sandbox 밖 `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | app target의 `MainHomeView.swift`, 추천 루트 컴파일 및 `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. 샌드박스 내 실패는 동일 명령의 샌드박스 밖 성공으로 환경 문제임을 확인했다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 프로젝트 포함 확인 | 충족 | 실제 app target compile |
| 후보 판정 | 충족 | 남은 후보 없음 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 기록 | 충족 | 4절 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- Phase 4~6 데이터·루트 파일의 프로젝트 포함과 현재 빌드 기준을 다시 확인했다.
- Phase 7 소유의 추가 확정 발견 사항 없음. 새 Sources 중복은 해당 변경을 만든 Phase 16의 `REV-P16-002`로 전환했다.
### 3차 통합 재점검 — 2026-07-31
- 프로덕션 빌드 경고와 PBX Sources phase를 다시 대조한 결과, Phase 4~6 범위의 `MainHomeRecommendationViewModel.swift`, `MainHomeView.swift`, `MainHomeRecommendationView.swift`, `MainHomeRankingView.swift`, `MainHomeFollowingView.swift``SodaLive` target에 각각 두 번 등록된 사실을 확인했다.
- 같은 패턴이 이후 추천 섹션 파일까지 이어지므로 최종 프로젝트 Gate인 Phase 18의 `REV-P18-001` / `Task 18.3`이 일괄 소유한다.
- 기존 “Phase 16의 신규 중복만 존재” 판정은 불완전했으며, 현재 결론은 `REV-P18-001` 후속 수정 필요다.
### 4차 최종 재점검 — 2026-07-31
- `Task 18.3` 이후 현재 PBX Sources phase를 다시 센 결과 Phase 4~6 범위 파일도 여전히 `SodaLive=2`, `SodaLive-dev=1`이다.
- 격리 DerivedData를 사용한 프로덕션 빌드에서 해당 파일들의 duplicate build warning을 재현했고 빌드는 종료 코드 0으로 완료됐다.
- 잔여 중복은 Phase 18 `REV-P18-002` / `Task 18.4`로 전환했으며, Phase 7 자체의 신규 production 코드 결함은 없다.
### 5차 최종 재점검 — 2026-07-31
- `Task 18.4` 반영 후 `xcodebuild -workspace SodaLive.xcworkspace -list`가 종료 코드 0으로 완료되고 `SodaLive`, `SodaLive-dev` scheme을 확인했다.
- clean DerivedData의 `SodaLive-dev` Debug 전체 빌드가 종료 코드 0으로 완료됐다.
- Phase 4~6 및 홈 추천 범위 대상의 duplicate build warning은 재발하지 않아 Phase 7 추가 확정 발견 사항 없음 판정을 유지한다.

View File

@@ -0,0 +1,61 @@
# Phase 8 현재 라이브 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 8 / Task 8.1 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.1·7.3, `plan-task.md` Phase 8 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 라이브 응답 필드 표시, 빈 상태 숨김과 기존 라이브 입장 흐름 연결을 확인한다.
- 성인 여부 계약 자체는 PRD Open Questions로 제외한다.
## 3. 판정 기준
- `roomId` 전달, `LiveViewModel.enterLiveRoom`, 결제/비밀번호 dialog 재사용 및 빈 배열 미표시를 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `MainHomeLiveSection.swift:3-78`, `MainView.swift:508-523`, `:84-104`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "MainHomeLiveSection\|roomId\|enterLiveRoom\|LivePaymentDialog\|LiveRoomPasswordDialog" ...` | 성공 | 필드와 기존 입장 흐름 연결 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. `HomeLiveItem.isAdult` 제공 여부는 PRD에 담당 주체 확인이 필요한 외부 조건으로 남아 있다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 범위 확인 | 충족 | item→입장 flow 추적 |
| 후보 판정 | 충족 | 코드 결함 후보 없음 |
| 보류 조건 기록 | 충족 | 성인 여부 계약은 PRD Open Questions |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 현재 라이브 섹션의 빈 상태, 프로필 표시와 room callback 연결을 다시 확인했다.
- 추가 확정 발견 사항 없음.

View File

@@ -0,0 +1,122 @@
# Phase 9 배너 섹션 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 9 / Task 9.1~9.4 |
| 기준 commit 또는 working tree | `3ae2dc19d67d0a88bc5043f970e4cfabdfb28b60` + 현재 working tree |
| 리뷰 일자 | 2026-07-30 |
| 리뷰어 | Codex |
| 기준 문서 | PRD 7.1·8.1, `plan-task.md` Phase 9 |
| 리뷰 상태 | 판정 완료 |
## 2. 리뷰 목적과 범위
- 1:1 카드, 폭/inset/간격/radius, 무한 스크롤, 인디케이터, 응답 매핑과 이동 우선순위를 확인한다.
- 이번 리뷰에서 Figma 실화면 캡처 재비교는 제외하고 기존 검증 기록과 코드 수치를 대조한다.
## 3. 판정 기준
- 확정 수치와 navigation 우선순위, 빈 데이터 숨김, 금지 URL 부재를 기준으로 판정한다.
## 4. 검토한 근거와 실행한 검증
- 코드: `BannerCarousel.swift:13-243`, `MainHomeBannerSection.swift`, `MainView.swift:606-627`
- 환경: macOS 26.0, Xcode 26.0, Swift 6.2
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
| `rg "SodaSpacing.s8\|SodaSpacing.s14\|GeometryReader\|currentPage\|visibleItems" BannerCarousel.swift` | 성공 | 간격/radius/인디케이터/순환 구현 확인 |
| `rg "eventItem\|creatorId\|seriesId\|link" MainHomeBannerSection.swift MainView.swift` | 성공 | 첫 non-null 이동 우선순위 확인 |
| 금지 URL 검색 | 성공 | 검색 결과 없음 |
| `xcodebuild ... -derivedDataPath "/private/tmp/SodaLiveDerivedDataReview20260730" build` | 성공 | `BUILD SUCCEEDED` |
## 5. 발견 사항 요약
**확정 발견 사항 없음.**
## 6. 발견 사항 상세
확정 발견 사항 없음. 최신 실기기 시각 회귀는 실행하지 않았으므로 기존 2026-06-29 기록을 근거로 유지한다.
## 7. 확정 항목의 plan·goal 전환
전환 항목 없음.
## 8. 리뷰 종료 판정
| 항목 | 결과 | 근거 |
|---|---|---|
| 코드 범위 확인 | 충족 | carousel과 route 추적 |
| 후보 판정 | 충족 | 남은 코드 후보 없음 |
| 수동 시각 검증 | 기존 증거 사용 | 이번 리뷰에서 재실행 안 함 |
| plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
**최종 결론:** 확정 발견 사항 없음
## 9. 수정 후 검증 기록
### 2차 통합 재점검 — 2026-07-31
- 배너 응답 매핑, 캐러셀 배치·indicator와 상세 이동 우선순위를 다시 확인했다.
- 추가 확정 발견 사항 없음.
### 3차 최종 재점검 — 2026-07-31
| ID | 심각도 | 상태 | 제목 | 소유 Task |
|---|---|---|---|---|
| `REV-P9-001` | Medium | 확정 | 복제 경계 전환 중 연속 스와이프가 carousel index를 유효 범위 밖으로 보낼 수 있음 | `Task 9.5` / `P9-R1` |
#### REV-P9-001 — 무한 캐러셀 경계 연속 스와이프 복구 누락
- **관찰 내용:** 마지막 실데이터 index `items.count`에서 왼쪽 스와이프하면 `items.count + 1`이 되고 0.35초 뒤 `1`로 reset된다. 그러나 `isSwiping`은 다음 main queue에서 바로 해제되며 gesture도 비활성화되지 않아 reset 전에 같은 방향 스와이프가 끝나면 `items.count + 2`가 된다. `wrapIfNeeded()``0``items.count + 1`만 처리하므로 이 상태를 복구하지 못한다.
- **영향:** HStack의 마지막 복제 item보다 바깥 offset과 잘못된 페이지 번호가 유지되어 배너가 빈 상태로 남을 수 있다. 빠른 경계 연속 입력에서만 발생하므로 Medium으로 판정한다.
- **근거:** `BannerCarousel.swift:140-158`, `:179-198`; `visibleItems`의 유효 index는 `0...items.count + 1`이다.
- **재현 또는 검증:** 마지막 실데이터에서 reset 지연 전에 왼쪽 스와이프 종료를 두 번 적용하면 `displayIndex == items.count + 2`가 되며, 첫 실데이터의 오른쪽 연속 스와이프는 `-1`이 된다. 두 값 모두 현재 wrap 조건에 포함되지 않는다.
- **권장 조치:** 전환 reset이 끝날 때까지 추가 gesture·timer index 변경을 막거나, 모든 범위 밖 index를 한 번의 reset 상태로 정규화하는 최소 guard를 추가한다. 크기·간격·timer·callback은 유지한다.
- **계획 전환:** `plan-task.md` Phase 9에 `Task 9.5` / `P9-R1`을 추가했다.
**현재 결론:** `REV-P9-001` 수정 goal 필요
#### REV-P9-001 수정 기록 — 2026-07-31
- `BannerCarousel.wrapIfNeeded()`에서 `displayIndex``0...items.count + 1` 복제 범위로 정규화하도록 보정했다.
- 배너 크기, 간격, 페이지 형식, 5초 자동 스크롤, 탭 callback은 변경하지 않았다.
- Swift 상태 전이 self-check로 기존 조건의 `items.count + 2``-1` 실패를 재현하고, 보정 후 경계 index가 기존 reset 분기로 복구되는 것을 확인했다.
- `rg -n "displayIndex = min\(max\(displayIndex, 0\), items.count \+ 1\)|wrapIfNeeded|currentPage" "SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift"`, `git diff --check`, `plutil -lint`로 검증했다.
- `SodaLive-dev` Debug 빌드는 같은 DerivedData로 2회 실행했지만 Pods/SPM 및 app resource 처리 중 10분 도구 제한으로 `BUILD INTERRUPTED`되어 성공 증거를 확보하지 못했다.
- 실제 기기/시뮬레이터의 빠른 경계 연속 스와이프 시각 확인은 이번 환경에서 수행하지 못해 정적 self-check와 빌드 결과로 대체했다.
**현재 결론:** 코드 수정 완료, 빌드·수동 시각 검증 차단
### 4차 최종 재점검 — 2026-07-31
- **상태:** `REV-P9-001` 확정 / 수정 미완료
- **관찰 내용:** `displayIndex``0...items.count + 1`로 정규화해 같은 방향 연속 입력의 범위 이탈은 막았지만, 경계에서 예약한 `asyncAfter` reset은 실행 시 현재 index를 확인하지 않는다. 마지막 복제 index에서 `1`로 reset을 예약한 뒤 사용자가 반대 방향으로 실제 마지막 index에 돌아와도 이전 reset이 실행되어 현재 index를 `1`로 덮어쓴다. 첫 복제 경계도 대칭으로 같은 문제가 있다.
- **영향:** 빈 배너 지속 문제는 완화됐지만 빠른 양방향 경계 입력에서 사용자가 이동한 페이지와 다른 첫/마지막 페이지로 갑자기 이동한다. `items` 변경 전에 예약된 reset도 새 상태를 덮어쓸 수 있어 Task 9.5의 “안전한 연속 스와이프” 완료 조건을 충족하지 못한다.
- **근거:** `BannerCarousel.swift:179-200`의 두 지연 closure가 조건 없이 `displayIndex`를 대입한다.
- **재현:** 항목 3개 기준 마지막 실제 index `3`에서 왼쪽 입력으로 `4`와 reset을 예약하고, reset 전에 오른쪽 입력으로 `3`에 복귀한다. 상태 전이 self-check 결과는 `beforeDelayedReset=3`, 예약 closure 실행 뒤 `afterDelayedReset=1, expected=3`이다.
- **권장 조치:** 지연 reset 실행 시 `displayIndex`가 예약 당시의 같은 복제 경계에 남아 있는 경우에만 실제 index로 치환한다. 기존 크기·간격·timer·callback과 index 정규화는 유지한다.
- **계획 반영:** 같은 경계 상태를 소유하고 아직 미완료인 `Task 9.5` / `P9-R1`에 반대 방향 입력·데이터 변경 상태 전이와 reset 실행 조건을 추가했다. 중복 회귀 Task는 만들지 않았다.
#### REV-P9-001 최종 수정 기록 — 2026-07-31
- `BannerCarousel.wrapIfNeeded()`에서 지연 reset 예약 시점의 `displayIndex``itemsRevision`을 저장하고, reset 실행 시 두 값이 유지되는 경우에만 실제 index로 치환하도록 수정했다.
- 기존 index 정규화, 배너 크기, 간격, 페이지 형식, 5초 자동 스크롤, 탭 callback은 변경하지 않았다.
- Swift 상태 전이 self-check로 기존 방식의 반대 방향 이동 덮어쓰기 실패를 재현하고, 수정 방식에서 반대 방향 이동과 같은 개수의 `items` 변경은 덮어쓰지 않으며 같은 경계에 남은 경우만 reset되는 것을 확인했다.
- `rg -n "itemsRevision|scheduledIndex|scheduledRevision|wrapIfNeeded|currentPage" "SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift"`, `git diff --check`, `plutil -lint`로 정적 검증했다.
- `SodaLive-dev` Debug 빌드는 iPhoneOS에서 10분 도구 제한으로 반복 `BUILD INTERRUPTED`, Simulator에서 SPM `Promises` 컴파일 단계 실패로 성공 증거를 확보하지 못했다.
- 실제 기기/시뮬레이터의 빠른 경계 연속 스와이프 시각 확인은 이번 환경에서 수행하지 못해 상태 전이 self-check와 정적 검증으로 대체했다.
**현재 결론:** `REV-P9-001` 수정 완료
### 5차 수정 검증 — 2026-07-31
- 마지막·첫 복제 경계의 정상 reset, 반대 방향 이동, 같은 개수의 `items` 교체, 반복 같은 방향 입력을 포함한 상태 전이 self-check가 `banner-reset-self-check=passed`로 통과했다.
- 지연 closure는 `displayIndex == scheduledIndex`, `itemsRevision == scheduledRevision`인 경우에만 실제 index를 대입한다.
- `git diff --check`, `git diff --cached --check`, `plutil -lint`를 통과했다.
- `SodaLive-dev``SodaLive` Debug `generic/platform=iOS` 빌드는 모두 종료 코드 0으로 통과했다.
- 수정 범위에서 추가 확정 발견 사항 없음.
**현재 결론:** `REV-P9-001` 수정 및 검증 완료