# 코드 리뷰 보고서: 커뮤니티 아이템 통일 ## 1. 리뷰 정보 | 항목 | 내용 | |---|---| | 리뷰 대상 | Phase 1~2 / `P1-T1~P2-R4` | | 기준 commit 또는 working tree | `89c070875cbeea8395c1e90cb9f47ff80df5d90f` 이후 미커밋 변경 | | 리뷰 일자 | 2026-08-10 | | 리뷰어 | Sisyphus 구현 검토·Oracle 독립 검토 | | 기준 문서 | `docs/20260810_크리에이터_채널_커뮤니티_아이템_통일/prd.md`, `plan-task.md` | | 리뷰 상태 | `REV-P3-005` 수동 검증 완료·`REV-P3-006` 코드/자동 검증 완료·상태별 더보기 확인 대기 | ## 2. 리뷰 목적과 범위 ### 목적 - 홈·커뮤니티 탭의 아이템과 공지, 상세 진입, 더보기 노출, 목록 구매 제거, 상세 구매 이동·댓글 잠금이 `CCCI-001~012`를 충족하는지 확인한다. - 코드·문서·검증 기록이 실제 working tree와 일치하는지 확인한다. ### 포함 범위 - 코드: `SodaLive/Sources/V2/CreatorChannel/Home/**`, `Community/**`, `CreatorChannelView.swift`의 변경분 - 검증: 정적 contract check, `git diff --check`, `SodaLive-dev` Debug Simulator build - 수동 검증: 부팅된 Simulator와 `EXT-001` 반영 여부 확인 ### 제외 범위 - 백엔드 구현·배포 - 구매 endpoint와 `CommunityPostPurchaseDialog` 내부 UI - `CommunityPostCard`의 메인 홈 사용처 ## 3. 판정 기준 | 심각도 | 기준 | |---|---| | Blocker | 핵심 상세·구매·관리 흐름이 불가능하거나 완료 판정을 무효화함 | | High | 확정 요구사항 또는 API 계약 위반 | | Medium | 제한된 조건의 기능·상태 동기화 문제 | | Low | 문서 정합성 또는 비핵심 유지보수 문제 | | 상태 | 의미 | |---|---| | 확정 | 코드·명령 근거로 문제 확인 | | 오탐 | 요구사항·실행 결과상 문제가 아님 | | 보류 | 외부 계약·환경 반영 후 판정 가능 | | 수정 완료 | 수정과 관련 검증 완료 | ## 4. 검토한 근거 ### 문서와 코드 - 요구사항: `CCCI-001~012`, `DEC-001~016`, `EXT-001` - 계획: `P1-T1~P2-T2`, `P2-R1`, `P2-R2`, `P2-R3`, `P2-R4`, `P1-GATE`, `P2-GATE` - 홈 변환·아이템: `CreatorChannelCommunitySection.swift:3` - 목록 잠금·상세 gesture: `CreatorChannelCommunityListItem.swift`, `CreatorChannelCommunityGridItem.swift` - 상세 구매: `CreatorChannelCommunityPostDetailView.swift:19`, `CreatorChannelCommunityPostDetailViewModel.swift:186` - 메뉴·route·동기화: `CreatorChannelView.swift:480`, `CreatorChannelView.swift:605` ### 실행 환경 ```text OS: macOS 26.0 (25A354) Xcode: 26.0 (17A324) Scheme: SodaLive-dev / Debug Destination: iPhone 17 Pro, iOS 26.0 Simulator Backend: EXT-001 반영 여부를 이 환경에서 확인하지 못함 ``` ### 실행한 검증 | 명령 또는 수동 검증 | 결과 | 핵심 증거 | |---|---|---| | 홈 모델·변환·route·menu `rg` contract check | 성공 | 필드·`communityPostItem`·`CreatorChannelCommunityListItem`·`postId` route match | | 목록 구매 심볼·잠금 배경 `rg` contract check | 성공 | 목록 구매 심볼과 잠금 `Color.gray400` 0건, 목록·상세 `Color.gray800` match | | 상세 구매 `rg` contract check | 성공 | `showPurchaseError` 0건, 상세 dialog·repository 호출 match | | reaction bar 조건부 삽입 `rg` contract check | 성공 | 무조건 삽입 0건, `if !isPaidLocked` 조건부 삽입 2건 | | 공지 상세 전용 구매 `rg` RED/GREEN contract check | 성공 | RED 직접 구매 callback 7건·미연결 생성부 1건, GREEN 기존 callback 0건·상세 연결 match | | 상세 댓글 잠금 `rg` RED/GREEN contract check | 성공 | RED View 직접 조건 2건, GREEN 직접 조건 0건·shared 상태 참조 10건·mutation guard match | | 리스트 더보기 노출 `rg` RED/GREEN contract check | 성공 | RED `else if !isOwnPost` 1건, GREEN 기존 조건 0건·`else if !isPaidLocked` 1건·production diff 한 줄 | | P2-R4 Oracle 독립 코드 리뷰 | 승인 | 네 상태 논리표·공용 호출부·범위 외 회귀·문서 추적성 확인, blocker 0건 | | Oracle 독립 코드 리뷰 | 승인 | 세 상태 시나리오·mutation API 경로·문서 보류 판정 확인, blocker 0건 | | `git diff --check` | 성공 | whitespace 오류 0건 | | `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive-dev -configuration Debug -destination 'platform=iOS Simulator,id=E9FC7721-AA96-440F-8349-2DC4B85F40F5' -derivedDataPath /tmp/sodalive-community-baseline CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES build` | 성공 | exit code 0 | | iPhone 17 Pro iOS 26.0 Simulator build·install·launch·screenshot | 부분 성공 | Simulator build exit 0, 홈 실행 `/tmp/p2-r3-launch.png`; 익명 세션에 유효 상세 데이터가 없어 상태별 상세 검증 대기, 이후 Simulator 종료 | ## 5. 발견 사항 요약 사용자 회귀 제보로 확정한 reaction bar 빈 공간 1건은 수정·빌드를 완료했고, 홈 공지의 상세 전용 구매 원칙 누락 1건을 추가 확정했다. | ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal | |---|---|---|---|---|---| | `REV-P3-001` | High | 수정 완료 | 상세 잠금 버튼이 구매 대신 공통 오류만 표시하던 계획 누락 | `P1-T3` | 완료 | | `REV-P3-002` | High | 보류 | 홈 API 신규 필드와 런타임 동작의 통합 검증 필요 | `P3-GATE`, `EXT-001` | 외부 반영 후 재개 | | `REV-P3-003` | Medium | 수정 완료 | 하트·댓글이 숨겨져도 빈 reaction bar가 24pt 공간을 차지함 | `P2-R1` | 완료 | | `REV-P3-004` | High | 수정 완료 | 홈 공지에 목록 구매 callback이 남고 상세 callback이 연결되지 않음 | `P2-R2` | 완료 | | `REV-P3-005` | High | 수정·수동 검증 완료 | 유료 미구매 상세가 댓글 UI와 mutation을 허용함 | `P2-R3` | 완료 | | `REV-P3-006` | Medium | 코드·자동 검증 완료·수동 대기 | 유료 미구매 타인 게시글에도 더보기가 표시됨 | `P2-R4` | 홈·커뮤니티 상태별 확인 | ## 6. 발견 사항 상세 ### REV-P3-001 — 상세 구매 연결 누락 - **심각도:** High - **상태:** 수정 완료 - **관련 요구사항:** `CCCI-004`, `CCCI-007` - **소유 Task:** `P1-T3` 기준선에서 상세 잠금 영역은 `showPurchaseError()`만 호출해 목록 구매 제거 후 실제 구매가 불가능했다. 코드 수정 전에 PRD `DEC-012`와 계획 `P1-T3`를 추가하고, 기존 `CommunityPostPurchaseDialog`·`CreatorCommunityRepository.purchaseCommunityPost(postId:)`를 상세 View·ViewModel로 이동했다. 정적 contract check와 Debug build가 통과했다. ### REV-P3-002 — 백엔드·Simulator 통합 검증 대기 - **심각도:** High - **상태:** 보류 - **관련 요구사항:** `CCCI-001~008`, `EXT-001` - **소유 Task:** `P3-GATE` 백엔드가 홈 API `notices`·`communities`에 `isCommentAvailable`·`isPinned`을 제공해야 하며, 실제 터치·메뉴·구매 흐름은 부팅된 Simulator에서 확인해야 한다. 현재 Simulator는 모두 종료 상태다. 백엔드 반영과 Simulator 부팅 후 홈·리스트·썸네일·상세 흐름을 재검증한다. ### REV-P3-003 — 표시할 reaction이 없을 때 빈 공간 유지 - **심각도:** Medium - **상태:** 수정 완료 - **관련 요구사항:** `CCCI-009` - **소유 Task:** `P2-R1` `CreatorChannelCommunityListItem`과 `CreatorChannelCommunityPostDetailContentView`는 유료 잠금 상태에서 하트·댓글 자식만 숨기고, body에는 `reactionBar`를 항상 삽입했다. 그 결과 빈 `HStack`의 `.frame(height: 24)`와 부모 `VStack` spacing이 홈·커뮤니티 리스트·상세에 남았다. 코드 수정 전에 `P2-R1`을 추가하고, 두 body에서 기존 `isPaidLocked`로 reaction bar 전체를 조건부 제외했다. focused contract check·`git diff --check`·Debug build가 통과했다. ### REV-P3-004 — 홈 공지의 상세 전용 구매 원칙 누락 - **심각도:** High - **상태:** 수정 완료 - **관련 요구사항:** `CCCI-010` - **소유 Task:** `P2-R2` `CreatorChannelNoticeSection`은 `onTapNotice`·`onTapComment`·`onTapPurchase`를 각각 기본 no-op으로 선언하고 잠금 영역에 별도 구매 `Button`을 뒀다. `CreatorChannelHomeView`는 notices만 전달해 상세 callback을 연결하지 않았다. 코드 수정 전에 `P2-R2`를 추가하고, 세 callback을 단일 `onTapDetail`로 통일해 기존 `onTapCommunityDetail` route에 연결했다. 잠금 구매 `Button` wrapper만 제거했으며 카드 외형과 상세 구매·refresh 구현은 변경하지 않았다. focused RED/GREEN contract check·`git diff --check`·Debug build가 통과했다. ### REV-P3-005 — 유료 미구매 상세의 댓글 접근 허용 - **심각도:** High - **상태:** 수정·수동 검증 완료 - **관련 요구사항:** `CCCI-011` - **소유 Task:** `P2-R3` 상세 View와 ViewModel이 `isCommentAvailable`만 확인해 타인 유료 미구매 게시글에서도 댓글 구분선·목록·입력창과 작성·수정·삭제 mutation을 허용했다. 기존 유료 잠금 조건과 `isCommentAvailable`을 결합한 `isCommentInteractionAvailable`을 추가하고 UI 삽입·페이지네이션·전송·편집·삭제·`applyDetail` 초기화에 재사용했다. 정적 contract check·device/Simulator Debug build가 통과했고 사용자가 상태별 상세 동작을 수동 확인했다. ### REV-P3-006 — 유료 미구매 타인 게시글의 더보기 노출 - **심각도:** Medium - **상태:** 코드·자동 검증 완료·수동 대기 - **관련 요구사항:** `CCCI-012` - **소유 Task:** `P2-R4` 공용 리스트 아이템의 방문자 분기가 `else if !isOwnPost`만 확인해 유료 미구매 타인 게시글에도 더보기를 표시했다. 기존 `isPaidLocked`의 반대 조건으로 분기를 교체해 홈·커뮤니티 리스트에 함께 적용했다. production diff는 한 줄이며 정적 contract check·`git diff --check`·Debug build가 통과했다. ## 7. 확정 항목의 plan·goal 전환 - `REV-P3-001`: 코드 수정 전에 `P1-T3`로 전환했고 수정·빌드를 완료했다. - `REV-P3-003`: 코드 수정 전에 `P2-R1`로 전환했고 수정·검증을 완료했다. - `REV-P3-004`: 코드 수정 전에 `P2-R2`로 전환했고 수정·검증을 완료했다. - `REV-P3-005`: 코드 수정 전에 `P2-R3`로 전환했고 코드·자동·상태별 수동 검증을 완료했다. - `REV-P3-006`: 코드 수정 전에 `P2-R4`로 전환했고 코드·자동 검증을 완료했다. 상태별 더보기 수동 확인은 대기한다. - `REV-P3-002`: 외부 조건이므로 `P3-GATE` 완료를 보류한다. ## 8. 리뷰 종료 판정 | 판정 항목 | 결과 | 근거 | |---|---|---| | 리뷰 범위 전체 확인 | 충족 | 사용자 회귀 제보와 수정 결과를 추가 반영 | | 후보 항목 판정 완료 | 충족 | `REV-P3-001`·`REV-P3-003`·`REV-P3-004` 수정 완료, `REV-P3-002` 보류 | | 확정 항목 plan 반영 | 충족 | `P1-T3`, `P2-R1`, `P2-R2`, `DEC-012~014` | | 보류 항목의 담당·재개 조건 기록 | 충족 | `EXT-001`·부팅된 Simulator | | 검증 명령과 결과 기록 | 충족 | 정적 계약·Debug build exit 0 | **최종 결론:** 상세 댓글 차단 상태별 검증 완료, 더보기 노출 조건 코드·자동 검증 완료·상태별 수동 확인 대기 **남은 항목:** 홈·커뮤니티 리스트에서 더보기 네 상태를 수동 확인하고, 백엔드 `EXT-001` 반영 후 나머지 통합 흐름을 확인한다. ## 9. 수정 후 검증 기록 ### 1차 수정 검증 — 2026-08-10 - 무엇을: `REV-P3-001`의 상세 구매 누락을 수정했다. - 왜: 목록 구매 제거 후에도 사용자가 상세에서 실제 구매할 수 있어야 한다. - 어떻게: - 상세 구매 contract check — 성공 - `SodaLive-dev` 단일 Simulator Debug build — exit code 0 - 남은 항목: `REV-P3-002` ### 2차 수정 검증 — 2026-08-10 - 무엇을: `REV-P3-003`의 빈 reaction bar 공간을 제거했다. - 왜: 하트·댓글을 모두 숨기는 유료 잠금 상태에서 고정 24pt 높이와 부모 spacing까지 없어져야 한다. - 어떻게: - 무조건 `reactionBar` 삽입 contract check — 0건 - `if !isPaidLocked` 조건부 삽입 contract check — 2건 - `git diff --check` — 성공 - `SodaLive-dev` 단일 Simulator Debug build — exit code 0 - 남은 항목: `REV-P3-002`와 Simulator 시각 검증 ### 3차 수정 검증 — 2026-08-10 - 무엇을: `REV-P3-004`의 홈 공지 직접 구매·상세 미연결을 수정했다. - 왜: 공지도 커뮤니티 게시글이므로 목록이 아닌 상세에서만 구매해야 한다. - 어떻게: - RED: 직접 구매 callback 7건, 홈 상세 callback 미연결 생성부 1건 확인 - GREEN: 기존 callback 0건, 단일 `onTapDetail`·홈 `onTapCommunityDetail` 연결 확인 - 기존 상세 구매 dialog/API와 구매 후 상세·홈·커뮤니티 refresh 연결 유지 확인 - `git diff --check` — 성공 - `SodaLive-dev` Debug build — exit code 0 - 남은 항목: `REV-P3-002`와 Simulator 통합 검증 ### 4차 수정 검증 — 2026-08-10 - 무엇을: `REV-P3-005`의 유료 미구매 상세 댓글 UI·mutation 허용을 차단했다. - 왜: 구매 전 방문자는 reaction뿐 아니라 댓글 영역과 작성·수정·삭제 API에도 접근할 수 없어야 한다. - 어떻게: - RED: 상세 View의 `isCommentAvailable` 직접 조건 2건과 paid-lock 미반영 ViewModel guard 확인 - GREEN: View 직접 조건 0건, shared 상태 참조 10건, mutation guard match - `git diff --check` — 성공 - device와 iPhone 17 Pro Simulator 대상 `SodaLive-dev` Debug build — 각각 exit code 0 - Simulator build·install·launch·홈 캡처 후 종료 - 남은 항목: 익명 세션에서 확보하지 못한 유효 유료 미구매·구매 완료·본인 게시글 기반 수동 검증, `REV-P3-002` - 독립 리뷰: Oracle `APPROVE`, blocker 0건. 상태별 상세 fixture 부재는 문서화된 잔여 수동 검증 위험으로 판정했다. ### 5차 수정 검증 — 2026-08-10 - 무엇을: `REV-P3-006`의 유료 미구매 타인 게시글 더보기 노출을 차단했다. - 왜: 구매 전 타인 게시글은 관리·신고 더보기 진입점을 표시하지 않아야 한다. - 어떻게: - RED: `else if !isOwnPost` 1건 확인 - GREEN: 기존 조건 0건, `else if !isPaidLocked` 1건, production diff 한 줄 - `git diff --check` — 성공 - `SodaLive-dev` Debug build — exit code 0 - 남은 항목: 홈·커뮤니티 탭에서 유료 미구매 타인 숨김, 무료·구매 완료·작성자 표시 수동 확인 - 독립 리뷰: 최초 문서 상태·추적 범위 2건을 수정한 뒤 Oracle 재검토 `APPROVE`, blocker 0건.