Files
voiceon-character-admin/docs/20260725_AI캐릭터관리자웹/reviews/review-phase-5-20260729.md

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, PRD 14.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~002P5-R1
  • REV-P5-003P5-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 이외의 isActive mutation과 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:null parse가 성공함을 확인했다.
    • GREEN: SeriesForm은 원본과 달라진 경우에만 state를 request에 넣고, seriesUpdateRequestSchema는 enum state만 허용한다. 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이었다.

P5-R2 Series image format pair 검증, 2026-07-30

  • 무엇을: Series image validation에서 .jpg/.jpegimage/jpeg, .pngimage/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이었다.

남은 항목: 기존 P10 server integration과 Phase 9 inactive 정책은 후속 범위로 유지한다.