164 lines
7.4 KiB
Markdown
164 lines
7.4 KiB
Markdown
# 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`로 보완했다.
|