docs(ai-character): Mock Preview 계획 추가

This commit is contained in:
Yu Sung
2026-07-27 15:25:06 +09:00
parent 5356bd1e6a
commit b4841ff579
7 changed files with 340 additions and 164 deletions

View File

@@ -90,6 +90,16 @@ type PageData<T> = {
- 오디오 media error는 signed URL 만료로 구분하거나 추정하지 않는다. 재생 오류만으로 목록·상세 API를 자동 재조회하거나 `play()`를 자동 재호출하지 않으며 일반 오류와 수동 재시도·페이지 새로고침 안내를 제공한다.
- 커뮤니티 첨부 audio URL만을 갱신하기 위한 요청은 하지 않고 media error를 refetch trigger로 사용하지 않는다. 사용자 페이지 새로고침이나 mutation 후 cache 무효화 등 일반 목록 재조회에서는 새 응답의 URL을 사용한다. 전용 상세 조회나 URL 재발급 호출을 추가하지 않는다.
### 1.5 개발 전용 Mock Preview 계약 경계
- browser MSW handler와 fixture는 이 문서에 **제공됨**으로 기록된 endpoint, request/response DTO와 오류 규칙에서만 파생한다.
- mock mode도 production과 같은 URL, method, header, serializer, envelope와 API client를 사용하며 별도 mock 전용 DTO·adapter를 만들지 않는다.
- 계약은 제공됐지만 backend endpoint가 아직 404인 경우 mock mode에서 최종 UI를 확인할 수 있다. server mode의 404·network error를 감지해 mock으로 자동 fallback하지 않는다.
- “백엔드 제공 대기” 항목은 fixture로 추정하지 않는다. 계약이 제공되기 전에는 관련 최종 network UI와 integration 완료를 주장하지 않는다.
- mock mutation은 deterministic in-memory store를 갱신하고 새로고침 때 seed로 초기화한다. domain fixture를 browser 영구 저장소에 기록하지 않는다.
- mock mode는 개발 환경에서만 명시적으로 활성화하고 production build에서는 거부한다.
- mock UI Gate 통과는 실제 backend 연동 완료 증거가 아니다. 각 도메인은 `UI 확인 완료(mock)``실제 서버 연동 완료(server)` 결과를 별도로 기록한다.
## 2. 공통 enum과 형식
```ts

View File

@@ -10,7 +10,7 @@
| 문서 항목 | 내용 |
|---|---|
| 상태 | Phase 0~1 완료, Phase 2 착수 전 |
| 상태 | Phase 0~1 완료, 신규 Phase 2 Mock Preview 착수 전 |
| 최초 작성일 | 2026-07-25 |
| 재작성일 | 2026-07-26 |
| 요구사항 기준 | [prd.md](./prd.md) |
@@ -18,7 +18,7 @@
## 1. 전역 제약
- 이번 단계에서는 이 계획 문서만 수정한다. 애플리케이션 코드와 프로젝트 설정은 후속 구현 단계에서 변경한다.
- 계획 보완 단계에서는 PRD·API Contract·plan과 연결 가이드만 동기화하고, 애플리케이션 코드와 프로젝트 설정은 해당 Task goal 실행 때 변경한다.
- PRD와 최초 API Contract가 충돌하면 PRD `11.4 API 계약 보정사항`을 우선한다.
- 로그인은 `POST /admin/member/login`, 로그아웃은 body 없는 `POST /member/logout`을 사용한다.
- JWT와 ADMIN role은 `sessionStorage`에만 저장한다. refresh token과 자동 갱신은 구현하지 않는다.
@@ -42,18 +42,23 @@
- Page는 routing·query·permission·component 조합만 담당한다. 도메인 표시와 상호작용은 feature component로, 두 Phase 이상에서 의미와 동작이 같은 것은 shared component로 분리한다.
- 단순 markup 한 조각, 한 화면 전용 UI, 서로 다른 도메인 규칙을 하나의 범용 prop API로 합치기 위한 component는 만들지 않는다.
- 모든 기능은 실패하는 test를 먼저 만들고 최소 구현으로 통과시킨다.
- API mode는 `server | mock`을 명시적으로 선택한다. 기본 `npm run dev`는 실제 개발 API, `npm run dev:mock`은 browser MSW를 사용한다.
- 실제 API의 404·network error를 감지한 자동 mock fallback은 만들지 않고 production build에서는 mock mode를 거부한다.
- 제공 계약이 있는 기능만 contract 기반 browser fixture를 만들며, 계약 자체가 없으면 최종 network UI를 추정 구현하지 않는다.
- 각 도메인은 `UI 확인 완료(mock)``실제 서버 연동 완료(server)`를 별도 증거로 기록하며 mock Gate만으로 Phase 전체 완료를 주장하지 않는다.
## 2. Phase 운영 규칙
### 2.1 독립 검증 규칙
각 Phase는 다음 결과를 모두 가진다.
도메인 Phase는 다음 결과를 모두 가진다.
1. 사용자가 직접 확인할 수 있는 하나 이상의 완결된 흐름
2. 해당 Phase가 소유하는 API contract test
3. loading·empty·error·success 상태
4. 해당 viewport 범위와 keyboard·접근성 검증
5. Phase 전용 E2E와 공통 typecheck·lint·build 결과
6. 제공 계약 범위의 mock preview E2E와 backend 구현 후 server integration 결과를 구분한 기록
각 Task는 다음 Red/Green loop를 따른다.
@@ -126,24 +131,26 @@ Goal objective 권장 형식:
|---:|---|---|---|
| 0 | 프로젝트 세팅 | 없음 | fresh install, root smoke, unit/E2E/build |
| 1 | 공통 플랫폼·인증/인가·컴포넌트 기반 | Phase 0 | shared component contract + login → protected shell → refresh restore → logout/401/403 |
| 2 | Character workspace | Phase 1 | list/search → create/select → detail/edit → deactivate |
| 3 | Audio vertical slice | Phase 2의 workspace core | list/filter/detail/play → create/edit/upload → deactivate |
| 4 | Series vertical slice | Phase 3의 Audio 조회 API | CRUD → content link/unlink → full reorder |
| 5 | Community vertical slice | Phase 3media/file primitive | list → collection Sheet edit/pin → media play → deactivate |
| 6 | FanTalk vertical slice | Phase 2 | list/filter/detail → one reply → edit |
| 7 | Comments vertical slice | Phase 3 + Phase 5 | Audio/Community thread → permission별 CRUD |
| 8 | 교차 회귀·인수인계 | 활성 범위의 Phase 0~7 | 전체 journey, viewport, axe, security, build |
| 2 | 개발 전용 Mock Preview 기반 | Phase 1 | explicit mock/server mode, browser MSW, mock banner, no production fallback |
| 3 | Character workspace | Phase 2 | list/search → create/select → detail/edit → deactivate |
| 4 | Audio vertical slice | Phase 3의 workspace core | list/filter/detail/play → create/edit/upload → deactivate |
| 5 | Series vertical slice | Phase 4Audio 조회 API | CRUD → content link/unlinkfull reorder |
| 6 | Community vertical slice | Phase 4의 media/file primitive | list → collection Sheet edit/pin → media play → deactivate |
| 7 | FanTalk vertical slice | Phase 3 | list/filter/detail → one reply → edit |
| 8 | Comments vertical slice | Phase 4 + Phase 6 | Audio/Community thread → permission별 CRUD |
| 9 | 교차 회귀·인수인계 | 활성 범위의 Phase 0~8 | 전체 journey, viewport, axe, security, mock/server build |
기본 진행 순서는 Phase 번호를 따른다. 다만 Phase 5·6은 자신의 선행조건과 계약이 충족되면 병행할 수 있고, 외부 계약으로 막힌 Phase가 다른 독립 Phase를 막지 않는다.
기본 진행 순서는 Phase 번호를 따른다. 다만 Phase 5·6·7은 자신의 선행조건과 계약이 충족되면 병행할 수 있고, 외부 계약으로 막힌 Phase가 다른 독립 Phase를 막지 않는다.
```text
Phase 0 Setup
└─ Phase 1 Platform + Auth/Authz + Shared Components
└─ Phase 2 Character Workspace
─ Phase 3 Audio ──┬─ Phase 4 Series
│ └─ Phase 5 Community ──┐
└─ Phase 6 FanTalk ─ Phase 8 Final
Phase 3 + Phase 5 ── Phase 7 Comments ┘
└─ Phase 2 Mock Preview Foundation
─ Phase 3 Character Workspace
├─ Phase 4 Audio ──┬─ Phase 5 Series
─ Phase 6 Community ──┐
└─ Phase 7 FanTalk ├─ Phase 9 Final
Phase 4 + Phase 6 ── Phase 8 Comments ┘
```
## 4. 파일 책임 지도
@@ -407,13 +414,13 @@ npm run build:prod
- **시작 조건:** `P1-T3` 완료.
- **완료 증거:** 체크박스 전체, route/shell test, auth·accessibility E2E와 viewport/axe 기록.
- **범위 밖:** Phase 2 Character 데이터와 mutation.
- **범위 밖:** Phase 3 Character 데이터와 mutation.
- [x] 미인증 사용자가 보호 content를 한 프레임도 보지 않고 `/login`으로 이동하는 test를 작성한다.
- [x] 401은 session 제거 후 login, 403은 AccessDeniedPage로 가는 route test를 작성한다.
- [x] desktop sidebar, mobile Sheet, header, logout, breadcrumb, skip link, `main` landmark를 구현한다.
- [x] keyboard로 login, navigation, logout을 완료하고 dialog/menu focus가 trigger로 복귀하는 test를 작성한다.
- [x] `/ai-characters`에는 Phase 2가 교체할 명시적 빈 route state만 두고 가짜 도메인 데이터를 만들지 않는다.
- [x] `/ai-characters`에는 Phase 3이 교체할 명시적 빈 route state만 두고 가짜 도메인 데이터를 만들지 않는다.
- [x] 320px과 200% zoom에서 shell overflow와 가려진 control이 없는지 E2E로 확인한다.
- [x] shell route의 axe critical·serious 위반 0건을 확인한다.
@@ -489,7 +496,7 @@ npm run build
- 연결 리뷰: `REV-P1-002`, `REV-P1-006`, `REV-P1-007`
- 완료 증거: stale ADMIN probe pending·403 동안 보호 shell 비노출, probe `size=20`, 서버 한국어 message 우선 표시, logout in-flight 중 API 1회 호출.
- 범위 밖: 인증 방식 또는 token 저장 방식 교체, Phase 2 권한 기능.
- 범위 밖: 인증 방식 또는 token 저장 방식 교체, Phase 3 권한 기능.
- [x] 지연 403 전 보호 shell 비노출과 probe `size=20` request assertion을 `src/app/App.test.tsx`에 추가했다.
- [x] 로그인 실패 시 `Error.message`의 서버 한국어 message를 우선 표시하도록 `LoginPage`를 수정했다.
@@ -567,14 +574,103 @@ npm run build
---
## Phase 2. Character workspace vertical slice
## Phase 2. 개발 전용 Mock Preview 기반
**목표:** backend endpoint 구현 전에도 제공된 API Contract 범위의 최종 UI를 브라우저에서 확인하고, 명시적 전환으로 실제 개발 API와 같은 frontend code를 사용한다.
**Phase Goal `P2`:** Task 2.1 → 2.3과 Phase 2 Gate로 browser MSW 기반 mock preview와 server mode 경계를 완성한다.
- **시작 조건:** `P1-GATE` 완료.
- **완료 조건:** `P2-T1`~`P2-T3`, `P2-GATE` 완료. mock/server 분리와 production 차단이 자동 test와 E2E로 고정됨.
- **현재 상태:** Phase 1 완료 후 새로 추가된 독립 후속 Phase. Phase 0·1의 완료 이력은 변경하지 않는다.
**요구사항:** `MOCK-001~009`, PRD `11.1~11.2`, `12`, `13`.
**주요 Files:**
- Modify: `package.json`, `.env.example`, `src/shared/config/env.ts`, `src/shared/config/env.test.ts`
- Modify: `src/main.tsx`, `src/app/providers.tsx`
- Create: `src/shared/mocks/{browser,handlers,fixtures,store}.ts`
- Create: `src/shared/mocks/__tests__/{mode-boundary,handlers,store}.test.ts`
- Create: `src/shared/ui/mock-mode-banner.tsx`
- Create: `src/shared/ui/__tests__/mock-mode-banner.test.tsx`
- Create: `public/mockServiceWorker.js`
- Create: `tests/e2e/mock-preview-shell.spec.ts`
- Modify when implementation is complete: `README.md`, `docs/agent-guide/{environment,scripts}.md`
### Task 2.1 explicit API mode·production 경계
**Goal 실행 `P2-T1`:** `server | mock` mode와 dev-only browser worker bootstrap을 구성하고 자동 fallback·production mock을 차단한다.
- **시작 조건:** `P1-GATE` 완료, PRD `MOCK-001~006` 확인.
- **완료 증거:** env/script/bootstrap focused test, `dev` server 기본값, `dev:mock` explicit mode, production mock 거부와 404 no-fallback test.
- **범위 밖:** Character 이후 도메인 fixture와 실제 backend endpoint 구현.
- [ ] `VITE_API_MODE=server | mock`을 검증하고 누락 기본값은 `server`로 고정하는 실패 test를 작성한다.
- [ ] `npm run dev:mock`만 Vite mock mode와 browser worker를 시작하고 기본 `npm run dev`는 실제 `VITE_API_BASE_URL`을 사용하는 script test를 작성한다.
- [ ] Playwright가 같은 spec을 explicit mode로 실행할 수 있도록 `e2e:mock`과 server mode 명령을 정의하고 각 webServer 환경을 test한다.
- [ ] `import.meta.env.DEV && apiMode === "mock"`일 때만 browser module을 dynamic import하고 worker 준비 후 React를 mount한다.
- [ ] production mode에서 `mock`을 설정하면 build/start 전에 설명 가능한 오류로 거부하고 worker·fixture가 production graph에서 실행되지 않는 test를 작성한다.
- [ ] `server` mode의 404·network error가 browser mock을 시작하거나 응답을 교체하지 않는 test를 작성한다.
- [ ] worker의 unhandled request 정책은 error로 두어 누락된 handler가 실제 backend로 조용히 통과하지 않게 한다.
### Task 2.2 공통 preview session·fixture store
**Goal 실행 `P2-T2`:** production API 경계를 그대로 사용하는 deterministic auth fixture, in-memory store와 mock mode 안내를 완성한다.
- **시작 조건:** `P2-T1` 완료, API Contract §1·§3 확인.
- **완료 증거:** auth handler/store/banner focused test와 login → protected shell mock preview E2E 기록.
- **범위 밖:** 도메인별 endpoint handler와 계약 미제공 fixture.
- [ ] fixture는 정규화 `ApiResponse<T>`와 실제 endpoint·header·body contract를 사용하고 별도 mock DTO를 만들지 않는다.
- [ ] ADMIN login·logout, 401·403 fixture와 새로고침 시 seed로 초기화되는 in-memory store를 만든다.
- [ ] mock mutation은 같은 store의 목록·상세 응답을 갱신하고 browser storage에 domain fixture를 영구 저장하지 않는다.
- [ ] mock mode임을 지속적으로 표시하는 접근 가능한 banner와 server mode에서 banner가 없는 test를 작성한다.
- [ ] JWT·password·signed URL·multipart body가 mock logger와 storage에 기록되지 않는 test를 작성한다.
- [ ] handler와 fixture가 `src/shared/test/server.ts`의 Node test lifecycle을 변경하지 않고 필요한 contract factory만 공유하게 한다.
### Task 2.3 Mock Preview 반응형·문서화
**Goal 실행 `P2-T3`:** mock mode의 browser 접근성·반응형과 실행 문서를 검증해 사용자가 최종 UI 확인 mode를 명확히 선택하게 한다.
- **시작 조건:** `P2-T1`, `P2-T2` 완료.
- **완료 증거:** 320px·desktop mock shell E2E, axe 결과, README와 environment/scripts 가이드의 실제 명령 동기화 기록.
- **범위 밖:** 도메인별 최종 UI와 server integration 완료 주장.
- [ ] mock banner가 320px·200% zoom에서 핵심 control을 가리지 않고 axe critical·serious 위반이 없는지 E2E로 확인한다.
- [ ] README에 `npm run dev``npm run dev:mock`, mode 차이, mock data reset, production 금지와 no-auto-fallback을 기록한다.
- [ ] 구현이 완료된 뒤 `docs/agent-guide/environment.md``scripts.md`에 실제 환경 변수와 명령을 추가한다.
- [ ] 후속 도메인 Phase가 handler·fixture·mock E2E를 소유한다는 규칙을 문서화한다.
### Phase 2 Gate
**Goal 실행 `P2-GATE`:** mock/server mode 경계, auth preview와 production 차단을 최종 판정한다.
- **시작 조건:** `P2-T1`~`P2-T3` 완료.
- **완료 증거:** 아래 명령과 Expected 통과 및 Phase 2 Progress 기록.
- **범위 밖:** mock 성공을 실제 도메인 server integration 성공으로 간주하는 것.
```bash
npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx
npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts
npm run typecheck
npm run lint
npm run build:dev
npm run build:prod
```
**Expected:** `npm run dev:mock`에서는 ADMIN login → protected shell과 mock banner가 동작하고 실제 backend 요청은 0건이다. 기본 server mode는 실제 API 오류를 그대로 처리하며 production build는 browser mock을 활성화하지 않는다.
---
## Phase 3. Character workspace vertical slice
**목표:** ADMIN이 active Character를 검색·생성·선택하고 workspace에서 상세·수정·soft delete까지 완료한다.
**Phase Goal `P2`:** Task 2.1 → 2.4와 Phase 2 Gate로 Character workspace vertical slice를 완성한다.
**Phase Goal `P3`:** Task 3.1 → 3.4와 Phase 3 Gate로 Character workspace vertical slice를 완성한다.
- **시작 조건:** `P1-GATE` 완료.
- **완료 조건:** `P2-T1`~`P2-T4`, `P2-GATE` 완료. 외부 의존은 구현 또는 명시적 제외 결정으로 종결.
- **시작 조건:** `P2-GATE` 완료.
- **완료 조건:** `P3-T1`~`P3-T4`, `P3-GATE` 완료. 외부 의존은 구현 또는 명시적 제외 결정으로 종결.
- **실행 순서:** 계약 확인 → 목록/workspace → mutation → 반응형·접근성.
**요구사항:** `CHAR-001~014`, `FILE-001~002`, `FILE-008~010`, `FILE-012`, PRD `7`, `9`의 Character 범위.
@@ -597,11 +693,11 @@ npm run build
- Create: `tests/e2e/character-workspace.spec.ts`
- Modify: `src/app/router.tsx`, `src/app/route-paths.ts`
### Task 2.1 Phase 계약 확인
### Task 3.1 Phase 계약 확인
**Goal 실행 `P2-T1`:** Character 구현 계약 화면 component map을 확정한다.
**Goal 실행 `P3-T1`:** Character 구현 계약, mock scenario와 화면 component map을 확정한다.
- **시작 조건:** `P1-GATE` 완료, PRD `CHAR-001~014`와 API Contract §4 확인.
- **시작 조건:** `P2-GATE` 완료, PRD `CHAR-001~014`, `MOCK-001~009`와 API Contract §4 확인.
- **완료 증거:** 체크박스 전체, 제공 계약 또는 제외 결정의 세 문서 일치, 상태/action inventory.
- **범위 밖:** 계약을 추정한 production adapter와 Character 화면 구현.
@@ -609,12 +705,13 @@ npm run build
- [ ] 계약이 없으면 original work network control과 serializer를 추측하지 않고, 현재 slice에서 제외할 범위를 PRD·API Contract·plan에 먼저 기록한다.
- [ ] Character 도메인 오류의 비2xx status와 message key를 contract fixture에 기록한다.
- [ ] 목록·상세·form·workspace의 상태/action inventory를 작성하고 Page는 route/query/policy 조합, feature component는 Character 표시·입력 규칙, shared component는 Phase 1 contract를 사용하도록 component map을 확정한다.
- [ ] 제공 계약 범위의 Character seed, loading·empty·error·success와 CRUD/deactivate browser handler 시나리오를 확정한다. 계약 미제공 original work network fixture는 만들지 않는다.
### Task 2.2 목록·선택·workspace
### Task 3.2 목록·선택·workspace
**Goal 실행 `P2-T2`:** active Character 목록·검색·선택과 URL 기반 workspace 복원을 완성한다.
**Goal 실행 `P3-T2`:** active Character 목록·검색·선택과 URL 기반 workspace 복원을 완성한다.
- **시작 조건:** `P2-T1` 완료.
- **시작 조건:** `P3-T1` 완료.
- **완료 증거:** 체크박스 전체, route/list/workspace test와 read-only/error 상태 검증 기록.
- **범위 밖:** Character 생성·수정·비활성화 form.
@@ -625,12 +722,13 @@ npm run build
- [ ] workspace header에 image, name, active 상태, `characterId`와 탭·breadcrumb를 표시한다.
- [ ] 상세 성공 응답이 `isActive=false`이면 read-only 배너와 중앙 write policy로 모든 mutation 진입점을 차단한다.
- [ ] 상세 400/404/500은 공통 오류 화면을 사용하고 비활성 ID 응답 정책을 client가 추정하지 않는다.
- [ ] browser MSW handler와 deterministic seed로 목록 → 선택 → workspace deep link의 최종 UI를 `dev:mock`에서 확인한다.
### Task 2.3 생성·수정·soft delete
### Task 3.3 생성·수정·soft delete
**Goal 실행 `P2-T3`:** Character multipart 생성·수정·soft delete와 image/original-work 규칙을 완성한다.
**Goal 실행 `P3-T3`:** Character multipart 생성·수정·soft delete와 image/original-work 규칙을 완성한다.
- **시작 조건:** `P2-T1`, `P2-T2` 완료.
- **시작 조건:** `P3-T1`, `P3-T2` 완료.
- **완료 증거:** 체크박스 전체, serializer/form/image/deactivate test, `OQ-009` 결정과 검증 기록.
- **범위 밖:** 계약 미제공 original-work integration과 하위 도메인 mutation.
@@ -645,12 +743,13 @@ npm run build
- [ ] 비활성화 AlertDialog가 영향·복원 미지원·hard delete 미지원을 설명하는 test를 작성한다.
- [ ] soft delete 성공 후 active-only 목록 재조회, 목록 이동, 성공 toast를 확인하고 상세에 머물지 않는다.
- [ ] Character form을 실제로 작성한 뒤 `OQ-009``name`·`description` 상한 필요성을 판단하고, 구현 전 결정 문서를 갱신하거나 “상한 추가 없음”으로 종결한다.
- [ ] mock store가 create/update/deactivate 후 목록·상세를 같은 server response contract로 갱신하는 E2E를 작성한다.
### Task 2.4 Character 반응형·접근성
### Task 3.4 Character 반응형·접근성
**Goal 실행 `P2-T4`:** Character viewport capability와 keyboard·zoom·axe 기준을 검증한다.
**Goal 실행 `P3-T4`:** Character viewport capability와 keyboard·zoom·axe 기준을 검증한다.
- **시작 조건:** `P2-T2`, `P2-T3`의 활성 범위 완료.
- **시작 조건:** `P3-T2`, `P3-T3`의 활성 범위 완료.
- **완료 증거:** 체크박스 전체, 320/768/1280px·200% zoom·keyboard·axe E2E 기록.
- **범위 밖:** 다음 Phase 도메인과 모바일 mutation.
@@ -660,34 +759,35 @@ npm run build
- [ ] keyboard-only로 search → select → tabs → form → dialog를 완료한다.
- [ ] 320/768/1280px, 200% zoom, axe critical·serious 0건을 Phase E2E에서 확인한다.
### Phase 2 Gate
### Phase 3 Gate
**Goal 실행 `P2-GATE`:** Character 전체 journey와 공통 품질 Gate를 최종 판정한다.
**Goal 실행 `P3-GATE`:** Character mock UI journey와 실제 server integration 상태를 분리해 판정한다.
- **시작 조건:** `P2-T1`~`P2-T4` 완료.
- **시작 조건:** `P3-T1`~`P3-T4` 완료.
- **완료 증거:** 아래 명령과 Expected 통과, 외부 의존의 구현/제외 상태 기록.
- **범위 밖:** 실패와 무관한 Audio 기능.
```bash
npm run test:run -- src/features/characters src/layouts/CharacterWorkspaceLayout.test.tsx
npm run e2e:mock -- tests/e2e/character-workspace.spec.ts
npm run e2e -- tests/e2e/character-workspace.spec.ts
npm run typecheck
npm run lint
npm run build
```
**Expected:** login → Character 검색/생성 → 선택/workspace → 수정 → soft delete → active-only 목록 복귀가 한 slice로 통과한다. original work 계약이 없으면 그 기능의 제외 결정과 문서가 명시돼야 하며 완료로 가장하지 않는다.
**Expected:** mock mode에서 login → Character 검색/생성 → 선택/workspace → 수정 → soft delete → active-only 목록 복귀의 최종 UI가 통과한다. server mode 결과는 별도로 기록하며 backend 미구현 404이면 `UI 확인 완료(mock) / 실제 서버 연동 대기`로 남긴다. original work 계약이 없으면 fixture를 추정하지 않는다.
---
## Phase 3. Audio vertical slice
## Phase 4. Audio vertical slice
**목표:** 선택 Character의 Audio를 검색·검수·발행·수정·비활성화하고 대용량 upload를 안전하게 제어한다.
**Phase Goal `P3`:** Task 3.1 → 3.4와 Phase 3 Gate로 Audio 조회·재생·발행·upload slice를 완성한다.
**Phase Goal `P4`:** Task 4.1 → 4.4와 Phase 4 Gate로 Audio 조회·재생·발행·upload slice를 완성한다.
- **시작 조건:** Phase 2 workspace core(`P2-T2`)와 `P1-T6` 완료.
- **완료 조건:** `P3-T1`~`P3-T4`, `P3-GATE` 완료. 오류·price 계약은 제공값 또는 명시된 최소 규칙으로 종결.
- **시작 조건:** Phase 3 workspace core(`P3-T2`)와 `P2-GATE` 완료.
- **완료 조건:** `P4-T1`~`P4-T4`, `P4-GATE` 완료. 오류·price 계약은 제공값 또는 명시된 최소 규칙으로 종결.
- **실행 순서:** 계약 확인 → 목록/상세/player → form/upload → 반응형·접근성.
**요구사항:** `AUDIO-001~028`, `FILE-001~002`, `FILE-006~009`, `FILE-012~013`, PRD `9`의 Audio 범위.
@@ -706,11 +806,11 @@ npm run build
- Create: `src/features/audio-contents/tests/{audio-list,audio-player,audio-form}.test.tsx`
- Create: `tests/e2e/audio-content.spec.ts`
### Task 3.1 Phase 계약 확인
### Task 4.1 Phase 계약 확인
**Goal 실행 `P3-T1`:** Audio 오류·price·theme·상태 계약 component map을 확정한다.
**Goal 실행 `P4-T1`:** Audio 오류·price·theme·상태 계약, mock scenario와 component map을 확정한다.
- **시작 조건:** `P2-T2`, `P1-T6` 완료, PRD `AUDIO-001~028`와 API Contract §5 확인.
- **시작 조건:** `P3-T2`, `P2-GATE` 완료, PRD `AUDIO-001~028`, `MOCK-001~009`와 API Contract §5 확인.
- **완료 증거:** 체크박스 전체, contract fixture와 상태/action inventory의 세 문서 일치.
- **범위 밖:** 오류 status/key 또는 price 상한 추정과 Audio UI 구현.
@@ -719,12 +819,13 @@ npm run build
- [ ] price 최대값이 제공되면 schema와 경계 test를 추가하고, 없으면 상한을 만들지 않는다.
- [ ] status query 미전송 시 server가 결과 집합을 결정한다는 계약을 유지하고 client fixture에서 임의 집합을 강제하지 않는다.
- [ ] 목록·상세·player·form/upload의 상태/action inventory를 작성하고 Page는 route/query/policy 조합, feature component는 Audio 규칙, shared component는 Phase 1 contract를 사용하도록 component map을 확정한다.
- [ ] 제공 계약 범위의 Audio seed와 목록·상세·player·theme·upload CRUD browser handler 시나리오를 확정한다.
### Task 3.2 목록·상세·player
### Task 4.2 목록·상세·player
**Goal 실행 `P3-T2`:** Audio active-only 목록·상세 복원과 안전한 단일 재생 흐름을 완성한다.
**Goal 실행 `P4-T2`:** Audio active-only 목록·상세 복원과 안전한 단일 재생 흐름을 완성한다.
- **시작 조건:** `P3-T1` 완료.
- **시작 조건:** `P4-T1` 완료.
- **완료 증거:** 체크박스 전체, list/detail/player test와 signed URL 비기록·no-auto-refetch 기록.
- **범위 밖:** 생성·수정·upload form.
@@ -736,12 +837,13 @@ npm run build
- [ ] media error를 signed URL 만료로 추정하지 않고 일반 오류·수동 재시도·페이지 새로고침 안내를 표시한다.
- [ ] media error만으로 목록/detail GET과 `play()`가 자동 재호출되지 않는 test를 작성한다.
- [ ] signed URL이 log·storage·분석 event로 전달되지 않는 test를 작성한다.
- [ ] mock mode는 실제 endpoint shape와 local preview audio asset으로 목록 → 상세 → 단일 재생 최종 UI를 확인한다.
### Task 3.3 발행 form·upload
### Task 4.3 발행 form·upload
**Goal 실행 `P3-T3`:** theme 선택, 즉시/예약 발행, multipart media와 진행률·취소·재시도를 완성한다.
**Goal 실행 `P4-T3`:** theme 선택, 즉시/예약 발행, multipart media와 진행률·취소·재시도를 완성한다.
- **시작 조건:** `P3-T1`, `P3-T2` 완료.
- **시작 조건:** `P4-T1`, `P4-T2` 완료.
- **완료 증거:** 체크박스 전체, contract/form/upload/file-boundary test, `OQ-009` 결정과 검증 기록.
- **범위 밖:** resumable upload, client codec 판정, 계약 없는 price 상한.
@@ -761,12 +863,13 @@ npm run build
- [ ] 415 server 오류를 field 안내로 보존하고 resumable upload는 만들지 않는다.
- [ ] 저장 성공은 detail/list cache를 갱신하고 soft delete 성공은 active-only 목록 이동과 toast로 끝낸다.
- [ ] Audio form을 실제로 작성한 뒤 `OQ-009``title`·`description`·`seriesIds` 상한 필요성을 판단하고, 구현 전 결정 문서를 갱신하거나 “상한 추가 없음”으로 종결한다.
- [ ] mock handler가 multipart request contract를 검증하고 create/update/deactivate 후 같은 store의 list/detail을 갱신하는 E2E를 작성한다.
### Task 3.4 Audio 반응형·접근성
### Task 4.4 Audio 반응형·접근성
**Goal 실행 `P3-T4`:** Audio viewport capability와 player/form 접근성 기준을 검증한다.
**Goal 실행 `P4-T4`:** Audio viewport capability와 player/form 접근성 기준을 검증한다.
- **시작 조건:** `P3-T2`, `P3-T3`의 활성 범위 완료.
- **시작 조건:** `P4-T2`, `P4-T3`의 활성 범위 완료.
- **완료 증거:** 체크박스 전체, 320px·keyboard·200% zoom·axe E2E 기록.
- **범위 밖:** 모바일 create/edit/deactivate/upload.
@@ -775,34 +878,35 @@ npm run build
- [ ] 320px에서 player control, error text, 긴 title이 overflow하지 않는 E2E를 작성한다.
- [ ] keyboard-only player/form, 200% zoom, axe critical·serious 0건을 확인한다.
### Phase 3 Gate
### Phase 4 Gate
**Goal 실행 `P3-GATE`:** Audio journey, upload 복구와 media 오류 불변식을 최종 판정한다.
**Goal 실행 `P4-GATE`:** Audio mock UI journey와 실제 server integration 상태를 분리해 판정한다.
- **시작 조건:** `P3-T1`~`P3-T4` 완료.
- **시작 조건:** `P4-T1`~`P4-T4` 완료.
- **완료 증거:** 아래 명령과 Expected 통과 및 Phase 검증 기록.
- **범위 밖:** 실패와 무관한 Series/Community 구현.
```bash
npm run test:run -- src/features/audio-contents src/shared/ui src/shared/validation
npm run e2e:mock -- tests/e2e/audio-content.spec.ts
npm run e2e -- tests/e2e/audio-content.spec.ts
npm run typecheck
npm run lint
npm run build
```
**Expected:** Audio 즉시/예약 생성 → 진행률/취소/재시도 → 목록·상세 재생 → 수정 → soft delete가 통과하고 media error가 자동 refetch·자동 재생을 0회 발생시킨다.
**Expected:** mock mode에서 Audio 즉시/예약 생성 → 진행률/취소/재시도 → 목록·상세 재생 → 수정 → soft delete의 최종 UI가 통과하고 media error가 자동 refetch·자동 재생을 0회 발생시킨다. server mode는 별도 결과를 기록한다.
---
## Phase 4. Series vertical slice
## Phase 5. Series vertical slice
**목표:** 선택 Character의 Series를 생성·수정·비활성화하고 Audio 연결·해제와 활성 Series 전체 순서를 관리한다.
**Phase Goal `P4`:** Task 4.1 → 4.4와 Phase 4 Gate로 Series CRUD·연결·전체 순서 slice를 완성한다.
**Phase Goal `P5`:** Task 5.1 → 5.4와 Phase 5 Gate로 Series CRUD·연결·전체 순서 slice를 완성한다.
- **시작 조건:** `P3-T2`의 Audio 조회 API 완료.
- **완료 조건:** `P4-T1`~`P4-T4`, `P4-GATE` 완료. 계약 없는 genre·연결·순서는 구현 또는 명시적 제외 결정으로 종결.
- **시작 조건:** `P4-T2`의 Audio 조회 API 완료.
- **완료 조건:** `P5-T1`~`P5-T4`, `P5-GATE` 완료. 계약 없는 genre·연결·순서는 구현 또는 명시적 제외 결정으로 종결.
- **실행 순서:** 계약 확인 → CRUD → 연결/순서 → 반응형·접근성.
**요구사항:** `SERIES-001~013`, `FILE-001~002`, `FILE-005`, `FILE-007~009`, `FILE-012`, `FILE-015`, PRD `9`의 Series 범위.
@@ -821,11 +925,11 @@ npm run build
- Create: `src/features/series/tests/{series-form,series-contents,series-order}.test.tsx`
- Create: `tests/e2e/series.spec.ts`
### Task 4.1 Phase 계약 확인
### Task 5.1 Phase 계약 확인
**Goal 실행 `P4-T1`:** Series genre·연결 후보·전체 순서·오류 계약 component map을 확정한다.
**Goal 실행 `P5-T1`:** Series genre·연결 후보·전체 순서·오류 계약, mock scenario와 component map을 확정한다.
- **시작 조건:** `P3-T2` 완료, PRD `SERIES-001~013`와 API Contract §6 확인.
- **시작 조건:** `P4-T2` 완료, PRD `SERIES-001~013`, `MOCK-001~009`와 API Contract §6 확인.
- **완료 증거:** 체크박스 전체, 제공 계약 또는 제외 결정의 세 문서 일치, 상태/action inventory.
- **범위 밖:** 계약 없는 genre/연결/순서 network 구현.
@@ -834,12 +938,13 @@ npm run build
- [ ] 활성 Series가 50개를 넘을 때 전체를 누락 없이 읽는 방식과 누락 ID·동시 변경 충돌 오류를 기록한다.
- [ ] 계약이 없는 연결·순서·genre 기능은 추측 구현하지 않고 제외/후속 여부를 문서에서 먼저 결정한다.
- [ ] 목록·상세·form·연결·순서 화면의 상태/action inventory를 작성하고 Page는 route/query/policy 조합, feature component는 Series 규칙, shared component는 Phase 1 contract를 사용하도록 component map을 확정한다.
- [ ] 제공 계약 범위만 Series CRUD·연결·순서 browser fixture로 만들고 genre·후보·충돌 계약 미제공 부분은 mock에서도 추정하지 않는다.
### Task 4.2 Series CRUD
### Task 5.2 Series CRUD
**Goal 실행 `P4-T2`:** Series CRUD, enum·요일·image와 soft delete 규칙을 완성한다.
**Goal 실행 `P5-T2`:** Series CRUD, enum·요일·image와 soft delete 규칙을 완성한다.
- **시작 조건:** `P4-T1` 완료.
- **시작 조건:** `P5-T1` 완료.
- **완료 증거:** 체크박스 전체, contract/form/image/list/detail/deactivate test와 검증 기록.
- **범위 밖:** Audio 연결·해제와 전체 순서 저장.
@@ -852,12 +957,13 @@ npm run build
- [ ] genre 계약이 제공됐다면 이름 검색 후 `genreId`만 전송하는 Combobox를 test한다.
- [ ] Series image JPEG/PNG·10MB, `210:297`, `height=round(width×297÷210)`, 최대 1000×1414, 1px 오차, no-upscale을 test한다.
- [ ] 일반 update와 soft delete의 `isActive` 규칙, soft delete 후 목록 이동·toast를 test한다.
- [ ] mock store로 Series CRUD 후 list/detail과 server state enum이 일관되게 갱신되는 최종 UI를 확인한다.
### Task 4.3 Audio 연결·해제·전체 순서
### Task 5.3 Audio 연결·해제·전체 순서
**Goal 실행 `P4-T3`:** Series Audio 연결·해제와 active Series 전체 순서를 안전하게 관리한다.
**Goal 실행 `P5-T3`:** Series Audio 연결·해제와 active Series 전체 순서를 안전하게 관리한다.
- **시작 조건:** `P4-T1`, `P4-T2` 완료 및 관련 P0 계약 제공.
- **시작 조건:** `P5-T1`, `P5-T2` 완료 및 관련 P0 계약 제공.
- **완료 증거:** 체크박스 전체, link/unlink/reorder contract·interaction test, 충돌 보존, `OQ-009` 결정 기록.
- **범위 밖:** 계약 없는 후보/전체 로딩/충돌 동작의 추정 구현.
@@ -869,12 +975,13 @@ npm run build
- [ ] drag-and-drop과 동일한 결과를 keyboard·위/아래 button으로 만들 수 있는 test를 작성한다.
- [ ] server의 누락 ID·동시 충돌 오류에서 기존 화면 순서를 보존하고 재조회/재시도 안내를 제공한다.
- [ ] Series form·연결·순서 UI를 실제로 작성한 뒤 `OQ-009`의 title·introduction·keywords·writer·studio·days·contentIds·seriesIds 상한 필요성을 판단하고, 구현 전 결정 문서를 갱신하거나 “상한 추가 없음”으로 종결한다.
- [ ] 계약 제공 후 mock handler가 연결·해제와 전체 순서 payload를 검증하고 store 결과를 반영하는 E2E를 작성한다.
### Task 4.4 Series 반응형·접근성
### Task 5.4 Series 반응형·접근성
**Goal 실행 `P4-T4`:** Series viewport capability와 form·연결·정렬 접근성을 검증한다.
**Goal 실행 `P5-T4`:** Series viewport capability와 form·연결·정렬 접근성을 검증한다.
- **시작 조건:** `P4-T2`, `P4-T3`의 활성 범위 완료.
- **시작 조건:** `P5-T2`, `P5-T3`의 활성 범위 완료.
- **완료 증거:** 체크박스 전체, 320px·keyboard·200% zoom·axe E2E 기록.
- **범위 밖:** 모바일 CRUD·연결·순서 mutation.
@@ -882,39 +989,40 @@ npm run build
- [ ] desktop/tablet에서 전체 관리 흐름을 제공한다.
- [ ] keyboard-only 요일·genre·연결·정렬, 320px 조회, 200% zoom, axe critical·serious 0건을 확인한다.
### Phase 4 Gate
### Phase 5 Gate
**Goal 실행 `P4-GATE`:** Series CRUD·연결·전체 reorder journey와 serializer 불변식을 판정한다.
**Goal 실행 `P5-GATE`:** Series mock UI journey와 실제 server integration 상태를 분리해 판정한다.
- **시작 조건:** `P4-T1`~`P4-T4` 완료.
- **시작 조건:** `P5-T1`~`P5-T4` 완료.
- **완료 증거:** 아래 명령과 Expected 통과, 외부 의존 상태와 Phase 검증 기록.
- **범위 밖:** 실패와 무관한 Community/FanTalk 구현.
```bash
npm run test:run -- src/features/series
npm run e2e:mock -- tests/e2e/series.spec.ts
npm run e2e -- tests/e2e/series.spec.ts
npm run typecheck
npm run lint
npm run build
```
**Expected:** Series 생성 → 수정 → Audio 연결/해제 → active 전체 reorder → soft delete가 통과하고 잘못된 enum·부분 순서 payload가 생성되지 않는다.
**Expected:** 제공 계약 범위의 mock mode에서 Series 생성 → 수정 → Audio 연결/해제 → active 전체 reorder → soft delete 최종 UI가 통과하고 잘못된 enum·부분 순서 payload가 생성되지 않는다. server mode는 별도 결과를 기록한다.
---
## Phase 5. Community vertical slice
## Phase 6. Community vertical slice
**목표:** 별도 상세 route/GET 없이 active Community 목록과 Sheet만으로 게시글 등록·조회·수정·고정·비활성화·첨부 재생을 완료한다.
**Phase Goal `P5`:** Task 5.1 → 5.4와 Phase 5 Gate로 목록 기반 Community Sheet·media slice를 완성한다.
**Phase Goal `P6`:** Task 6.1 → 6.4와 Phase 6 Gate로 목록 기반 Community Sheet·media slice를 완성한다.
- **시작 조건:** `P1-T6` file/media primitive`P3-T2` player integration 완료.
- **완료 조건:** `P5-T1`~`P5-T4`, `P5-GATE` 완료. 오류·price는 제공 계약 또는 최소 규칙으로 종결.
- **시작 조건:** `P2-GATE``P4-T2` player integration 완료.
- **완료 조건:** `P6-T1`~`P6-T4`, `P6-GATE` 완료. 오류·price는 제공 계약 또는 최소 규칙으로 종결.
- **실행 순서:** 계약 확인 → 목록/Sheet → form/media → 반응형·접근성.
**요구사항:** `COMMUNITY-001~011`, `FILE-001~004`, `FILE-007~009`, `FILE-011~014`, PRD `9`의 Community 범위.
**외부 의존:** Community 신규 오류 계약, optional P1 price 상한. Comments는 Phase 7에서 연결한다.
**외부 의존:** Community 신규 오류 계약, optional P1 price 상한. Comments는 Phase 8에서 연결한다.
**주요 Files:**
@@ -928,23 +1036,24 @@ npm run build
- Create: `src/features/community-posts/tests/{community-list,community-sheet}.test.tsx`
- Create: `tests/e2e/community-post.spec.ts`
### Task 5.1 Phase 계약 확인
### Task 6.1 Phase 계약 확인
**Goal 실행 `P5-T1`:** Community 오류·media·price 계약 목록/Sheet component map을 확정한다.
**Goal 실행 `P6-T1`:** Community 오류·media·price 계약, mock scenario와 목록/Sheet component map을 확정한다.
- **시작 조건:** `P1-T6`, `P3-T2` 완료, PRD `COMMUNITY-001~011` API Contract §7 확인.
- **시작 조건:** `P2-GATE`, `P4-T2` 완료, PRD `COMMUNITY-001~011`, `MOCK-001~009` API Contract §7 확인.
- **완료 증거:** 체크박스 전체, contract fixture와 상태/action inventory의 세 문서 일치.
- **범위 밖:** price 상한·오류 key 추정과 Comments 구현.
- [ ] Community 오류 status/message key와 media upload 오류 fixture를 기록한다.
- [ ] price 최대값이 제공되면 Audio와 같은 정책으로 갱신하고, 없으면 0 이상 정수만 유지한다.
- [ ] 목록·Sheet·form/media의 상태/action inventory를 작성하고 Page는 collection query/policy 조합, feature component는 Community 규칙, shared component는 Phase 1 contract를 사용하도록 component map을 확정한다.
- [ ] Community active list, Sheet, pin, media와 CRUD의 deterministic browser fixture 시나리오를 확정한다.
### Task 5.2 목록·collection Sheet
### Task 6.2 목록·collection Sheet
**Goal 실행 `P5-T2`:** 전용 detail route/GET 없는 active 목록과 collection Sheet mutation 흐름을 완성한다.
**Goal 실행 `P6-T2`:** 전용 detail route/GET 없는 active 목록과 collection Sheet mutation 흐름을 완성한다.
- **시작 조건:** `P5-T1` 완료.
- **시작 조건:** `P6-T1` 완료.
- **완료 증거:** 체크박스 전체, list/Sheet/router/cache/pin/deactivate test와 detail GET 0회 기록.
- **범위 밖:** 댓글과 제공 계약에 없는 Community 검색.
@@ -955,12 +1064,13 @@ npm run build
- [ ] 고정/해제 후 `isFixed/fixedAtUtc`를 server 값으로 표시한다.
- [ ] soft delete 응답의 `isFixed=false`, `fixedAtUtc=null`을 contract test로 고정한다.
- [ ] soft delete 성공 시 Sheet 종료, active-only 목록 재조회·항목 제거, 성공 toast를 확인한다.
- [ ] mock mode도 전용 detail GET 없이 list store만으로 Sheet와 pin/deactivate 최종 UI를 갱신한다.
### Task 5.3 게시글 form·첨부 media
### Task 6.3 게시글 form·첨부 media
**Goal 실행 `P5-T3`:** Community form, JPEG/PNG/GIF와 audio 첨부·재생 규칙을 완성한다.
**Goal 실행 `P6-T3`:** Community form, JPEG/PNG/GIF와 audio 첨부·재생 규칙을 완성한다.
- **시작 조건:** `P5-T1`, `P5-T2` 완료.
- **시작 조건:** `P6-T1`, `P6-T2` 완료.
- **완료 증거:** 체크박스 전체, serializer/form/media/file-boundary/player test, `OQ-009` 결정 기록.
- **범위 밖:** GIF 재인코딩, URL 갱신 전용 요청, Comments.
@@ -969,17 +1079,18 @@ npm run build
- [ ] JPEG/PNG는 자유 ratio crop·최대 800px·no-upscale을 적용한다.
- [ ] GIF는 Community에서만 허용하고 crop Dialog/canvas/re-encode 없이 원본 ratio·animation을 유지한다.
- [ ] GIF 원본 width 800px은 허용하고 801px은 제출 전에 거부한다.
- [ ] 첨부 Audio는 Phase 1의 공통 audio file policy와 `FileField`를 Phase 3과 동일하게 조합해 MP3/AAC/M4A, x-m4a, `1,024,000,000 bytes`, WAV 거부 규칙을 재사용한다.
- [ ] 첨부 Audio는 Phase 1의 공통 audio file policy와 `FileField`를 Phase 4와 동일하게 조합해 MP3/AAC/M4A, x-m4a, `1,024,000,000 bytes`, WAV 거부 규칙을 재사용한다.
- [ ] 첨부 Audio가 있으면 목록 Card/row와 Sheet에 공통 player를 렌더링한다.
- [ ] media error가 detail/list refetch·URL 재발급·자동 play를 발생시키지 않는다.
- [ ] 사용자 새로고침이나 mutation cache invalidation으로 목록이 정상 재조회된 때만 새 `audioSignedUrl`을 사용한다.
- [ ] Community Sheet/form을 실제로 작성한 뒤 `OQ-009``content` 상한 필요성을 판단하고, 구현 전 결정 문서를 갱신하거나 “상한 추가 없음”으로 종결한다.
- [ ] mock handler가 multipart contract를 검증하고 local preview media asset으로 create/update/play 최종 UI를 재현한다.
### Task 5.4 Community 반응형·접근성
### Task 6.4 Community 반응형·접근성
**Goal 실행 `P5-T4`:** Community viewport capability와 Sheet/media/form 접근성을 검증한다.
**Goal 실행 `P6-T4`:** Community viewport capability와 Sheet/media/form 접근성을 검증한다.
- **시작 조건:** `P5-T2`, `P5-T3`의 활성 범위 완료.
- **시작 조건:** `P6-T2`, `P6-T3`의 활성 범위 완료.
- **완료 증거:** 체크박스 전체, focus trap/복귀·320px·keyboard·200% zoom·axe E2E 기록.
- **범위 밖:** 모바일 등록·수정·고정·비활성화.
@@ -987,34 +1098,35 @@ npm run build
- [ ] desktop/tablet에는 전체 관리 흐름을 제공한다.
- [ ] Sheet focus trap/복귀, keyboard media/form, 320px overflow, 200% zoom, axe critical·serious 0건을 확인한다.
### Phase 5 Gate
### Phase 6 Gate
**Goal 실행 `P5-GATE`:** Community 목록/Sheet CRUD·pin·media journey와 no-detail 불변식을 판정한다.
**Goal 실행 `P6-GATE`:** Community mock UI journey와 실제 server integration 상태를 분리해 판정한다.
- **시작 조건:** `P5-T1`~`P5-T4` 완료.
- **시작 조건:** `P6-T1`~`P6-T4` 완료.
- **완료 증거:** 아래 명령과 Expected 통과 및 Phase 검증 기록.
- **범위 밖:** 실패와 무관한 FanTalk/Comments 구현.
```bash
npm run test:run -- src/features/community-posts src/shared/validation
npm run e2e:mock -- tests/e2e/community-post.spec.ts
npm run e2e -- tests/e2e/community-post.spec.ts
npm run typecheck
npm run lint
npm run build
```
**Expected:** create → 목록 item Sheet 조회/수정 → pin/unpin → 첨부 재생 → soft delete가 통과하고 Community detail GET·detail route 호출은 0건이다.
**Expected:** mock mode에서 create → 목록 item Sheet 조회/수정 → pin/unpin → 첨부 재생 → soft delete 최종 UI가 통과하고 Community detail GET·detail route 호출은 0건이다. server mode는 별도 결과를 기록한다.
---
## Phase 6. FanTalk vertical slice
## Phase 7. FanTalk vertical slice
**목표:** 모든 viewport에서 FanTalk를 최신순·답변 상태로 조회하고 답변을 한 번 작성한 뒤 기존 답변만 수정한다.
**Phase Goal `P6`:** Task 6.1 → 6.3과 Phase 6 Gate로 FanTalk 조회·단일 답변·수정 slice를 완성한다.
**Phase Goal `P7`:** Task 7.1 → 7.3과 Phase 7 Gate로 FanTalk 조회·단일 답변·수정 slice를 완성한다.
- **시작 조건:** `P2-T2` workspace core 완료.
- **완료 조건:** 핵심 계약이 제공되면 `P6-T1`~`P6-T3`, `P6-GATE` 완료. 미제공이면 Phase 제외/후속 결정 문서화로 종결.
- **시작 조건:** `P3-T2` workspace core`P2-GATE` 완료.
- **완료 조건:** 핵심 계약이 제공되면 `P7-T1`~`P7-T3`, `P7-GATE` 완료. 미제공이면 Phase 제외/후속 결정 문서화로 종결.
- **실행 순서:** 계약 확인 → 목록/답변 → 반응형·접근성.
**요구사항:** `FANTALK-001~008`, PRD `9`의 FanTalk 범위.
@@ -1033,11 +1145,11 @@ npm run build
- Create: `tests/e2e/fan-talk.spec.ts`
- Modify: `src/app/router.tsx`
### Task 6.1 Phase 계약 확인
### Task 7.1 Phase 계약 확인
**Goal 실행 `P6-T1`:** FanTalk 목록·상세·수정·유일성·오류 계약 component map을 확정한다.
**Goal 실행 `P7-T1`:** FanTalk 목록·상세·수정·유일성·오류 계약, mock 가능 범위와 component map을 확정한다.
- **시작 조건:** `P2-T2` 완료, PRD `FANTALK-001~008`와 API Contract §8~9 확인.
- **시작 조건:** `P3-T2`, `P2-GATE` 완료, PRD `FANTALK-001~008`, `MOCK-001~009`와 API Contract §8~9 확인.
- **완료 증거:** 체크박스 전체, 제공 계약 또는 Phase 제외/후속 결정의 세 문서 일치.
- **범위 밖:** 임시 endpoint·placeholder DTO·production mock adapter.
@@ -1045,12 +1157,13 @@ npm run build
- [ ] 답변 1개를 server가 원자적으로 강제하는 방식과 중복 생성 비2xx status/message key를 기록한다.
- [ ] 계약이 없으면 임시 endpoint·placeholder DTO·mock production adapter를 만들지 않고 Phase 제외/후속 결정을 문서화한다.
- [ ] 목록·상세·reply form의 상태/action inventory를 작성하고 Page는 route/query/policy 조합, feature component는 FanTalk 규칙, shared component는 Phase 1 contract를 사용하도록 component map을 확정한다.
- [ ] 목록·상세·수정·유일성 계약이 모두 제공된 뒤에만 browser fixture를 만들고, 현재 제공된 POST만으로 최종 FanTalk mock UI를 추정하지 않는다.
### Task 6.2 목록·답변 생성·수정
### Task 7.2 목록·답변 생성·수정
**Goal 실행 `P6-T2`:** 최신순/filter 목록과 답변 1회 생성·기존 답변 수정 흐름을 완성한다.
**Goal 실행 `P7-T2`:** 최신순/filter 목록과 답변 1회 생성·기존 답변 수정 흐름을 완성한다.
- **시작 조건:** `P6-T1`에서 핵심 계약 제공 확인.
- **시작 조건:** `P7-T1`에서 핵심 계약 제공 확인.
- **완료 증거:** 체크박스 전체, list/detail/reply contract·UI test, 중복 제출/오류 복구, `OQ-009` 결정 기록.
- **범위 밖:** 답변 삭제·두 번째 답변과 계약 없는 network 동작.
@@ -1062,12 +1175,13 @@ npm run build
- [ ] server 중복 오류를 받으면 최신 detail을 재조회해 edit 상태로 전환하고 status/key를 추정 분기하지 않는다.
- [ ] 저장 중 중복 제출 차단, visible label, 오류 연결, 성공 live feedback을 test한다.
- [ ] FanTalk reply form을 실제로 작성한 뒤 `OQ-009``content` 상한 필요성을 판단하고, 구현 전 결정 문서를 갱신하거나 “상한 추가 없음”으로 종결한다.
- [ ] 계약 제공 후 mock store로 미답변 → 답변 생성 → 수정과 중복 오류의 최종 UI E2E를 작성한다.
### Task 6.3 FanTalk 반응형·접근성
### Task 7.3 FanTalk 반응형·접근성
**Goal 실행 `P6-T3`:** 모든 viewport의 FanTalk 응대 capability와 접근성을 검증한다.
**Goal 실행 `P7-T3`:** 모든 viewport의 FanTalk 응대 capability와 접근성을 검증한다.
- **시작 조건:** `P6-T2` 완료.
- **시작 조건:** `P7-T2` 완료.
- **완료 증거:** 체크박스 전체, 320px keyboard viewport·keyboard-only·200% zoom·axe E2E 기록.
- **범위 밖:** viewport별 기능 축소와 답변 삭제.
@@ -1075,34 +1189,35 @@ npm run build
- [ ] 320px에서 keyboard가 reply input/submit을 가리지 않는 E2E를 작성한다.
- [ ] keyboard-only filter/detail/create/edit, 200% zoom, axe critical·serious 0건을 확인한다.
### Phase 6 Gate
### Phase 7 Gate
**Goal 실행 `P6-GATE`:** FanTalk 단일 답변 불변식과 모든 viewport journey를 최종 판정한다.
**Goal 실행 `P7-GATE`:** FanTalk mock UI journey와 실제 server integration 상태를 분리해 판정한다.
- **시작 조건:** `P6-T1`~`P6-T3` 활성 범위 완료.
- **시작 조건:** `P7-T1`~`P7-T3` 활성 범위 완료.
- **완료 증거:** 아래 명령과 Expected 통과 또는 Phase 제외 결정의 검증 기록.
- **범위 밖:** 계약 미제공 상태에서 network integration 완료 주장.
```bash
npm run test:run -- src/features/fan-talks
npm run e2e:mock -- tests/e2e/fan-talk.spec.ts
npm run e2e -- tests/e2e/fan-talk.spec.ts
npm run typecheck
npm run lint
npm run build
```
**Expected:** 미답변 조회 → 답변 1회 생성 → 기존 답변 수정 모든 viewport에서 통과하며 두 번째 reply 생성과 delete UI가 없다.
**Expected:** 핵심 계약 제공 후 mock mode에서 미답변 조회 → 답변 1회 생성 → 기존 답변 수정 최종 UI가 모든 viewport에서 통과하며 두 번째 reply 생성과 delete UI가 없다. 계약 미제공이면 mock fixture도 만들지 않고 연동 대기로 기록한다.
---
## Phase 7. Comments vertical slice
## Phase 8. Comments vertical slice
**목표:** Audio detail과 Community Sheet 양쪽에서 같은 2단계 댓글 UX를 제공하고 작성자별 수정·soft delete 권한을 일관되게 적용한다.
**Phase Goal `P7`:** Task 7.1 → 7.4와 Phase 7 Gate로 두 target의 2단계 Comments slice를 완성한다.
**Phase Goal `P8`:** Task 8.1 → 8.4와 Phase 8 Gate로 두 target의 2단계 Comments slice를 완성한다.
- **시작 조건:** `P3-T2` Audio detail `P5-T2` Community Sheet 완료.
- **완료 조건:** 핵심 계약이 제공되면 `P7-T1`~`P7-T4`, `P7-GATE` 완료. 미제공이면 Phase 제외/후속 결정 문서화로 종결.
- **시작 조건:** `P4-T2` Audio detail, `P6-T2` Community Sheet`P2-GATE` 완료.
- **완료 조건:** 핵심 계약이 제공되면 `P8-T1`~`P8-T4`, `P8-GATE` 완료. 미제공이면 Phase 제외/후속 결정 문서화로 종결.
- **실행 순서:** 계약 확인 → target/thread → 권한 action → 반응형·접근성.
**요구사항:** `COMMENT-001~006`, PRD `9`의 Comments 범위.
@@ -1120,11 +1235,11 @@ npm run build
- Create: `tests/e2e/comments.spec.ts`
- Modify: `AudioContentDetailPage.tsx`, `CommunityPostSheet.tsx`
### Task 7.1 Phase 계약 확인
### Task 8.1 Phase 계약 확인
**Goal 실행 `P7-T1`:** 두 댓글 target의 CRUD·2단계·권한 오류 계약 component map을 확정한다.
**Goal 실행 `P8-T1`:** 두 댓글 target의 CRUD·2단계·권한 오류 계약, mock 가능 범위와 component map을 확정한다.
- **시작 조건:** `P3-T2`, `P5-T2` 완료, PRD `COMMENT-001~006` API Contract §9 확인.
- **시작 조건:** `P4-T2`, `P6-T2`, `P2-GATE` 완료, PRD `COMMENT-001~006`, `MOCK-001~009` API Contract §9 확인.
- **완료 증거:** 체크박스 전체, 제공 계약 또는 Phase 제외/후속 결정의 세 문서 일치.
- **범위 밖:** endpoint 이름 추정과 client-only permission 완료 주장.
@@ -1132,12 +1247,13 @@ npm run build
- [ ] root/direct reply 정확히 2단계인 server rule과 fan content 삭제 권한 오류 status/message key를 기록한다.
- [ ] 계약이 없으면 target endpoint를 이름만 보고 추정하거나 client-only permission을 완료로 간주하지 않는다.
- [ ] Audio detail·Community Sheet 진입별 thread/form/action inventory를 작성하고 host Page/Sheet는 target·query 조합, Comments component는 thread·permission 규칙, shared component는 Phase 1 contract를 사용하도록 component map을 확정한다.
- [ ] target별 CRUD·2단계·권한 오류 계약이 제공된 뒤에만 browser fixture를 만들고 현재 미제공 계약을 mock DTO로 추정하지 않는다.
### Task 7.2 target adapter·2단계 thread
### Task 8.2 target adapter·2단계 thread
**Goal 실행 `P7-T2`:** Audio/Community target adapter와 정확히 2단계인 comment thread 조회를 완성한다.
**Goal 실행 `P8-T2`:** Audio/Community target adapter와 정확히 2단계인 comment thread 조회를 완성한다.
- **시작 조건:** `P7-T1`에서 핵심 계약 제공 확인.
- **시작 조건:** `P8-T1`에서 핵심 계약 제공 확인.
- **완료 증거:** 체크박스 전체, target contract/thread/state/page test와 Community 상태 보존 기록.
- **범위 밖:** 작성자별 mutation action.
@@ -1147,11 +1263,11 @@ npm run build
- [ ] root와 direct reply만 렌더링하고 reply에는 reply action이 없음을 test한다.
- [ ] long content, loading·empty·error·retry, page 갱신을 양 target에서 test한다.
### Task 7.3 작성자별 action
### Task 8.3 작성자별 action
**Goal 실행 `P7-T3`:** AI Character와 fan 작성자별 수정·soft delete 권한을 일관되게 적용한다.
**Goal 실행 `P8-T3`:** AI Character와 fan 작성자별 수정·soft delete 권한을 일관되게 적용한다.
- **시작 조건:** `P7-T1`, `P7-T2` 완료.
- **시작 조건:** `P8-T1`, `P8-T2` 완료.
- **완료 증거:** 체크박스 전체, permission/mutation/session/read-only test, `OQ-009` 결정 기록.
- **범위 밖:** fan edit와 reply의 reply.
@@ -1162,12 +1278,13 @@ npm run build
- [ ] Character workspace read-only 정책이 모든 comment mutation도 차단하는 test를 작성한다.
- [ ] 중복 제출, server permission 오류, session 401/403이 공통 정책을 따르는지 test한다.
- [ ] Comment thread/form을 실제로 작성한 뒤 `OQ-009``content` 상한 필요성을 판단하고, 구현 전 결정 문서를 갱신하거나 “상한 추가 없음”으로 종결한다.
- [ ] 계약 제공 후 mock store로 양 target의 root/reply CRUD와 작성자별 권한 오류 최종 UI E2E를 작성한다.
### Task 7.4 Comments 반응형·접근성
### Task 8.4 Comments 반응형·접근성
**Goal 실행 `P7-T4`:** 모든 viewport의 Comments CRUD capability와 thread/form/dialog 접근성을 검증한다.
**Goal 실행 `P8-T4`:** 모든 viewport의 Comments CRUD capability와 thread/form/dialog 접근성을 검증한다.
- **시작 조건:** `P7-T2`, `P7-T3` 완료.
- **시작 조건:** `P8-T2`, `P8-T3` 완료.
- **완료 증거:** 체크박스 전체, 320px·keyboard-only·focus 복귀·200% zoom·axe E2E 기록.
- **범위 밖:** 모바일 기능 축소.
@@ -1175,34 +1292,35 @@ npm run build
- [ ] 320px에서 긴 댓글, reply indentation, action menu, keyboard 입력이 overflow하지 않는 E2E를 작성한다.
- [ ] keyboard-only root/reply 작성·수정·delete dialog, focus 복귀, 200% zoom, axe critical·serious 0건을 확인한다.
### Phase 7 Gate
### Phase 8 Gate
**Goal 실행 `P7-GATE`:** 두 target의 2단계 CRUD·권한·모바일 journey를 최종 판정한다.
**Goal 실행 `P8-GATE`:** Comments mock UI journey와 실제 server integration 상태를 분리해 판정한다.
- **시작 조건:** `P7-T1`~`P7-T4` 활성 범위 완료.
- **시작 조건:** `P8-T1`~`P8-T4` 활성 범위 완료.
- **완료 증거:** 아래 명령과 Expected 통과 또는 Phase 제외 결정의 검증 기록.
- **범위 밖:** 계약 미제공 상태에서 network integration 완료 주장.
```bash
npm run test:run -- src/features/comments
npm run e2e:mock -- tests/e2e/comments.spec.ts
npm run e2e -- tests/e2e/comments.spec.ts
npm run typecheck
npm run lint
npm run build
```
**Expected:** Audio와 Community 두 진입점에서 2단계 댓글 CRUD·권한·모바일 흐름이 통과하고 reply의 reply 및 fan edit request는 생성되지 않는다.
**Expected:** 핵심 계약 제공 후 mock mode에서 Audio와 Community 두 진입점 2단계 댓글 CRUD·권한·모바일 최종 UI가 통과하고 reply의 reply 및 fan edit request는 생성되지 않는다. 계약 미제공이면 mock fixture도 만들지 않고 연동 대기로 기록한다.
---
## Phase 8. 교차 회귀·인수인계
## Phase 9. 교차 회귀·인수인계
**목표:** 새 기능을 추가하지 않고 활성 릴리스 범위 전체가 PRD, API Contract, 보안, 반응형, 접근성 기준을 만족한다는 최신 증거를 남긴다.
**Phase Goal `P8`:** Task 8.1 → 8.3과 Phase 8 Gate로 활성 릴리스 범위의 최종 인수 증거를 완성한다.
**Phase Goal `P9`:** Task 9.1 → 9.3과 Phase 9 Gate로 활성 릴리스 범위의 최종 인수 증거를 완성한다.
- **시작 조건:** 활성 범위의 `P0-GATE`~`P7-GATE` 완료 또는 명시적 제외 결정.
- **완료 조건:** `P8-T1`~`P8-T3`, `P8-GATE` 완료, 전체 자동 Gate 0 failure/0 error와 수동 검증 기록.
- **시작 조건:** 활성 범위의 `P0-GATE`~`P8-GATE` 완료 또는 명시적 제외 결정.
- **완료 조건:** `P9-T1`~`P9-T3`, `P9-GATE` 완료, mock/server 전체 자동 Gate 0 failure/0 error와 수동 검증 기록.
- **실행 순서:** 교차 journey/오류 → 반응형/접근성/보안 → 문서/품질 → 전체 Gate.
**주요 Files:**
@@ -1211,11 +1329,11 @@ npm run build
- Modify: `README.md`
- Modify: `docs/20260725_AI캐릭터관리자웹/{prd.md,api-contract.md,plan-task.md}` only when actual implementation decision differs.
### Task 8.1 교차 journey·오류 회귀
### Task 9.1 교차 journey·오류 회귀
**Goal 실행 `P8-T1`:** 활성 도메인의 교차 journey, 오류 매핑과 serializer/security 불변식을 회귀 검증한다.
**Goal 실행 `P9-T1`:** 활성 도메인의 mock/server 교차 journey, 오류 매핑과 serializer/security 불변식을 회귀 검증한다.
- **시작 조건:** 활성 범위의 Phase 0~7 Gate 완료 또는 제외 결정.
- **시작 조건:** 활성 범위의 Phase 0~8 Gate 완료 또는 제외 결정.
- **완료 증거:** 체크박스 전체, resource-workflows/error-mapping E2E와 request 0회·serializer fixture 기록.
- **범위 밖:** 새 기능과 계약 미제공 제외 범위의 가짜 journey.
@@ -1225,12 +1343,14 @@ npm run build
- [ ] inactive Character workspace에서 모든 하위 mutation request가 0건인지 검증한다.
- [ ] media error로 Audio/Community GET·URL 재발급·자동 `play()`가 발생하지 않는지 검증한다.
- [ ] 생성·일반 수정·soft delete serializer 불변식을 모든 도메인 fixture에서 다시 검증한다.
- [ ] 모든 활성 도메인의 mock handler가 정규화 endpoint·DTO를 사용하고 실제 backend request 0건으로 최종 UI journey를 재현하는지 검증한다.
- [ ] server mode의 404·network error가 mock으로 전환되지 않고 실제 오류 UI와 연동 대기 기록으로 남는지 검증한다.
### Task 8.2 반응형·접근성·보안 회귀
### Task 9.2 반응형·접근성·보안 회귀
**Goal 실행 `P8-T2`:** 지원 viewport/browser의 capability, 접근성과 민감정보 비기록을 전역 검증한다.
**Goal 실행 `P9-T2`:** 지원 viewport/browser의 capability, 접근성과 민감정보 비기록을 mock/server 양 mode에서 검증한다.
- **시작 조건:** `P8-T1` 완료.
- **시작 조건:** `P9-T1` 완료.
- **완료 증거:** 체크박스 전체, table-driven E2E·axe·수동 browser/keyboard/zoom/security·UX 검색 기록.
- **범위 밖:** 다크 모드 구현과 외부 WCAG 인증.
@@ -1243,12 +1363,13 @@ npm run build
- [ ] JWT, password, signed URL, file body가 log·storage·분석 event에 남지 않는지 검증한다.
- [ ] 각 Page가 승인된 component map대로 route/query/permission과 component 조합만 담당하고, domain 상호작용이 feature/shared component test로 분리됐는지 review한다.
- [ ] PRD `10.9`의 UX 검증 검색을 다시 실행하고 채택·제외 결과를 기록한다.
- [ ] mock mode banner가 모든 viewport에서 핵심 UI를 가리지 않고 실제 server와 혼동되지 않는지 확인한다.
### Task 8.3 문서·품질 Gate
### Task 9.3 문서·품질 Gate
**Goal 실행 `P8-T3`:** 요구사항 추적, 외부 의존·Open Question, README와 검증 기록을 실제 구현에 맞춘다.
**Goal 실행 `P9-T3`:** 요구사항 추적, 외부 의존·Open Question, mock/server 상태, README와 검증 기록을 실제 구현에 맞춘다.
- **시작 조건:** `P8-T1`, `P8-T2` 완료.
- **시작 조건:** `P9-T1`, `P9-T2` 완료.
- **완료 증거:** 체크박스 전체, PRD 수용 기준별 증거 연결, 독립 code review 수정과 관련 Gate 재실행 기록.
- **범위 밖:** 결정되지 않은 계약을 문서상 확정하는 행위.
@@ -1260,12 +1381,13 @@ npm run build
- [ ] README에 install, env, run, test, build, 지원 브라우저, 알려진 backend 제약을 기록한다.
- [ ] plan 하단 검증 기록에 무엇을/왜/어떻게와 실제 명령·성공/실패/불가 사유를 누적한다.
- [ ] 별도 code review를 받고 지적사항 수정 후 관련 Phase Gate와 전체 Gate를 다시 실행한다.
- [ ] 각 도메인의 `UI 확인 완료(mock)``실제 서버 연동 완료(server)`가 Progress·추적표·알려진 제약에서 일치하는지 확인한다.
### Phase 8 Gate
### Phase 9 Gate
**Goal 실행 `P8-GATE`:** 활성 릴리스 전체의 install·type·lint·unit·E2E·build·금지 패턴을 최종 판정한다.
**Goal 실행 `P9-GATE`:** 활성 릴리스 전체의 install·type·lint·unit·mock/server E2E·build·금지 패턴을 최종 판정한다.
- **시작 조건:** `P8-T1`~`P8-T3` 완료.
- **시작 조건:** `P9-T1`~`P9-T3` 완료.
- **완료 증거:** 아래 명령이 0 failure/0 error이고 Expected 및 수동 검증 결과가 최종 검증 기록에 누적됨.
- **범위 밖:** Gate 통과를 위한 test 삭제·완화, production placeholder, 계약 추정.
@@ -1285,6 +1407,7 @@ npm ci
npm run typecheck
npm run lint
npm run test:run
npm run e2e:mock
npm run e2e
npm run build
assert_no_match 'TODO|TBD|FIXME' src tests
@@ -1292,7 +1415,7 @@ assert_no_match "externalCharacterId|SUNDAY|MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FR
--glob '!**/*.test.*' --glob '!**/*.spec.*' --glob '!**/__tests__/**'
```
**Expected:** 전체 자동 Gate가 0 failure/0 error이고, `assert_no_match`는 no-match인 `rg` exit 1만 성공으로 바꾸며 `rg` 실행 오류는 실패로 전파한다. production source의 금지 값과 미완료 placeholder는 0건이어야 한다. 부정 test fixture의 금지 문자열은 허용하며 production 결과와 구분한다.
**Expected:** mock UI 전체 journey와 실제 server integration 결과가 분리 기록되고 production mock 활성화·404 자동 fallback이 0건이다. 전체 자동 Gate는 0 failure/0 error이며 production source의 금지 값과 미완료 placeholder는 0건이다.
## 5. 요구사항 추적표
@@ -1300,21 +1423,25 @@ assert_no_match "externalCharacterId|SUNDAY|MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FR
|---:|---|---|
| 0 | React+TypeScript+Vite, 지원 브라우저 기반 | `src/app/App.test.tsx`, `tests/e2e/smoke.spec.ts` |
| 1 | `AUTH-001~013`, `UX-001~002`, `FILE`의 domain-neutral component mechanics, §7, §10 공통, §11.1~11.2, §12 | `src/shared`, `src/features/auth`, `tests/e2e/auth.spec.ts` |
| 2 | `CHAR-001~014`, Character 관련 `FILE`, §7, §9 | `src/features/characters`, `tests/e2e/character-workspace.spec.ts` |
| 3 | `AUDIO-001~028`, Audio 관련 `FILE`, §9 | `src/features/audio-contents`, `tests/e2e/audio-content.spec.ts` |
| 4 | `SERIES-001~013`, Series 관련 `FILE`, §9 | `src/features/series`, `tests/e2e/series.spec.ts` |
| 5 | `COMMUNITY-001~011`, Community 관련 `FILE`, §9 | `src/features/community-posts`, `tests/e2e/community-post.spec.ts` |
| 6 | `FANTALK-001~008`, §9 | `src/features/fan-talks`, `tests/e2e/fan-talk.spec.ts` |
| 7 | `COMMENT-001~006`, §9 | `src/features/comments`, `tests/e2e/comments.spec.ts` |
| 8 | §9~10, §12~14, 활성 범위 전체 | 전체 unit/integration/E2E/build |
| 2 | `MOCK-001~009`, §11.1~11.2, §12~13 | `src/shared/mocks`, `tests/e2e/mock-preview-shell.spec.ts` |
| 3 | `CHAR-001~014`, Character 관련 `FILE`, `MOCK`, §7, §9 | `src/features/characters`, `tests/e2e/character-workspace.spec.ts` |
| 4 | `AUDIO-001~028`, Audio 관련 `FILE`, `MOCK`, §9 | `src/features/audio-contents`, `tests/e2e/audio-content.spec.ts` |
| 5 | `SERIES-001~013`, Series 관련 `FILE`, `MOCK`, §9 | `src/features/series`, `tests/e2e/series.spec.ts` |
| 6 | `COMMUNITY-001~011`, Community 관련 `FILE`, `MOCK`, §9 | `src/features/community-posts`, `tests/e2e/community-post.spec.ts` |
| 7 | `FANTALK-001~008`, `MOCK`, §9 | `src/features/fan-talks`, `tests/e2e/fan-talk.spec.ts` |
| 8 | `COMMENT-001~006`, `MOCK`, §9 | `src/features/comments`, `tests/e2e/comments.spec.ts` |
| 9 | §9~10, §12~14, 활성 범위 전체 | 전체 unit/integration/mock·server E2E/build |
`FILE-001~015`의 domain-neutral component mechanics는 Phase 1에서 먼저 만다. Character·Audio·Series·Community Phase는 자신의 allowed type·crop profile·GIF 예외 multipart 흐름을 소유하면서 공통 mechanics를 조합·검증하고, Phase 8에서 전체 matrix를 회귀 검증한다.
`FILE-001~015`의 domain-neutral component mechanics는 Phase 1에서 먼저 만들고 browser mock 기반은 Phase 2가 소유한다. Character·Audio·Series·Community Phase는 자신의 allowed type·crop profile·GIF 예외, multipart handler와 fixture 흐름을 소유하며 Phase 9에서 mock/server 전체 matrix를 회귀 검증한다.
## 6. 구현 완료 정의
- [ ] Phase 0~7의 활성 범위 Gate와 Phase 8 전체 Gate가 최신 실행에서 통과한다.
- [ ] Phase 0~8의 활성 범위 Gate와 Phase 9 전체 Gate가 최신 실행에서 통과한다.
- [ ] 모든 **확정** 요구사항이 구현, 명시적 non-goal, 또는 결정 기록이 있는 후속 범위 중 하나로 추적된다.
- [ ] 외부 의존을 추정 endpoint·placeholder DTO·임시 production mock으로 우회하지 않았다.
- [ ] 제공 계약 범위는 `dev:mock`에서 최종 UI를 확인할 수 있고, 계약 미제공 범위는 fixture로 추정하지 않았다.
- [ ] 각 도메인의 `UI 확인 완료(mock)``실제 서버 연동 완료(server)` 상태와 증거가 분리돼 있다.
- [ ] server mode의 404·network error가 mock으로 자동 fallback되지 않고 production build에서 browser mock이 활성화되지 않는다.
- [ ] 생성·일반 수정·soft delete payload와 enum 보정 contract test가 통과한다.
- [ ] desktop/tablet/mobile 기능 matrix가 route와 action policy 양쪽에서 일치한다.
- [ ] 파일 MIME·크기·crop·no-upscale·GIF·오디오 경계 test가 통과한다.
@@ -1486,3 +1613,16 @@ assert_no_match "externalCharacterId|SUNDAY|MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FR
- PRD `AUDIO-027~028`, API Contract §5.0/§5.3, Phase 3 요구사항·contract/form checklist 대조 — `themeId`와 공통 테마 목록 계약이 일치함을 확인.
- 애플리케이션 test/build — 문서와 goal 실행 계약만 변경해 실행하지 않음.
- 남은 항목: Phase 2 이후 구현과 각 도메인의 backend 제공 대기 계약.
### Mock Preview Phase 추가 및 후속 Phase 재번호 — 2026-07-27
- 무엇을: 제공된 API Contract 범위에서 backend 구현 전 최종 UI를 확인할 수 있도록 개발 전용 browser MSW 요구사항과 계약 경계를 추가하고, 완료된 Phase 0~1 다음에 신규 Phase 2를 배치했다. 기존 Character~최종 인수인계 Phase는 Phase 3~9로 이동하고 모든 활성 Task·Goal·Gate·의존성·추적표를 함께 재번호했다.
- 왜: 실제 API의 404를 mock으로 자동 대체하지 않으면서도 frontend가 최종 화면을 개발·검토할 수 있고, mock UI 확인과 실제 server integration 완료를 혼동하지 않기 위해서다.
- 어떻게:
- PRD `MOCK-001~009`, API Contract §1.5, Phase 2의 Task 2.1~2.3과 Gate 대조 — explicit `server | mock` mode, production 차단, no-auto-fallback, deterministic in-memory store와 mock banner 규칙 일치 확인.
- 활성 계획 검사 — Phase·Phase Goal·Gate·Gate Goal 각각 10개, 일반 구현 Task·Task Goal 각각 37개, Goal 선언 중복 0건, 활성 영역의 이전 Phase 번호 참조 0건.
- 문서 동기화 검사 — `docs/agent-guide/{prd,goal-plan}.md``docs/sample/{sample-prd,sample-plan-task}.md` 4개 모두 같은 mock 작성 규칙 포함, 미구현 `docs/agent-guide/{environment,scripts}.md``dev:mock`·`e2e:mock`·`VITE_API_MODE` 선반영 0건.
- Markdown fence 짝수 여부와 수정 문서의 내부 link 검사 — 이상 0건.
- `git diff --check -- <수정 문서 7개>` — 성공.
- 애플리케이션 test/build는 구현 코드와 설정을 변경하지 않았고 신규 명령도 아직 계획 상태이므로 실행하지 않는다.
- 남은 항목: Phase 2 구현 시 실제 `dev:mock`·`e2e:mock` 명령과 환경 변수를 만든 후 README와 `docs/agent-guide/{environment,scripts}.md`를 실체에 맞게 갱신한다. 계약 미제공 도메인은 backend 계약 수신 전 fixture를 만들지 않는다.

View File

@@ -86,6 +86,7 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
- 정식 WCAG 2.2 AA 인증 또는 외부 접근성 감사
- 백엔드가 담당할 creator 생성·프로필 동기화의 조건부 정책 변경
- 비활성 ID에 대한 상세 GET 반환 여부와 오류 status 등 백엔드 조회 정책 결정
- 실제 API의 404를 감지해 mock 응답으로 자동 전환하는 production fallback
## 5. Target Users
@@ -333,6 +334,20 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
| FILE-014 | 확정 | 커뮤니티 GIF의 원본 가로가 800px을 초과하면 등록을 거부한다. client에서 제출 전에 차단하고 server도 같은 제한을 검증한다. GIF를 축소·crop·재인코딩하지 않는다. |
| FILE-015 | 확정 | Series crop 결과의 세로 pixel은 `round(width × 297 ÷ 210)`으로 계산한다. 최대 폭에서는 1,000×1,414px이며 비율 검증은 계산된 세로값 기준 1px 이내 오차를 허용한다. |
### 8.9 개발 전용 Mock Preview
| ID | 상태 | 요구사항 |
|---|---|---|
| MOCK-001 | 확정 | 개발 환경은 명시적인 `server``mock` API mode를 제공한다. 기본 `npm run dev`는 실제 개발 API를 사용하고 `npm run dev:mock`만 browser MSW를 활성화한다. |
| MOCK-002 | 확정 | mock mode도 production과 같은 Page, Query, API client, endpoint path, request serializer와 response DTO를 사용한다. 별도 화면이나 mock 전용 API adapter를 만들지 않는다. |
| MOCK-003 | 확정 | 실제 API의 404·network error를 감지해 mock으로 자동 fallback하지 않는다. `server` mode의 오류는 실제 오류 UI로 처리한다. |
| MOCK-004 | 확정 | production build에서는 mock mode를 거부하고 browser worker·fixture가 활성화되지 않는다. |
| MOCK-005 | 확정 | 계약이 제공됐지만 backend endpoint가 아직 구현되지 않은 기능은 정규화 API Contract 기반 fixture와 browser handler로 최종 UI의 happy path를 확인할 수 있다. |
| MOCK-006 | 확정 | endpoint·DTO·오류 계약 자체가 미제공인 기능은 fixture를 추정하지 않는다. 계약과 무관한 shell·상태 inventory만 구현하고 최종 network UI 완료를 주장하지 않는다. |
| MOCK-007 | 확정 | 각 도메인 mock은 deterministic seed와 새로고침 시 초기화되는 in-memory store를 사용해 목록·상세·생성·수정·soft delete의 연결된 흐름을 재현한다. |
| MOCK-008 | 확정 | mock mode 화면에는 실제 서버가 아니라는 지속적으로 보이는 안내를 제공하고, JWT·password·signed URL·업로드 파일 본문을 log나 영구 저장소에 기록하지 않는다. |
| MOCK-009 | 확정 | 도메인 완료 상태는 `UI 확인 완료(mock)``실제 서버 연동 완료(server)`를 분리한다. mock Gate만 통과한 경우 Phase 전체와 network integration을 완료로 표시하지 않는다. |
“가로 800/1,000”은 이 문서에서 등록 결과의 **최대 출력 폭**으로 해석한다. JPEG/PNG crop 결과에는 이 제한을 적용하되 선택한 원본 crop 영역이 더 작으면 확대하지 않는다. 커뮤니티 GIF는 원본 가로가 800px 이하일 때만 등록할 수 있다.
#### Image crop UI 흐름
@@ -678,6 +693,8 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
- 목록 검색 debounce 시간은 구현 시 300ms 전후로 일관되게 적용한다.
- 날짜, 가격, 상태 label은 중앙 formatter로 일관되게 표시한다.
- 브라우저 지원 범위는 데스크톱 Chrome/Edge/Safari 최신 2개 주요 버전과 모바일 Chrome/Safari 최신 2개 주요 버전이다.
- mock/server mode는 build-time 환경 설정으로 명시적으로 선택하며 runtime 404 fallback을 사용하지 않는다.
- domain fixture와 browser handler는 `api-contract.md`의 제공 계약에서 파생하고 contract test와 함께 변경한다.
## 14. 성공 기준
@@ -714,6 +731,10 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
- FanTalk 답변이 있으면 두 번째 POST가 UI에서 차단되고 수정 동작만 제공되며, 직접·동시 요청도 백엔드가 원자적으로 거부한다.
- 댓글은 2단계를 넘지 않고 작성자에 따른 수정·삭제 권한이 구분된다.
- 모바일에서 조회·오디오 재생·댓글 관리·FanTalk 답변 작성/수정이 가능하다.
- `npm run dev:mock`에서 실제 backend 요청 없이 제공 계약 범위의 최종 UI happy path를 확인할 수 있고 mock mode 안내가 표시된다.
- 기본 `npm run dev`에서는 실제 개발 API를 사용하며 404·network error가 mock 응답으로 바뀌지 않는다.
- production build에는 browser mock이 활성화되지 않고 mock mode 설정을 허용하지 않는다.
- 각 도메인의 Progress와 Phase Gate는 `UI 확인 완료(mock)``실제 서버 연동 완료(server)` 증거를 별도로 기록한다.
### 14.2 UI/UX 수용 기준
@@ -773,3 +794,4 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
| 2026-07-26 | 문자열 최대 길이와 배열 최대 개수는 초기 UI 작성 후 각 페이지에서 권고값을 정하고 백엔드 호환 확인 후 확정한다. 그 전에는 제공 계약에 없는 최대값을 추정하지 않는다. |
| 2026-07-26 | 댓글 API·팬 댓글 삭제 권한 오류와 FanTalk 목록·상세·답변 수정·중복 답변 계약은 백엔드 제공 대기 사항으로 분류하고 프론트엔드 Open Questions에서 제외한다. |
| 2026-07-27 | 오디오 콘텐츠 생성에는 `themeId`가 필수이며, 테마 선택지는 `GET /api/v2/admin/ai-characters/audio-content-themes`에서 query/body 없이 조회한다. |
| 2026-07-27 | backend endpoint 구현 전에도 제공된 API Contract 범위의 최종 UI를 확인할 수 있도록 명시적 개발 전용 browser MSW mode를 제공한다. 실제 404 자동 fallback은 금지하고 mock UI 완료와 실제 server 연동 완료를 분리한다. |

View File

@@ -74,6 +74,7 @@ goal을 만들기 전에 다음을 확인한다.
- 모든 Task의 시작 조건과 선행 Goal ID가 실제로 존재한다.
- Files와 Interfaces의 이름이 앞뒤 Task에서 일치한다.
- 외부 의존과 안전한 기본값이 구분돼 있다.
- backend 구현 전 UI preview가 필요하면 제공 계약 기반 explicit mock mode와 실제 server integration을 별도 Task·Gate·Progress로 구분하고 404 자동 fallback을 금지한다.
- 실제 검증 명령과 Expected가 구체적이다.
- placeholder, 미정 값, 추정 계약이 없다.
- 변경 금지 항목과 중단 규칙이 명시돼 있다.

View File

@@ -22,6 +22,7 @@
- 외부 의존에는 담당 주체, 영향받는 기능과 재개 조건을 기록한다. endpoint·DTO·enum·오류 status/key를 추정하지 않는다.
- 제외 항목에는 제외 이유, 결정 기록과 다시 포함할 조건을 둔다.
- Non-Goal, 반응형 capability, 접근성, 보안·데이터 처리, 성능과 지원 환경을 명시한다.
- backend 구현 전 UI 확인이 필요하면 explicit development mock mode, production 금지, no-auto-fallback과 mock/server 완료 상태 분리를 요구사항으로 명시한다.
## 4. 문서 간 추적

View File

@@ -48,6 +48,7 @@
- 호환성: `<지원 browser, viewport, runtime>`
- 의존성: 실제 소비 Task에서 필요한 최소 dependency만 추가한다.
- 계약: 제공되지 않은 endpoint, DTO, enum, 오류 status/key와 validation 상한을 추정하지 않는다.
- backend 구현 전 UI 확인이 필요하면 제공 계약 기반 explicit mock mode를 사용하고 실제 404 자동 fallback·production mock을 금지하며 mock/server 완료 증거를 분리한다.
- 구현: 모든 기능은 가장 작은 실패 test를 먼저 만들고 최소 구현으로 통과시킨다.
## Phase 1

View File

@@ -211,6 +211,7 @@ Non-Goal을 변경하려면 Decision Log와 `plan-task.md` 범위를 먼저 갱
- mutation 중복 제출·upload 취소/재시도: `<정책>`
- 지원 runtime·browser: `<정확한 범위>`
- test stack과 필수 Gate: `<unit/integration/E2E/typecheck/lint/build>`
- backend 구현 전 UI 확인: `<불필요 또는 explicit mock mode, production 금지, no-auto-fallback, mock/server 완료 상태 분리>`
## 14. 성공 기준