# Phase 6 코드 리뷰·QA — 2026-07-29 ## 1. 리뷰 정보 | 항목 | 내용 | |---|---| | 리뷰 대상 | Phase 6 / Community vertical slice와 `P10-T4` | | 기준 commit 또는 working tree | `dd30e36323543e8f60e9983326503653e8001f12`; 리뷰 시작 시 tracked diff 36개, 전체 변경 169개 | | 리뷰 일자 | 2026-07-29 | | 리뷰어 | Codex | | 기준 문서 | `prd.md`, `api-contract.openapi.json`, `plan-task.md` | | 리뷰 상태 | 판정 완료 | ## 2. 리뷰 목적과 범위 ### 목적 - `COMMUNITY-001~015`, Community pagination/media/price와 Sheet mutation을 대조한다. ### 포함 범위 - 코드: `src/features/community-posts` - 테스트: Community contract/form/list/sheet - 문서: Phase 6, `P10-T4`, OpenAPI Community request - 수동 검증: price raw input과 payload 계산 ### 제외 범위 - Comments 내부 pagination은 Phase 8, inactive comment mutation은 Phase 9 ## 3. 판정 기준 심각도는 `Blocker/High/Medium/Low`, 상태는 `확정/오탐/보류/수정 완료`를 사용했다. ## 4. 검토한 근거 ### 문서와 코드 - 요구사항: `COMMUNITY-007`, `COMMUNITY-012`, PRD `14.1` - 코드: `community-post-form-helpers.ts:11`, `community-post-form-helpers.ts:12`, `CommunityPostForm.tsx:99`, `CommunityPostForm.tsx:122`, `CommunityPostForm.tsx:142` - 테스트: `community-contract.test.ts`, `community-form.test.tsx`, `community-list.test.tsx`, `community-sheet.test.tsx` ### 실행 환경 ```text OS: macOS 26.0 Node: v24.12.0 npm: 11.7.0 API mode: injected client/MSW ``` ### 실행한 검증 | 명령 또는 검증 | 결과 | 핵심 증거 | |---|---|---| | `npm run test:run -- src/features/community-posts src/features/fan-talks src/features/comments` | 성공 | 합계 9 files / 52 tests passed | | 가격 parser `node` 계산 | 실패 재현 | `-1 → 1`, `1.5 → 15` | | pagination contract 대조 | 성공 | server `totalCount/page/size/hasNext/items` 사용 | ## 5. 발견 사항 요약 | ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal | |---|---|---|---|---|---| | `REV-P6-001` | High | 수정 완료 | Community 가격의 음수·소수를 거부하지 않고 다른 값으로 저장한다 | `P10-T7` | `P6-R1` | ## 6. 발견 사항 상세 ### REV-P6-001 — Community 금지 가격 입력이 유효한 다른 값으로 변환된다 - **심각도:** High - **상태:** 확정 - **관련 요구사항:** `COMMUNITY-007`, PRD `14.1` - **관련 계약:** create `price` integer `0..99999` - **소유 Task:** 신규 `P6-R1` **관찰 내용** 입력 parser가 숫자가 아닌 문자를 전부 제거한다. `-1`은 `1캔`, `1.5`는 `15캔`으로 바뀌고 form validation을 통과한다. **재현 또는 검증 절차** 1. Community create form 가격에 `-1` 또는 `1.5`를 입력한다. 2. input은 각각 `1캔`, `15캔`으로 바뀐다. 3. 필수 필드를 채워 제출하면 거부 대신 해당 양수 정수가 전송된다. 4. 요구 결과는 음수·소수 제출 전 거부다. **영향** 운영자 의도와 다른 유료 가격이 저장될 수 있다. **권장 조치** Audio와 동일한 raw-price validation primitive를 사용해 부호·소수점 입력을 invalid로 유지하고 form-level `-1`, `1.5` request 0건 test를 추가한다. **판정 기록** - 2026-07-29 — parser 계산과 submit serializer를 대조해 확정. - 2026-07-30 — `P6-R1`에서 raw CAN price parser를 shared로 분리하고 Community form이 `-1`, `1.5`를 원문 유지 오류로 차단하도록 수정 완료. ## 7. 확정 항목의 plan·goal 전환 - `REV-P6-001` → `P6-R1` ## 8. 리뷰 종료 판정 | 판정 항목 | 결과 | 근거 | |---|---|---| | 리뷰 범위 전체 확인 | 충족 | Community code/test/contract 대조 | | 후보 항목 판정 완료 | 충족 | 1건 확정 | | 확정 항목 plan 반영 | 충족 | `P6-R1` | | 보류 항목 담당·재개 조건 | 해당 없음 | 보류 없음 | | 검증 명령과 결과 기록 | 충족 | §4 | **최종 결론:** 수정 goal 필요. **남은 항목:** Phase 8 reply pagination, Phase 9 inactive/accessibility. ## 9. 수정 후 검증 기록 ### P6-R1 Community raw price validation 복구, 2026-07-30 - 무엇을: Community create 가격 입력에서 음수·소수 raw input을 다른 숫자로 변형하지 않고 제출 전에 차단했다. - 왜: `-1 → 1캔`, `1.5 → 15캔`처럼 운영자 의도와 다른 유료 가격이 저장될 수 있었기 때문이다. - 어떻게: - RED: `npm run test:run -- src/features/community-posts/tests/community-price-validation.test.tsx`는 3 failed로, `-1`/`1.5`가 오류 없이 양수 캔 가격으로 변형됨을 확인했다. - GREEN: `parseCanPriceInput`/`formatCanPriceInput`을 `src/shared/validation/can-price.ts`에 추가하고 Audio/Community helper가 같은 raw price 규칙을 쓰게 했다. Community form은 빈 가격만 optional로 허용하고, 입력이 있는데 parse 실패하면 inline 오류와 POST 0건으로 막는다. - 검증: focused `npm run test:run -- src/shared/validation/can-price.test.ts src/features/community-posts/tests/community-price-validation.test.tsx`는 2 files / 15 tests 통과했다. `npm run test:run -- src/shared/validation/can-price.test.ts src/features/community-posts`는 6 files / 51 tests 통과했고, `npm run typecheck`, `npm run lint`, `npm run build`도 모두 exit 0이었다. 남은 항목: Phase 8 reply pagination과 Phase 9 inactive/accessibility는 후속 범위로 유지한다.