7.9 KiB
Phase 5 코드 리뷰·QA — 2026-07-29
1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 5 / Series vertical slice와 P10-T3 |
| 기준 commit 또는 working tree | dd30e36323543e8f60e9983326503653e8001f12; 리뷰 시작 시 tracked diff 36개, 전체 변경 169개 |
| 리뷰 일자 | 2026-07-29 |
| 리뷰어 | Codex |
| 기준 문서 | prd.md, api-contract.openapi.json, plan-task.md |
| 리뷰 상태 | 판정 완료 |
2. 리뷰 목적과 범위
목적
SERIES-001~018, Series image/file 계약과 Phase 5/P10 후속 구현을 대조한다.
포함 범위
- 코드:
src/features/series - 테스트: Series contract/form/route/contents/order
- 문서: Phase 5,
P10-T3, OpenAPI Series request - 수동 검증: update payload와 initial state 비교
제외 범위
- inactive workspace 교차 정책은 Phase 9, 공통 crop은 Phase 1
3. 판정 기준
심각도는 Blocker/High/Medium/Low, 상태는 확정/오탐/보류/수정 완료를 사용했다.
4. 검토한 근거
문서와 코드
- 요구사항:
SERIES-004,SERIES-007,SERIES-010,FILE-002 - 코드:
SeriesForm.tsx:77,SeriesForm.tsx:127,series-schema.ts:28,series-schema.ts:35,series-image-policy.ts:14 - 테스트:
series-form.test.tsx,series-contract.test.ts
실행 환경
OS: macOS 26.0
Node: v24.12.0
npm: 11.7.0
API mode: injected client/MSW
실행한 검증
| 명령 | 결과 | 핵심 증거 |
|---|---|---|
npm run test:run -- src/features/audio-contents src/features/series |
성공 | 합계 11 files / 46 tests passed |
| edit payload 정적 대조 | 실패 재현 | 초기 state와 같아도 request에 state 포함 |
| image policy 대조 | 실패 재현 | extension/MIME 독립 allowlist |
5. 발견 사항 요약
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|---|---|---|---|---|---|
REV-P5-001 |
High | 수정 완료 | 변경하지 않은 Series state를 항상 전송한다 |
P10-T3 |
P5-R1 |
REV-P5-002 |
Medium | 수정 완료 | Series mutation schema가 isActive=true/null, state=null을 허용한다 |
P10-T3 |
P5-R1 |
REV-P5-003 |
Medium | 수정 완료 | Series image 확장자와 MIME 불일치가 통과한다 | P10-T3 |
P5-R2 |
6. 발견 사항 상세
REV-P5-001 — 변경 없는 Series state가 update payload에 포함된다
- 심각도: High
- 상태: 확정
- 관련 요구사항:
SERIES-004, PRD14.1 - 관련 계약:
SeriesUpdateRequest.state - 소유 Task: 신규
P5-R1
관찰 내용
edit form은 초기 state를 local state에 넣고 모든 저장 request에 state를 포함한다. 현재 값과 원본을 비교해 생략하는 분기가 없다.
영향
요구된 partial update 의미를 위반하고 다른 동시 변경을 불필요하게 덮어쓸 수 있다.
권장 조치
원본과 달라진 경우에만 state key를 추가하고 unchanged/changed payload test를 분리한다.
판정 기록
- 2026-07-29 —
SeriesForm.tsx:127과 기존 test의 항상 포함 assertion으로 확정. - 2026-07-30 —
P5-R1에서 unchanged state 생략 회귀 test와 원본 비교 serializer를 추가해 수정 완료.
REV-P5-002 — Series schema가 제품 mutation 불변식을 강제하지 않는다
- 심각도: Medium
- 상태: 확정
- 관련 요구사항:
SERIES-002,SERIES-010 - 관련 계약:
SeriesUpdateRequest - 소유 Task: 신규
P5-R1
관찰 내용
export된 update schema가 nullable state와 boolean/nullable isActive를 허용한다. 따라서 API helper 호출자는 state:null, isActive:true/null을 전송할 수 있다.
영향
현재 UI 밖의 후속 호출자가 복원 금지·soft-delete-only 정책을 우회할 수 있다.
권장 조치
일반 update와 deactivate request schema를 분리해 isActive=false만 별도 adapter에서 허용하고 state는 enum만 허용한다.
판정 기록
- 2026-07-29 — schema safe-parse 가능 범위를 PRD 정책과 대조해 확정.
- 2026-07-30 —
P5-R1에서 일반 update schema와 deactivate{ isActive:false }schema를 분리하고state:null,isActive:true/null거부 test로 수정 완료.
REV-P5-003 — Series image format pair가 검증되지 않는다
- 심각도: Medium
- 상태: 확정
- 관련 요구사항:
FILE-002,SERIES-014 - 관련 계약: JPEG/PNG image
- 소유 Task: 신규
P5-R2
관찰 내용
.png + image/jpeg 또는 .jpg + image/png가 허용된다.
영향
Series 생성·이미지 교체가 잘못된 multipart를 전송할 수 있다.
권장 조치
extension별 MIME mapping과 양방향 mismatch test를 추가한다.
판정 기록
- 2026-07-29 — Series policy가 독립 allowlist validator만 호출함을 확인해 확정.
- 2026-07-30 —
P5-R2에서 Series 전용 extension↔MIME pair test와 crop-before-validation test를 추가하고 policy mapping으로 수정 완료.
7. 확정 항목의 plan·goal 전환
REV-P5-001~002→P5-R1REV-P5-003→P5-R2
8. 리뷰 종료 판정
| 판정 항목 | 결과 | 근거 |
|---|---|---|
| 리뷰 범위 전체 확인 | 충족 | Series code/test/contract 대조 |
| 후보 항목 판정 완료 | 충족 | 3건 확정 |
| 확정 항목 plan 반영 | 충족 | P5-R1, P5-R2 |
| 보류 항목 담당·재개 조건 | 해당 없음 | 기존 server fixture 차단은 계획에 이미 기록 |
| 검증 명령과 결과 기록 | 충족 | §4 |
최종 결론: 수정 goal 필요.
남은 항목: 기존 P10 server integration, Phase 9 inactive 정책.
9. 수정 후 검증 기록
P5-R1 Series partial update 불변식 복구, 2026-07-30
- 무엇을: Series edit payload에서 원본과 같은
state를 생략하고, 일반 update schema와 deactivate{ isActive:false }schema를 분리했다. - 왜: partial update가 변경 없는 상태값을 덮어쓰지 않고, soft delete 이외의
isActivemutation과state:null을 boundary에서 막기 위해서다. - 어떻게:
- RED:
npm run test:run -- src/features/series/tests/series-form.test.tsx src/features/series/tests/series-update-invariants.test.ts는 2 failed로, unchanged edit payload에state: "PROCEEDING"이 포함되고state:nullparse가 성공함을 확인했다. - GREEN:
SeriesForm은 원본과 달라진 경우에만state를 request에 넣고,seriesUpdateRequestSchema는 enumstate만 허용한다.deactivateSeries와 mock store는 별도seriesDeactivateRequestSchema로{ isActive:false }만 처리한다. - 검증: focused 2 files / 5 tests 통과,
npm run test:run -- src/features/series는 6 files / 20 tests 통과했다.npm run typecheck,npm run lint,npm run build도 모두 exit 0이었다.
- RED:
P5-R2 Series image format pair 검증, 2026-07-30
- 무엇을: Series image validation에서
.jpg/.jpeg는image/jpeg,.png는image/png만 허용하도록 extension↔MIME pair를 고정했다. - 왜: 독립 allowlist만으로는
.png + image/jpeg,.jpg + image/png가 crop과 multipart upload까지 진행될 수 있기 때문이다. - 어떻게:
- RED:
npm run test:run -- src/features/series/tests/series-image-policy.test.ts src/features/series/tests/series-form.test.tsx는 2 failed로, mismatch file이{ ok: true }를 반환하고 crop dialog가 열림을 확인했다. - GREEN:
series-image-policy.ts에 Series 전용 extension→MIME mapping을 추가해 mismatch를{ ok:false, reason:"mime" }로 거부하고, 기존 size/extension/MIME allowlist 검사는 유지했다. - 검증: focused 2 files / 5 tests 통과,
npm run test:run -- src/features/series src/shared/validation/file-media-policy.test.ts는 8 files / 29 tests 통과했다.npm run typecheck,npm run lint,npm run build도 모두 exit 0이었다.
- RED:
남은 항목: 기존 P10 server integration과 Phase 9 inactive 정책은 후속 범위로 유지한다.