fix(ai-character): 폼 도메인 검증 복구

This commit is contained in:
Yu Sung
2026-08-05 00:34:04 +09:00
parent fb9b550040
commit ca5aa990d3
9 changed files with 290 additions and 14 deletions

View File

@@ -10,12 +10,12 @@
| 문서 항목 | 내용 |
|---|---|
| 상태 | 2026-08-04 `P6-R5` FileField 이미지 preview·Community 생성 media flow·공용 CAN 가격 계약 구현, 표준 배율 browser QA와 최종 review 완료. `P4-R10`, `P9-R18`~`P9-R19`, `P10-R16`~`P10-R17` 자동 보완 완료, 실제 crop pixel·stale ADMIN·Series/FanTalk/Comments/file policy 수동 QA 대기 |
| 상태 | 2026-08-04 `P6-R7` Audio preview 시간 domain validation 회귀 수정과 코드-only 재리뷰 완료. `P6-R6` 가격 domain validation submit 경로 회귀 수정 완료. `P6-R5` FileField 이미지 preview·Community 생성 media flow·공용 CAN 가격 계약 구현 완료. `P4-R10`, `P9-R18`~`P9-R19`, `P10-R16`~`P10-R17` 자동 보완 완료, 실제 crop pixel·stale ADMIN·Series/FanTalk/Comments/file policy 수동 QA 대기 |
| 최초 작성일 | 2026-07-25 |
| 재작성일 | 2026-07-26 |
| 요구사항 기준 | [prd.md](./prd.md) |
| API 기준 | [api-contract.openapi.json](./api-contract.openapi.json) |
| 현재 활성 Goal | `P6-R5` 완료 |
| 현재 활성 Goal | `P6-R7` 완료 |
## 목표
@@ -3147,6 +3147,56 @@ focus가 Sheet trigger로 복귀하는지 본다.
- 리뷰: 목표·품질 review에서 문서 추적성과 Audio raw `00` zero reset blocker를 발견했다. `parsePrice(value) === 0`으로 보완해 Audio focused 9 files / 69 tests를 통과했고, 문서 상태·파일 지도·Decision Log를 정렬했다.
- 최종 browser/visual: 768px·1280px Chromium/mobile Chrome에서 PNG crop/applied/replacement/error와 기존 GIF/audio/price 상태를 포함해 Community E2E 14/14를 통과했다. 4개 project/viewport evidence directory의 32개 표준 배율 PNG를 기능·디자인 시스템 및 CJK reviewer가 전수 확인해 PASS/HIGH, blocker 0건으로 판정했다. ``은 PRD의 정상 CAN 단위이며, 사용자 결정에 따라 browser zoom 검증은 제외했다.
### Task R6.6 — 가격 domain validation submit 경로 복구
**Goal 실행 `P6-R6`:** Audio와 Community 가격 입력의 native constraint가 domain submit validation을 우회하지 않게 하고 inline 오류·오류 focus 계약을 복구한다.
- **연결 리뷰:** [P6-R5 코드 리뷰](./reviews/phase6-community-create-media-flow.md) — `REV-P6-R5-001`.
- **시작 조건:** `P6-R5` 완료, `REV-P6-R5-001` 확정, `plan-task.md:3128`의 range validation domain submit 소유 계약.
- **완료 증거:** 음수·소수 가격을 실제 submit button으로 제출했을 때 Audio 생성·수정과 Community 생성 모두 domain inline 오류를 표시하고 request를 전송하지 않는 RED/GREEN, focused test·전체 unit·typecheck·lint·build·diff check 통과, 코드-only 재리뷰.
- **Files:** Modify: `src/features/audio-contents/components/AudioContentForm.tsx`, `src/features/audio-contents/tests/audio-form.test.tsx`, `src/features/audio-contents/tests/audio-form-update.test.tsx`, `src/features/community-posts/components/CommunityPostForm.tsx`, `src/features/community-posts/tests/community-price-validation.test.tsx`, `docs/20260725_AI캐릭터관리자웹/plan-task.md`; Create: `docs/20260725_AI캐릭터관리자웹/reviews/phase6-community-create-media-flow.md`.
- **Interfaces:** 두 form은 `noValidate`로 native submit 차단만 해제하고 `CanPriceField`의 `type=number`, `min=0`, `step=1`과 domain `parseCanPriceInput` validation은 유지한다.
- **범위 밖:** 가격 schema·오류 문구·payload 변경, FileField·media flow 변경, 브라우저/E2E 실행.
- [x] **RED:** 기존 음수·소수 테스트를 `fireEvent.submit(form)` 대신 실제 `생성`/`저장` button click으로 실행해 domain inline 오류 assertion이 실패하는 것을 확인한다.
- [x] **GREEN:** Audio와 Community form에 `noValidate`를 추가해 동일 테스트를 통과시킨다.
- [x] **REFACTOR:** 추가 abstraction 없이 focused/full 정적 Gate와 staged diff 재리뷰를 통과하고 review·Progress를 누적한다.
- **실행 명령:** `npm run test:run -- src/features/audio-contents/tests/audio-form.test.tsx src/features/audio-contents/tests/audio-form-update.test.tsx src/features/community-posts/tests/community-price-validation.test.tsx`; `npm run test:run -- src/shared/ui/__tests__/file-field.test.tsx src/shared/ui/__tests__/can-price-field.test.tsx src/features/audio-contents src/features/community-posts`; `npm run test:run`; `npm run typecheck`; `npm run lint`; `npm run build:dev`; `npm run build:prod`; `git diff --check`.
- **기대 결과:** 모든 명령 exit 0, 음수·소수 실제 submit에서 domain 오류 누락 0건, invalid price request 0건, 기존 FileField/media/price 회귀 0건.
- **수동 확인:** 없음 — 사용자 지시에 따라 browser를 실행하지 않고 실제 submit button을 사용하는 jsdom integration test로 검증한다.
**P6-R6 진행 기록 (2026-08-04):**
- RED: Audio 생성·수정과 Community 생성의 invalid price test를 실제 submit button click으로 변경했다. `npm run test:run -- src/features/audio-contents/tests/audio-form.test.tsx src/features/audio-contents/tests/audio-form-update.test.tsx src/features/community-posts/tests/community-price-validation.test.tsx` — 음수·소수 inline 오류 누락 6건 실패, 나머지 22건 통과로 native constraint의 submit 차단을 재현했다.
- GREEN: 두 form에 `noValidate`만 추가했다. 동일 명령 — 3 files / 28 tests passed.
- REFACTOR/Gate: 추가 abstraction 없음. domain focused — 18 files / 134 tests passed; full unit — 83 files / 458 tests passed; `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod`, `git diff --check` — exit 0. build의 500kB chunk warning은 기존 비차단 경고다.
- 리뷰: [P6-R5 코드 리뷰](./reviews/phase6-community-create-media-flow.md)의 `REV-P6-R5-001`을 수정 완료로 판정했다. 독립 코드 재리뷰에서 form-level `noValidate`가 기존 preview 시간 `pattern` 검증도 해제하는 `REV-P6-R5-002`를 추가 확정해 `P6-R7`로 전환했다. 사용자 지시에 따라 browser/E2E는 실행하지 않았다.
### Task R6.7 — Audio preview 시간 domain validation 복구
**Goal 실행 `P6-R7`:** Audio form의 `noValidate`를 유지하면서 preview 시작·종료의 완전한 `HH:MM:SS` 형식을 domain submit validation으로 보장한다.
- **연결 리뷰:** [P6-R5 코드 리뷰](./reviews/phase6-community-create-media-flow.md) — `REV-P6-R5-002`.
- **시작 조건:** `P6-R6` 완료, `REV-P6-R5-002` 확정, PRD `AUDIO-033`.
- **완료 증거:** preview 생성이 켜진 Audio 생성에서 잘못된 시작·종료 값을 실제 submit button으로 제출하면 inline 오류를 표시하고 request를 전송하지 않는 RED/GREEN, Audio focused·전체 unit·typecheck·lint·build·diff check 통과, 코드-only 재리뷰.
- **Files:** Modify: `src/features/audio-contents/components/AudioContentForm.tsx`, `src/features/audio-contents/components/AudioContentCreateOptions.tsx`, `src/features/audio-contents/tests/audio-form-create-red.test.tsx`, `docs/20260725_AI캐릭터관리자웹/plan-task.md`, `docs/20260725_AI캐릭터관리자웹/reviews/phase6-community-create-media-flow.md`.
- **Interfaces:** `noValidate`와 가격 domain validation은 유지한다. preview 생성이 켜지고 값이 입력된 경우에만 기존 input `pattern`과 동일한 완전한 `HH:MM:SS` 형식을 domain에서 검증해 inline 오류와 오류 focus를 제공한다.
- **범위 밖:** preview offset의 오디오 duration 상관관계, nullable contract 변경, 브라우저/E2E 실행.
- [x] **RED:** 잘못된 preview 시작·종료 값을 실제 `생성` button으로 제출해 request가 전송되는 실패를 확인한다.
- [x] **GREEN:** Audio domain validation과 input 오류 연결을 최소 추가해 request를 차단한다.
- [x] **REFACTOR:** 추가 abstraction 없이 Audio focused/full 정적 Gate와 코드 재리뷰를 통과하고 review·Progress를 누적한다.
- **실행 명령:** `npm run test:run -- src/features/audio-contents/tests/audio-form-create-red.test.tsx`; `npm run test:run -- src/features/audio-contents`; `npm run test:run`; `npm run typecheck`; `npm run lint`; `npm run build:dev`; `npm run build:prod`; `git diff --check`.
- **기대 결과:** 모든 명령 exit 0, malformed preview time request 0건, 정상 `HH:MM:SS`·가격 domain validation 회귀 0건.
- **수동 확인:** 없음 — 사용자 지시에 따라 browser를 실행하지 않고 실제 submit button을 사용하는 jsdom integration test로 검증한다.
**P6-R7 진행 기록 (2026-08-04):**
- RED: malformed preview 시작·종료를 실제 `생성` button으로 제출하는 test를 추가했다. `npm run test:run -- src/features/audio-contents/tests/audio-form-create-red.test.tsx` — 신규 1건 실패, 기존 9건 통과로 inline 오류 없이 request 경로가 진행되는 회귀를 재현했다.
- GREEN: preview 생성이 켜지고 값이 입력된 경우에만 기존 input `pattern`과 같은 `HH:MM:SS` 형식을 domain에서 검사하고, 시작·종료 inline 오류·`aria-describedby`·`aria-invalid`·첫 오류 focus를 연결했다. 동일 명령 — 1 file / 10 tests passed.
- REFACTOR/Gate: 추가 abstraction 없음. Audio·Community·shared 회귀 — 18 files / 135 tests passed; full unit — 83 files / 459 tests passed; `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — exit 0. build의 500kB chunk warning은 기존 비차단 경고다.
- 리뷰: 두 `noValidate` form의 native constraint와 domain 검증을 다시 추적했다. Audio 예약 일시는 기존 domain 검증이 담당하며 Community/FileField/media/price를 포함한 추가 확정 발견 사항은 없었다. 사용자 지시에 따라 browser/E2E는 실행하지 않았다.
---
## Phase 7. FanTalk vertical slice