fix(ai-character): 폼 도메인 검증 복구
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
# Phase 6 Community 생성 media flow 코드 리뷰
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 6 / `P6-R5`, 회귀 수정 `P6-R6`~`P6-R7` |
|
||||
| 기준 commit 또는 working tree | `766a06a` + staged/working tree 변경 |
|
||||
| 리뷰 일자 | `2026-08-04` |
|
||||
| 리뷰어 | Codex |
|
||||
| 기준 문서 | `../prd.md`, `../api-contract.openapi.json`, `../plan-task.md` |
|
||||
| 리뷰 상태 | 수정 검증 완료 |
|
||||
|
||||
## 2. 리뷰 목적과 범위
|
||||
|
||||
### 목적
|
||||
|
||||
- `COMMUNITY-003`, `COMMUNITY-007`, `COMMUNITY-013`, `COMMUNITY-016`, `FILE-016`과 `P6-R5`가 변경 코드에 구현됐는지 확인한다.
|
||||
- 발견된 가격 submit validation 우회와 Audio preview 시간 검증 회귀를 `P6-R6`~`P6-R7`에서 수정하고 관련 회귀가 없는지 확인한다.
|
||||
|
||||
### 포함 범위
|
||||
|
||||
- 코드: `src/shared/ui/file-field.tsx`, `src/shared/ui/can-price-field.tsx`, Audio/Community form과 helper, staged UI 보완 파일
|
||||
- 테스트: 관련 shared UI·Audio·Community Vitest와 staged `tests/e2e/community.spec.ts`의 정적 검토
|
||||
- 문서: 위 요구사항, OpenAPI Community/Audio create contract, `P6-R5`~`P6-R7`, `DESIGN.md`
|
||||
- 수동 검증: 없음
|
||||
|
||||
### 제외 범위
|
||||
|
||||
- 사용자 지시에 따라 browser/E2E 실행과 visual QA를 제외한다.
|
||||
- 실제 개발 API와 server file policy는 기존 외부 수동 QA 범위로 유지한다.
|
||||
- `P6-R5`와 무관한 기존 코드·Phase는 검토하지 않는다.
|
||||
|
||||
## 3. 판정 기준
|
||||
|
||||
심각도와 상태는 `docs/agent-guide/review.md`의 `Blocker`·`High`·`Medium`·`Low`, `후보`·`확정`·`오탐`·`보류`·`수정 완료` 정의를 사용한다.
|
||||
|
||||
## 4. 검토한 근거
|
||||
|
||||
### 문서와 코드
|
||||
|
||||
- 요구사항: `AUDIO-033`, `COMMUNITY-003`, `COMMUNITY-007`, `COMMUNITY-013`, `COMMUNITY-016`, `FILE-009`, `FILE-016`
|
||||
- API Contract: `AudioContentCreateRequest`, `CommunityPostCreateRequest`, `CommunityPostCreateMultipart`
|
||||
- 계획: `P6-R5`, `P6-R6`, `P6-R7`
|
||||
- 코드: `FileField`, `CanPriceField`, `AudioContentForm`, `CommunityPostForm`
|
||||
- 테스트: shared FileField/CAN field, Audio form create/update, Community form/price/contract
|
||||
|
||||
### 실행 환경
|
||||
|
||||
```text
|
||||
OS: Darwin 25.0.0 x86_64
|
||||
Node: v24.12.0
|
||||
npm: 11.7.0
|
||||
Browser/viewport: 실행하지 않음(사용자 지시)
|
||||
환경 변수: Vitest 기본 test 환경, build development/production mode
|
||||
```
|
||||
|
||||
### 실행한 검증
|
||||
|
||||
| 명령 또는 수동 검증 | 결과 | 핵심 증거 |
|
||||
|---|---|---|
|
||||
| `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` (RED) | 의도한 실패 | 음수·소수 6건 inline 오류 누락, 22건 통과 |
|
||||
| 동일 focused 명령 (GREEN) | 성공 | 3 files / 28 tests passed |
|
||||
| `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` | 성공 | 18 files / 134 tests passed |
|
||||
| `npm run test:run -- src/features/audio-contents/tests/audio-form-create-red.test.tsx` (`P6-R7` RED) | 의도한 실패 | malformed preview 신규 1건 실패, 기존 9건 통과 |
|
||||
| 동일 focused 명령 (`P6-R7` GREEN) | 성공 | 1 file / 10 tests passed |
|
||||
| Audio·Community·shared 회귀 | 성공 | 18 files / 135 tests passed |
|
||||
| `npm run test:run` | 성공 | 83 files / 459 tests passed |
|
||||
| `npm run typecheck` | 성공 | exit 0 |
|
||||
| `npm run lint` | 성공 | exit 0 |
|
||||
| `npm run build:dev` | 성공 | exit 0, 기존 500kB chunk warning |
|
||||
| `npm run build:prod` | 성공 | exit 0, 기존 500kB chunk warning |
|
||||
| `git diff --check` | 성공 | exit 0 |
|
||||
| Browser/E2E | 제외 | 사용자 지시에 따라 실행하지 않음 |
|
||||
|
||||
## 5. 발견 사항 요약
|
||||
|
||||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `REV-P6-R5-001` | Medium | 수정 완료 | native 가격 constraint가 domain submit validation을 우회함 | `P6-R6` | 완료 |
|
||||
| `REV-P6-R5-002` | Medium | 수정 완료 | Audio preview 시간 검증이 `noValidate`로 무력화됨 | `P6-R7` | 완료 |
|
||||
|
||||
`REV-P6-R5-002` 수정 후 코드-only 재리뷰에서 추가 확정 발견 사항은 없다.
|
||||
|
||||
## 6. 발견 사항 상세
|
||||
|
||||
### REV-P6-R5-001 — native 가격 constraint가 domain submit validation을 우회함
|
||||
|
||||
- **심각도:** Medium
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** `COMMUNITY-007`, `P6-R5` Interface의 domain submit validation 소유 계약
|
||||
- **관련 계약:** `CommunityPostCreateRequest.price` `0..99999` integer
|
||||
- **소유 Task:** `P6-R6`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
`CanPriceField`는 `type=number`, `min=0`, `step=1`을 사용하지만 Audio·Community form에 `noValidate`가 없었다. 실제 submit button click에서는 음수·소수의 native constraint가 submit event를 먼저 차단해 domain inline 오류와 오류 focus가 실행되지 않았다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `src/shared/ui/can-price-field.tsx`, `src/features/audio-contents/components/AudioContentForm.tsx`, `src/features/community-posts/components/CommunityPostForm.tsx`
|
||||
- 테스트: invalid price test가 `fireEvent.submit(form)`을 직접 호출해 native constraint 경로를 우회했다.
|
||||
- 문서: `plan-task.md` `P6-R5` Interface는 range validation을 domain submit이 소유한다고 명시한다.
|
||||
|
||||
**재현 또는 검증 절차**
|
||||
|
||||
1. Audio 생성·수정 또는 Community 생성 form 가격에 `-1` 또는 `1.5`를 입력한다.
|
||||
2. form event 직접 dispatch가 아니라 실제 `생성` 또는 `저장` button을 클릭한다.
|
||||
3. 수정 전에는 submit handler가 실행되지 않아 domain 가격 오류가 표시되지 않았다.
|
||||
4. 수정 후에는 domain 오류가 표시되고 request가 전송되지 않는다.
|
||||
|
||||
**영향**
|
||||
|
||||
invalid request 자체는 native validation이 막지만, 문서화된 공통 inline 오류·접근성 연결·오류 focus가 음수·소수에서 누락됐다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
Audio와 Community form에 `noValidate`를 추가하고 invalid price test를 실제 submit button click으로 유지한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- `2026-08-04` — 코드와 실제 submit button 기반 RED에서 확정.
|
||||
- `2026-08-04` — `P6-R6` 최소 수정과 focused/full Gate 통과로 수정 완료.
|
||||
|
||||
### REV-P6-R5-002 — Audio preview 시간 검증이 `noValidate`로 무력화됨
|
||||
|
||||
- **심각도:** Medium
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** `AUDIO-033`
|
||||
- **관련 계약:** `AudioContentCreateRequest.previewStartTime`, `previewEndTime`의 nullable `HH:mm:ss`
|
||||
- **소유 Task:** `P6-R7`
|
||||
|
||||
**관찰 내용**
|
||||
|
||||
`P6-R6`에서 Audio form에 추가한 `noValidate`는 가격 constraint뿐 아니라 preview 시작·종료 input의 기존 `pattern` 검증도 해제했다. 수정 전 form domain validation과 request schema는 잘못된 시간 문자열을 거부하지 않았다.
|
||||
|
||||
**근거**
|
||||
|
||||
- 코드: `src/features/audio-contents/components/AudioContentForm.tsx`, `src/features/audio-contents/components/AudioContentCreateOptions.tsx`, `src/features/audio-contents/schemas/audio-content-schema.ts`
|
||||
- 수정 전 테스트: 정상 `HH:MM:SS` 제출과 `pattern` attribute만 확인하며 malformed submit을 검증하지 않았다.
|
||||
- 문서: PRD `AUDIO-033`은 preview 시간 입력을 완전한 `HH:MM:SS`로 제한한다.
|
||||
|
||||
**영향**
|
||||
|
||||
preview 생성이 켜진 Audio 생성에서 malformed preview 시간이 multipart request에 포함될 수 있다.
|
||||
|
||||
**권장 조치**
|
||||
|
||||
`noValidate`는 유지하고 preview 시작·종료 형식을 domain validation에 추가해 inline 오류와 오류 focus를 제공한다. 실제 submit button 기반 malformed 값 회귀 테스트를 유지한다.
|
||||
|
||||
**판정 기록**
|
||||
|
||||
- `2026-08-04` — 독립 코드 재리뷰에서 확정하고 `P6-R7`로 전환.
|
||||
- `2026-08-04` — 실제 submit button RED와 domain inline 오류·focus 최소 수정, focused/full Gate 통과로 수정 완료.
|
||||
|
||||
## 7. 확정 항목의 plan·goal 전환
|
||||
|
||||
- `REV-P6-R5-001`을 `plan-task.md`의 `P6-R6`으로 전환했고 수정·검증을 완료했다.
|
||||
- `REV-P6-R5-002`를 `plan-task.md`의 `P6-R7`으로 전환했고 수정·검증을 완료했다.
|
||||
|
||||
## 8. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | staged/working tree 변경과 관련 caller·test 추적 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P6-R5-001`, `REV-P6-R5-002` 확정 |
|
||||
| 확정 항목 plan 반영 | 충족 | `P6-R6`, `P6-R7` |
|
||||
| 보류 항목의 담당·재개 조건 기록 | 해당 없음 | 보류 항목 없음 |
|
||||
| 검증 명령과 결과 기록 | 충족 | 4절과 `plan-task.md` 진행 기록 |
|
||||
|
||||
**최종 결론:** 수정 검증 완료, 추가 확정 발견 사항 없음.
|
||||
|
||||
**남은 항목:** browser/E2E와 실제 개발 API 수동 QA는 이번 사용자 요청에서 제외했다.
|
||||
|
||||
## 9. 수정 후 검증 기록
|
||||
|
||||
### 1차 수정 검증 — 2026-08-04
|
||||
|
||||
- 무엇을: `REV-P6-R5-001`의 native constraint submit 우회를 수정했다.
|
||||
- 왜: Audio와 Community가 공통 domain 가격 오류·접근성·focus 계약을 동일하게 실행해야 한다.
|
||||
- 어떻게:
|
||||
- RED — 음수·소수 6건 실패, 22건 통과.
|
||||
- GREEN — 3 files / 28 tests passed.
|
||||
- 회귀 — domain 18 files / 134 tests, full 83 files / 458 tests passed.
|
||||
- 정적/build — typecheck·lint·dev/prod build·diff check exit 0.
|
||||
- 남은 항목: 이번 코드-only 리뷰 범위의 확정 발견 사항 없음.
|
||||
|
||||
### 2차 수정 검증 — 2026-08-04
|
||||
|
||||
- 무엇을: `REV-P6-R5-002`의 Audio preview 시간 검증 회귀를 수정했다.
|
||||
- 왜: form-level `noValidate`를 유지하면서 PRD `AUDIO-033`의 완전한 `HH:MM:SS` 계약을 domain submit 경로가 소유해야 한다.
|
||||
- 어떻게:
|
||||
- RED — malformed preview 신규 1건 실패, 기존 9건 통과.
|
||||
- GREEN — 1 file / 10 tests passed, request 0건과 inline 오류·첫 오류 focus 확인.
|
||||
- 회귀 — 관련 18 files / 135 tests, full 83 files / 459 tests passed.
|
||||
- 정적/build — typecheck·lint·dev/prod build exit 0.
|
||||
- 남은 항목: 이번 코드-only 리뷰 범위의 확정 발견 사항 없음.
|
||||
Reference in New Issue
Block a user