# Phase 8 코드 리뷰·QA — 2026-07-29 ## 1. 리뷰 정보 | 항목 | 내용 | |---|---| | 리뷰 대상 | Phase 8 / Comments vertical slice와 `P10-T6` | | 기준 commit 또는 working tree | `dd30e36323543e8f60e9983326503653e8001f12`; 리뷰 시작 시 tracked diff 36개, 전체 변경 169개 | | 리뷰 일자 | 2026-07-29 | | 리뷰어 | Codex | | 기준 문서 | `prd.md`, `api-contract.openapi.json`, `plan-task.md` | | 리뷰 상태 | 판정 완료 | ## 2. 리뷰 목적과 범위 ### 목적 - `COMMENT-001~008`, root/direct reply CRUD·권한·pagination과 screen state를 대조한다. ### 포함 범위 - 코드: `src/features/comments`, Audio detail/Community Sheet 소비 경로 - 테스트: comment contract/thread와 mock E2E - 문서: Phase 8 과거 이력, `P10-T6`, comment OpenAPI operations - 수동 검증: 20개 초과 reply와 empty state 정적 재현 ### 제외 범위 - inactive workspace의 쓰기 차단은 Phase 9에서 교차 판정 ## 3. 판정 기준 심각도는 `Blocker/High/Medium/Low`, 상태는 `확정/오탐/보류/수정 완료`를 사용했다. ## 4. 검토한 근거 ### 문서와 코드 - 요구사항: `COMMENT-001~008`, PRD `10.5` - 계약: root/reply GET `page`, `size`, `totalCount`, `items` - 코드: `CommentThread.tsx:43`, `CommentThread.tsx:46`, `CommentThread.tsx:127`, `CommentThread.tsx:144`, `CommentThread.tsx:155` - 테스트: `comment-contract.test.ts`, `comment-thread.test.tsx`, `tests/e2e/comments.spec.ts` ### 실행 환경 ```text OS: macOS 26.0 Node: v24.12.0 npm: 11.7.0 API mode: injected client/MSW/mock E2E ``` ### 실행한 검증 | 명령 또는 검증 | 결과 | 핵심 증거 | |---|---|---| | `npm run test:run -- src/features/community-posts src/features/fan-talks src/features/comments` | 성공 | 합계 9 files / 52 tests passed | | reply pagination code 대조 | 실패 재현 | reply GET은 항상 `page=0,size=20`; reply pagination control 0건 | | empty response code 대조 | 실패 재현 | total 0에서 empty `PageState` 없이 `총 0개`만 표시 | | mock E2E 통합 실행 | 실패 | 7개 spec 병렬 실행 중 WebKit comments flow 1건 30초 timeout | | WebKit 실패 4건 단일 worker 재실행 | 성공 | 4 passed / 28.4초; comments flow 포함, 단독 재현 실패 | ## 5. 발견 사항 요약 | ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal | |---|---|---|---|---|---| | `REV-P8-001` | High | 수정 완료 | 루트별 답글은 첫 20개만 관리할 수 있다 | `P10-T6` | `P8-R1` | | `REV-P8-002` | Medium | 수정 완료 | 댓글 0건에서 명시적 empty state가 없다 | `P10-T6` | `P8-R1` | ## 6. 발견 사항 상세 ### REV-P8-001 — reply pagination이 없어 21번째 이후 답글에 접근할 수 없다 - **심각도:** High - **상태:** 수정 완료 - **관련 요구사항:** `COMMENT-001`, `COMMENT-006~007` - **관련 계약:** replies GET `page`, `size`, response `totalCount` - **소유 Task:** 신규 `P8-R1` **관찰 내용** `loadReplies`는 항상 `page:0,size:20`으로 호출하고 받은 items만 표시한다. root 목록에만 `ResourcePagination`이 있고 reply section에는 page state/control이 없다. **재현 또는 검증 절차** 1. 하나의 root에 활성 답글 21개 이상인 응답을 준비한다. 2. 답글 보기를 연다. 3. 첫 20개만 보이고 다음 page 요청/버튼이 없다. 4. 요구 결과는 서버 pagination으로 모든 직접 답글에 접근하는 것이다. **영향** 관리자가 21번째 이후 팬·AI 답글을 조회·수정·삭제할 수 없다. **권장 조치** root별 `{page,state}`를 관리하고 reply `ResourcePagination`을 추가한다. page 이동, mutation 후 현재 page refetch, 마지막 item 삭제 경계를 test한다. **판정 기록** - 2026-07-29 — reply query와 렌더 경로 전체 대조로 확정. - 2026-07-30 — `CommentThread`에 root별 reply page state와 reply `ResourcePagination`을 추가하고, 21번째 reply 접근 및 마지막 item 삭제 후 유효 page 복귀 회귀 test로 확인했다. ### REV-P8-002 — 댓글 0건 empty state가 없다 - **심각도:** Medium - **상태:** 수정 완료 - **관련 요구사항:** PRD `10.5` - **관련 계약:** comment page `totalCount=0,items=[]` - **소유 Task:** 신규 `P8-R1` **관찰 내용** root load가 성공하면 item 수와 무관하게 `총 0개`와 빈 container, pagination만 렌더한다. **영향** 운영자가 정상 빈 결과인지 렌더 누락인지 명확히 구분하기 어렵다. **권장 조치** 댓글 전용 empty `PageState`를 추가하고 작성 form은 유지하는 test를 보강한다. **판정 기록** - 2026-07-29 — content branch의 empty 분기 0건으로 확정. - 2026-07-30 — root `totalCount=0`에서 댓글 작성 form은 유지하고 `댓글이 없습니다` empty `PageState`를 표시하도록 회귀 test와 구현을 추가했다. ## 7. 확정 항목의 plan·goal 전환 - `REV-P8-001~002` → `P8-R1` ## 8. 리뷰 종료 판정 | 판정 항목 | 결과 | 근거 | |---|---|---| | 리뷰 범위 전체 확인 | 충족 | comment API/UI/test 대조 | | 후보 항목 판정 완료 | 충족 | 2건 확정 | | 확정 항목 plan 반영 | 충족 | `P8-R1` | | 보류 항목 담당·재개 조건 | 해당 없음 | server integration은 기존 Gate 상태 | | 검증 명령과 결과 기록 | 충족 | §4 | **최종 결론:** 수정 goal 필요. **남은 항목:** Phase 9 inactive/accessibility, 기존 server integration. ## 9. 수정 후 검증 기록 애플리케이션 수정은 아직 하지 않았다. 병렬 통합 실행에서 timeout 난 WebKit 4건을 `--project=webkit --workers=1 --last-failed`로 다시 실행해 4 passed / 28.4초를 확인했다. Comments flow도 2.1초에 통과했으므로 이 timeout은 재현 가능한 Comments 기능 결함으로 확정하지 않았다. ### P8-R1 수정 완료, 2026-07-30 - 무엇을: `CommentThread`의 direct reply 조회를 root별 `{ page, state }`로 관리하고 reply `ResourcePagination`을 추가했다. Root 댓글 0건에서는 create form을 유지한 채 `댓글이 없습니다` empty `PageState`를 표시한다. - 왜: `REV-P8-001`은 reply GET이 항상 `page=0,size=20`이라 21번째 이후 답글에 접근할 수 없었고, `REV-P8-002`는 root 0건에서 정상 빈 결과를 명확히 구분하지 못했다. - 어떻게: - RED: `npm run test:run -- src/features/comments/tests/comment-thread.test.tsx`는 3 tests 중 2 failed였다. 실패 원인은 reply 영역에 `다음 페이지` 버튼이 없고, root 0건에서 `댓글이 없습니다` empty state 없이 pagination이 렌더되는 것이었다. - GREEN: 같은 focused test는 3 tests 통과했다. 회귀는 21개 reply의 page 1 요청·표시, 마지막 reply 삭제 후 page 1 재조회와 page 0 복귀, root empty state와 create form 동시 노출을 확인한다. - 회귀: `npm run test:run -- src/features/comments src/features/audio-contents src/features/community-posts`는 15 files / 87 tests 통과했다. `npm run e2e:mock -- tests/e2e/comments.spec.ts`는 12 tests 중 10 passed / 2 skipped였다. skipped 2건은 기존 WebKit/mobile Safari keyboard-only path 조건이다. - Static/build: `npm run typecheck`, `npm run lint`, `npm run build`는 모두 exit 0이었다. build는 252 modules transformed와 `dist/assets/index-B1Jymnar.js` 492.57 kB(gzip 128.23 kB)를 기록했다. - LSP: `CommentThread.tsx` diagnostics는 clean이었다. `comment-thread.test.tsx` 단일 fresh diagnostics는 3초 timeout됐으나 focused tests와 `typecheck`로 보완했다.