Files
voiceon-character-admin/docs/20260805_오디오콘텐츠댓글답글/prd.md

211 lines
10 KiB
Markdown

# 오디오 콘텐츠 댓글 답글 작성 진입 PRD
## 문서 정보
| 항목 | 내용 |
|---|---|
| 문서 상태 | 구현 기준 확정 |
| 작성일 | 2026-08-05 |
| 최종 수정일 | 2026-08-05 |
| 대상 기능 | 오디오 콘텐츠 댓글의 첫 답글 작성 진입 |
| 작성자·결정권자 | Codex 작성, 사용자 결정 |
| 상위 제품 기준 | [AI 캐릭터 관리자 웹 PRD](../20260725_AI캐릭터관리자웹/prd.md) |
| 관련 API Contract | [api-contract.md](./api-contract.md) |
| 관련 구현 계획 | [plan-task.md](./plan-task.md) |
| 관련 review | [Phase 1 구현 리뷰](./reviews/phase1-audio-comment-first-reply.md) |
### 요구사항 상태
| 상태 | 의미 |
|---|---|
| 확정 | 구현과 검증 기준으로 사용한다. |
| 미결 | 제품 결정 전에는 구현하지 않는다. |
| 외부 의존 | 외부 계약이 제공될 때까지 영향 범위를 구현 완료로 표시하지 않는다. |
| 제외 | 현재 기능 범위에 포함하지 않는다. |
## 1. Overview
활성 AI 캐릭터의 오디오 콘텐츠 댓글에서 직접 답글이 아직 없는 원댓글에도
`답글 작성` 진입을 제공한다. 기존 답글 조회 영역, 작성 form과 댓글 POST를
재사용하며 커뮤니티 댓글 동작은 변경하지 않는다.
## 2. Problem Statement
현재 답글 POST와 작성 form은 구현돼 있지만 원댓글의 `replyCount`가 0이면
답글 영역을 여는 버튼이 렌더되지 않는다. 따라서 관리자는 이미 답글이 있는
원댓글에만 추가 답글을 쓸 수 있고 첫 답글은 작성할 수 없다.
문제를 해결했다는 판단은 답글 0개인 오디오 원댓글에서 `답글 작성`을 눌러
기존 form을 열고 `parentId`가 포함된 POST를 한 번 전송할 수 있는지로 한다.
## 3. Goals
### 3.1 제품 목표
- 활성 오디오 콘텐츠의 모든 원댓글에 첫 답글을 작성할 수 있다.
- 기존 2단계 댓글 구조와 API 계약을 그대로 유지한다.
### 3.2 UX 목표
- 답글이 0개인 원댓글에는 `답글 작성`이라는 명확한 진입점을 표시한다.
- 버튼을 누르면 기존 답글 영역과 작성 form을 펼친다.
- 기존 loading, 오류, 전송 중, 실패 후 초안 보존 동작을 유지한다.
## 4. Non-Goals
- 커뮤니티 댓글의 답글 진입 조건 변경
- 답글 form 상시 노출
- 답글의 답글을 포함한 3단계 구조
- 새 endpoint, DTO, 상태관리 또는 UI dependency 추가
- `isSecret=false`, Audio `languageCode=null` 작성 정책 변경
- 답글 수정·삭제·pagination 동작 변경
## 5. Target Users and Permissions
| 사용자 | 목표 | 주요 작업 | 사용 환경 |
|---|---|---|---|
| ADMIN | AI 캐릭터 명의로 팬 원댓글에 첫 답글 작성 | 답글 영역 열기, 작성, 재시도 | desktop, tablet, mobile |
- 인증과 ADMIN 권한은 상위 제품 기준을 따른다.
- 활성 AI 캐릭터 workspace에서만 답글 작성 control을 제공한다.
- 비활성 AI 캐릭터 workspace는 기존처럼 조회 전용이다.
- 원댓글 작성자가 팬인지 AI 캐릭터인지와 관계없이 답글을 작성할 수 있다.
## 6. 핵심 사용자 흐름
1. 관리자가 활성 AI 캐릭터의 오디오 콘텐츠 상세에 진입한다.
2. 답글이 0개인 원댓글에서 `답글 작성`을 누른다.
3. UI가 기존 직접 답글 GET을 실행하고 답글 영역과 작성 form을 표시한다.
4. 관리자가 내용을 입력해 등록한다.
5. 기존 댓글 POST에 원댓글 ID를 `parentId`로 보내고 성공 후 원댓글·답글 목록을 재조회한다.
6. 실패하면 오류를 표시하고 입력 초안을 유지해 재시도할 수 있다.
## 7. 정보 구조와 라우팅
```text
/ai-characters/:characterId/audio-contents/:contentId
└─ 댓글 관리
└─ 원댓글
└─ 직접 답글 영역 및 작성 form
```
- 새 route와 query parameter를 추가하지 않는다.
- 오디오 콘텐츠 상세의 기존 `CommentThread` 안에서만 동작한다.
## 8. 기능 요구사항
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|---|---|---|---|---|
| `ACR-001` | 확정 | 활성 Audio target의 답글 0개 원댓글에 `답글 작성` 버튼을 표시한다. | `replyCount=0`, `canMutate=true`인 Audio root에서 버튼을 찾을 수 있다. | contract 불필요, `P1-T1` |
| `ACR-002` | 확정 | `답글 작성`을 누르면 기존 직접 답글 영역과 작성 form을 연다. | 버튼 클릭 뒤 원댓글 이름과 연결된 답글 region·textarea·등록 버튼이 표시된다. | 답글 GET, `P1-T1` |
| `ACR-003` | 확정 | 첫 답글은 기존 Audio 댓글 POST를 사용한다. | body가 `comment`, root ID `parentId`, `isSecret=false`, `languageCode=null`을 포함하고 성공 후 목록을 재조회한다. | 댓글 POST, `P1-T1` |
| `ACR-004` | 확정 | 기존 경계를 유지한다. | Community의 답글 0개 root와 비활성 Audio root에는 신규 작성 진입이 없고, reply row에는 답글 action이 없다. | contract 불필요, `P1-T1` |
| `ACR-005` | 확정 | 기존 mutation 상태를 유지한다. | pending 중 중복 POST가 없고 실패 시 초안 유지, 성공 시 입력 초기화가 기존 test와 함께 통과한다. | `NullSuccess`, `P1-GATE` |
## 9. 반응형 기능 범위
| 기능 | Desktop | Tablet | Mobile | 비고 |
|---|---:|---:|---:|---|
| `답글 작성` 진입 | 지원 | 지원 | 지원 | 기존 댓글 action layout 재사용 |
| 답글 작성 form | 지원 | 지원 | 지원 | 기존 form 재사용 |
- 상위 제품의 최소 320px, keyboard-only와 touch target 기준을 유지한다.
## 10. UI/UX Expectations
### 10.1 디자인과 component 원칙
- `CommentThread`, `CommentItem`, `CommentForm`을 재사용한다.
- 새 component나 dependency를 추가하지 않는다.
- 기존 답글이 있는 원댓글의 `답글 보기` UI는 유지한다.
### 10.2 화면 상태
- 클릭 직후 기존 답글 loading 상태를 표시한다.
- 빈 답글 응답 뒤에도 작성 form을 표시한다.
- 조회 오류는 기존 재시도 UI를 사용한다.
- 작성 중·성공·실패는 기존 Comments mutation 정책을 사용한다.
### 10.3 접근성
- 버튼의 accessible name은 원댓글 내용과 `답글 작성`을 조합해 식별 가능해야 한다.
- form의 visible label과 오류 연결, keyboard focus 표시를 유지한다.
- 답글 region은 기존처럼 원댓글 내용과 `답글`을 조합한 accessible name을 유지한다.
## 11. API 계약
### 11.1 공통 규칙
- 이 기능은 API를 변경하지 않는다.
- 정확한 request, response와 오류는 [기능 API Contract](./api-contract.md)를 따른다.
- 원본 OpenAPI는 [프로젝트 OpenAPI](../20260725_AI캐릭터관리자웹/api-contract.openapi.json)다.
### 11.2 Endpoint 추적
| 요구사항 | Method | Path | 계약 상태 | 소유 Goal |
|---|---|---|---|---|
| `ACR-002` | GET | `/api/v2/admin/ai-characters/{characterId}/audio-contents/{contentId}/comments/{commentId}/replies` | 기존 제공 | `P1-T1` |
| `ACR-003` | POST | `/api/v2/admin/ai-characters/{characterId}/audio-contents/{contentId}/comments` | 기존 제공 | `P1-T1` |
### 11.3 외부 제공 대기 계약
없음. 필요한 GET·POST와 DTO가 이미 제공돼 있다.
## 12. 보안과 데이터 취급
- 기존 Bearer 인증, ADMIN 권한과 `characterId`·`contentId` target 격리를 유지한다.
- 클라이언트가 임의의 다른 target ID를 생성하지 않는다.
- 댓글 본문과 인증 정보는 console, 분석 이벤트와 영구 저장소에 기록하지 않는다.
- 401·403은 공통 인증·인가 정책을 따른다.
## 13. 성능과 품질 요구사항
- 버튼 클릭 때 선택한 root의 답글 page 0만 기존 방식으로 조회한다.
- 전체 root 또는 모든 답글을 선조회하지 않는다.
- 새 dependency와 optimistic update를 추가하지 않는다.
- Vitest focused test, Comments 회귀, Chromium mock E2E, typecheck와 lint를 통과한다.
- server 404나 network error를 mock으로 자동 전환하지 않는다.
## 14. 성공 기준
### 14.1 기능 수용 기준
- [x] 답글 0개인 활성 Audio root에서 첫 답글을 작성한다. (`ACR-001~003`)
- [x] Community, 비활성 Audio와 reply row의 기존 경계가 유지된다. (`ACR-004`)
- [x] 실패·재시도와 중복 제출 방지가 회귀하지 않는다. (`ACR-005`)
### 14.2 UI/UX 수용 기준
- [x] 버튼·답글 region·form의 accessible name과 label이 연결된다.
- [x] 320px와 keyboard-only 흐름에서 작성 control을 사용할 수 있다.
### 14.3 추적성 완료 기준
- [x] 모든 확정 요구사항이 API 또는 contract 불필요 판정, `P1-T1`, `P1-GATE`와 연결된다.
- [x] 구현·검증 결과가 [plan-task.md](./plan-task.md)의 Progress에 기록된다.
## 15. Open Questions
없음.
## 16. 요구사항 추적표
| 요구사항 범위 | API Contract | 계획 Phase | Goal | 자동 검증 | 수동 검증 |
|---|---|---:|---|---|---|
| `ACR-001~005` | [api-contract.md](./api-contract.md) | 1 | `P1-T1`, `P1-GATE` | `comment-thread.test.tsx`, `comments.spec.ts` | 활성 Audio 첫 답글, Community·비활성 경계 |
## 17. Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항·계약·Goal |
|---|---|---|---|---|---|
| 2026-08-05 | `ACR-DEC-001` | 확정 | 신규 진입은 오디오 콘텐츠 댓글에만 적용하고 커뮤니티 댓글은 변경하지 않는다. | 사용자 선택 B | `ACR-001`, `ACR-004`, `P1-T1` |
| 2026-08-05 | `ACR-DEC-002` | 확정 | 답글 0개 root에 form을 상시 노출하지 않고 `답글 작성` 버튼으로 기존 답글 영역을 연다. | 사용자 선택 A | `ACR-001~002`, `P1-T1` |
| 2026-08-05 | `ACR-DEC-003` | 확정 | 새 API·컴포넌트 없이 기존 `parentId` POST와 Comments UI를 재사용한다. | 기존 OpenAPI와 구현 확인 | `ACR-002~005`, [api-contract.md](./api-contract.md) |
## 18. 변경 관리
- 범위가 바뀌면 이 문서의 Decision Log와 요구사항을 먼저 갱신한다.
- API가 바뀌면 [api-contract.md](./api-contract.md)와 원본 OpenAPI의 제공 버전을 확인한다.
- 구현 범위가 바뀌면 코드보다 [plan-task.md](./plan-task.md)를 먼저 갱신한다.
- 기존 Progress와 검증 기록은 삭제하거나 덮어쓰지 않는다.