# Phase 15 커뮤니티 구매·상세 메뉴 리뷰 보고서 ## 1. 리뷰 정보 | 항목 | 내용 | |---|---| | 리뷰 대상 | Phase 15 / `P15-T1`~`P15-T5`, `P15-GATE` | | 기준 commit 또는 working tree | `02cd583f` 기반 미커밋 working tree | | 리뷰 일자 | 2026-08-10 | | 리뷰어 | Codex, 독립 재검토 `phase15_review` | | 기준 문서 | `prd.md` F0-20260810, `plan-task.md` Phase 15 | | 리뷰 상태 | 수정 검증 완료 | ## 2. 리뷰 목적과 범위 ### 목적 - Phase 15의 미체크 항목이 단순 체크 누락인지 미구현인지 확인한다. - PRD의 홈 Response, 유료 미구매 표시, 상세 메뉴 권한, 상위 새로고침 요구사항을 코드·테스트·Gate와 대조한다. ### 포함 범위 - 코드: Phase 15 파일 구조에 나열된 홈, 커뮤니티 탭, 상세, 상위 Activity, action 파일 - 테스트: Phase 15 focused local unit/source contract 테스트 - 문서: `prd.md` F0-20260810, `plan-task.md` Phase 15 - 수동 검증: 코드/diff 정적 검토 ### 제외 범위 - `app/src/androidTest`, 기기·에뮬레이터 UI 조작, 스크린샷·시각 QA - 서버 Response 실제 배포 여부와 외부 API 운영 상태 ## 3. 판정 기준 | 심각도 | 기준 | |---|---| | Blocker | 보안·데이터 손실, 핵심 흐름 불능 | | High | 확정 PRD/API 계약 위반 또는 주요 회귀 | | Medium | 제한 조건의 기능·접근성·복구 문제 | | Low | 유지보수성, 문서 정합성, style Gate 문제 | ## 4. 검토한 근거 ### 문서와 코드 - 요구사항: PRD `Home Response Contract`, `Home Community Requirements`, `Community Tab Requirements`, `Detail Purchase and Badge Requirements`, `Detail More Menu Requirements`, `Change Propagation Requirements` - 계획: `P15-T1`~`P15-T5`, `P15-GATE` - 코드/테스트: Phase 15 파일 구조와 각 Task에 명시된 파일 ### 실행 환경 ```text OS: Darwin 25.0.0 x86_64 Java: OpenJDK 17.0.15 Android/Gradle: 저장소 wrapper 및 debug variant 민감 환경 값: 기록하지 않음 ``` ### 실행한 검증 | 명령 | 결과 | 핵심 증거 | |---|---|---| | 홈 focused unit test | 성공 | `BUILD SUCCESSFUL` | | 커뮤니/action focused unit test | 성공 | `BUILD SUCCESSFUL` | | `CreatorChannelActivitySourceTest` | 성공 | `BUILD SUCCESSFUL` | | `./gradlew :app:mergeDebugResources` | 성공 | `BUILD SUCCESSFUL` | | `./gradlew :app:compileDebugKotlin` | 성공 | `BUILD SUCCESSFUL` | | `./gradlew :app:ktlintCheck` | 실패 | `CreatorChannelActivity.kt:29:1 Unused import` 1건 | | `git diff --check` | 성공 | 출력 없음 | | 수정 후 전체 focused test | 성공 | `BUILD SUCCESSFUL` | | 수정 후 resource/compile/ktlint Gate | 성공 | `BUILD SUCCESSFUL` | | 수정 후 `git diff --check`, `git diff HEAD --check` | 성공 | 출력 없음 | ## 5. 발견 사항 요약 | ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal | |---|---|---|---|---|---| | `REV-P15-001` | Low | 수정 완료 | 목록 메뉴 제거 후 미사용 import가 style Gate를 막음 | `P15-R1` | 미사용 import 1줄 제거 및 Gate 재검증 | | `REV-P15-002` | High | 수정 완료 | 홈 reaction id가 작성자 row에 연결됨 | `P15-R2` | reaction id를 실제 하트·댓글 row로 이동 및 Gate 재검증 | | `REV-P15-003` | High | 수정 완료 | 공유 FeedItem 잠금 정책이 다른 홈 화면 reaction을 숨김 | `P15-R3` | 크리에이터 채널 홈만 잠금 숨김 정책을 선택하고 공유 기본값 회귀 검증 | | `REV-P15-004` | Low | 수정 완료 | 메인 홈 회귀 테스트 파일의 미사용 import가 style Gate를 막음 | `P15-R4` | 미사용 import 1줄 제거 및 Gate 재검증 | ## 6. 발견 사항 상세 ### REV-P15-001 — 목록 메뉴 제거 후 미사용 import가 style Gate를 막음 - **심각도:** Low - **상태:** 수정 완료 - **관련 요구사항:** `P15-T5` orphan 제거, `P15-GATE` - **소유 Task:** `P15-R1` **관찰 내용** `CreatorChannelActivity`의 목록 고정/삭제 로직을 제거한 후 `SharedPreferenceManager` import가 남아 `ktlintCheck`가 실패했다. **근거** - 코드: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt:29` - 검증: `./gradlew :app:ktlintCheck` 결과 `Unused import (no-unused-imports)` - 문서: `plan-task.md` `P15-T5`, `P15-GATE` **영향** 기능 동작에는 영향이 없으나 Phase 15 style Gate를 통과할 수 없다. **권장 조치** `SharedPreferenceManager` import 1줄만 제거하고 `ktlintCheck`와 Phase 15 Gate를 재실행한다. **판정 기록** - 2026-08-10 — 기능 테스트·resource merge·compile은 통과했고 ktlint 정확한 1건으로 확정했다. - 2026-08-10 — `SharedPreferenceManager` import 1줄 제거 후 최종 `ktlintCheck`와 Phase 15 Gate가 PASS했다. ### REV-P15-002 — 홈 reaction id가 작성자 row에 연결됨 - **심각도:** High - **상태:** 수정 완료 - **관련 요구사항:** PRD `Home Community Requirements`, `P15-T2` - **소유 Task:** `P15-R2` **관찰 내용** `FeedCommunityView.bind()`는 `ll_feed_community_reaction`에 `item.showReaction`을 적용하지만, XML의 해당 id가 프로필·닉네임·작성 시간 row에 붙어 있다. 실제 하트·댓글 row에는 id가 없다. **근거** - XML: `view_feed_community.xml:36-41`의 `ll_feed_community_reaction` 아래에 `iv_feed_community_profile`이 있음 - XML: `view_feed_community.xml:154-195`의 실제 reaction row에 id가 없음 - 코드: `FeedCommunityView.kt:51`, `FeedCommunityView.kt:81` **재현 또는 검증 절차** 1. `price > 0 && existOrdered == false`인 홈 item의 `showReaction == false`를 확인한다. 2. `FeedCommunityView.bind()`가 `ll_feed_community_reaction.isVisible = false`를 적용한다. 3. XML tree에서 해당 id가 작성자 row를 가리키므로 작성자 row가 숨겨진다. 4. 실제 reaction row는 자신의 visibility binding을 받지 않아 하트·댓글 UI가 남는다. **영향** 유료 미구매 홈 item이 PRD와 반대로 작성자 영역을 숨기고 reaction UI를 노출한다. **권장 조치** `ll_feed_community_reaction` id를 작성자 row에서 실제 하트·댓글 row로 이동한다. **판정 기록** - 2026-08-10 — XML tree와 binding 대상을 대조해 PRD 2번을 재현하는 실제 결함으로 확정했다. - 2026-08-10 — id를 실제 하트·댓글 reaction row로 이동하고 focused/resource/compile 및 최종 Gate PASS를 확인했다. ### REV-P15-003 — 공유 FeedItem 잠금 정책이 다른 홈 화면 reaction을 숨김 - **심각도:** High - **상태:** 수정 완료 - **관련 요구사항:** PRD `Edge Cases`, `Constraints & Dependencies`, `P15-T1`~`P15-T2` - **소유 Task:** `P15-R3` **관찰 내용** `FeedItem.Community.showReaction = !isLocked`와 공유 `FeedCommunityView.bind()` 조합이 크리에이터 채널 홈뿐 아니라 메인 홈 인기 커뮤니티에도 적용된다. 메인 홈 mapper도 `price`, `existOrdered`를 전달하므로 유료 미구매 item의 기존 reaction 표시가 함께 사라진다. **근거** - 공유 정책: `FeedItem.kt`의 `showReaction`, `showComment` - 다른 호출부: `HomeRecommendationMappers.kt:84-103`이 `price`, `existOrdered`를 전달 - 공유 View 사용: `HomePopularCommunityAdapter.kt:49`의 `view.bind(item.item)` - 문서: PRD `다른 화면에서 사용하는 FeedItem.Community는 신규 필드 기본값으로 기존 표시 동작을 유지한다.` **영향** 요구 범위 밖인 메인 홈 인기 커뮤니티의 유료 미구매 item 표시가 변경되어 기존 화면 회귀가 발생한다. **권장 조치** 공유 모델의 신규 정책 기본값은 기존 reaction 표시를 유지하고, 크리에이터 채널 홈 mapper만 잠금 reaction 숨김을 명시적으로 선택한다. **판정 기록** - 2026-08-10 — 전체 `FeedItem.Community` production 생성자와 공유 View 호출부를 추적해 실제 범위 확장 결함으로 확정했다. - 2026-08-10 — 기본값은 기존 reaction 표시를 유지하고 크리에이터 채널 홈만 opt-in하도록 수정했다. 공유 모델·메인 홈 mapper 회귀 테스트와 최종 Gate가 PASS했다. ### REV-P15-004 — 메인 홈 회귀 테스트 파일의 미사용 import가 style Gate를 막음 - **심각도:** Low - **상태:** 수정 완료 - **관련 요구사항:** `P15-R3`, `P15-GATE` - **소유 Task:** `P15-R4` **관찰 내용** 메인 홈 mapper 회귀 테스트 추가로 `HomeMainFragmentLayoutTest`가 다시 lint 대상이 되면서 사용처가 없는 `ColorDrawable` import 1건이 확인됐다. **근거** - 코드: `HomeMainFragmentLayoutTest.kt:6` - 검증: `./gradlew :app:ktlintCheck` 결과 `Unused import` **영향** 기능 동작에는 영향이 없으나 Phase 15 style Gate가 실패한다. **권장 조치** 사용처가 없는 `ColorDrawable` import 1줄만 제거한다. **판정 기록** - 2026-08-10 — `rg`로 import 외 사용처가 없음을 확인해 정확한 style Gate 결함으로 확정했다. - 2026-08-10 — `ColorDrawable` import 1줄 제거 후 `ktlintCheck`와 최종 focused test Gate가 PASS했다. ## 7. 확정 항목의 plan·goal 전환 - `plan-task.md` Phase 15에 `Task 15.R1` / goal `P15-R1`, `Task 15.R2` / goal `P15-R2`, `Task 15.R3` / goal `P15-R3`, `Task 15.R4` / goal `P15-R4`를 추가했다. - 각 항목의 대상 파일, 최소 수정 범위, 회귀 검증, 완료 증거를 명시했다. ## 8. 리뷰 종료 판정 | 판정 항목 | 결과 | 근거 | |---|---|---| | 리뷰 범위 전체 확인 | 충족 | PRD/plan·production/test/diff 대조 | | 후보 항목 판정 완료 | 충족 | `REV-P15-001`~`REV-P15-004` 확정 | | 확정 항목 plan 반영 | 충족 | `P15-R1`~`P15-R4` | | 보류 항목 기록 | 해당 없음 | 없음 | | 검증 명령과 결과 기록 | 충족 | `P15-R1`~`P15-R4` 수정 후 최종 Gate PASS | **최종 결론:** 수정 검증 완료 **남은 항목:** 없음. 기기·에뮬레이터 수동 UI 확인과 실제 서버 Response 배포 상태는 리뷰 범위 밖이다. ## 9. 수정 후 검증 기록 - `REV-P15-001`: `CreatorChannelActivity`의 미사용 `SharedPreferenceManager` import 1줄을 제거했다. - `REV-P15-002`: `ll_feed_community_reaction` id를 프로필 row에서 실제 하트·댓글 row로 이동했다. - `REV-P15-003`: `FeedItem.Community.hideReactionWhenLocked` 기본값을 `false`로 두고 크리에이터 채널 홈 mapper만 `true`를 전달했다. 메인 홈 인기 커뮤니티 잠금 item의 기존 reaction 표시 회귀 테스트를 추가했다. - `REV-P15-004`: `HomeMainFragmentLayoutTest`의 미사용 `ColorDrawable` import 1줄을 제거했다. - 최종 전체 focused test: `BUILD SUCCESSFUL`. - 최종 `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`: `BUILD SUCCESSFUL`. - 최종 `git diff --check`, `git diff HEAD --check`: 출력 없이 PASS. - 정적 재검토: 구매 호출은 상세에만 존재하고, 홈·커뮤니티 item은 상세 이동만 수행하며, 상세 배지 제거·권한별 메뉴·홈+커뮤니티 refresh 계약이 유지됨을 확인했다. - 독립 재검토: Critical/Minor 없음, Important 1건(`REV-P15-003`)을 수정 반영했다. - 수정 후 독립 최종 재검토: `REV-P15-001`~`REV-P15-004` 반영을 확인했고 새 Critical/Important/Minor 이슈 없음으로 판정했다.