docs(ai-character): OpenAPI 계약 반영
This commit is contained in:
@@ -4,13 +4,14 @@
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 문서 상태 | 인터뷰 반영 초안 |
|
||||
| 문서 상태 | OpenAPI 반영 구현 기준 |
|
||||
| 작성일 | 2026-07-25 |
|
||||
| 최종 수정일 | 2026-07-28 |
|
||||
| 대상 제품 | AI 캐릭터 전용 독립 관리자 웹 |
|
||||
| 구현 대상 | React + TypeScript + Vite SPA |
|
||||
| UI 기반 | Tailwind CSS + shadcn/ui |
|
||||
| 관련 계획 | [plan-task.md](./plan-task.md) |
|
||||
| 정규화 계약 | [api-contract.md](./api-contract.md) |
|
||||
| 정규화 계약 | [api-contract.openapi.json](./api-contract.openapi.json) |
|
||||
|
||||
### 상태 표기
|
||||
|
||||
@@ -18,12 +19,13 @@
|
||||
- **미결**: 프론트엔드 제품·UI 또는 운영 정책이 결정되지 않아 후속 인터뷰나 UI 검토가 필요한 사항
|
||||
- **외부 의존**: 프론트엔드가 결정할 사항이 아니며, 해당 기능의 network integration 전에 백엔드가 제공해야 하는 계약
|
||||
- **권고**: 미결 사항에 대한 현재 추천안이며, 확정 전에는 계약으로 간주하지 않음
|
||||
- **제외**: 현재 OpenAPI 또는 릴리스 범위에 포함하지 않으며, 다시 포함할 조건을 별도로 기록한 사항
|
||||
|
||||
### 문서 유지보수 원칙
|
||||
|
||||
1. 요구사항이 변경되면 `prd.md`의 결정 기록, 기능 요구사항, API 계약 보정표, 미결 사항을 함께 갱신한다.
|
||||
1. 요구사항이 변경되면 `prd.md`의 결정 기록, 기능 요구사항, OpenAPI 소비 주의사항, 미결·외부 의존 사항을 함께 갱신한다.
|
||||
2. 구현 범위나 순서가 바뀌면 같은 디렉터리의 `plan-task.md`도 같은 변경에서 갱신한다.
|
||||
3. 사용자 인터뷰 결정과 최초 API Contract가 충돌하면 이 문서의 “API 계약 보정사항”을 우선한다.
|
||||
3. endpoint, query, multipart part, request/response field, required 여부, status와 오류 응답은 OpenAPI 계약을 단일 진실 원천으로 사용한다. OpenAPI에 표현되지 않는 제품·UI·운영 정책은 이 문서가 소유한다.
|
||||
4. 미결 사항과 외부 의존 계약은 추측으로 구현하지 않는다. **미결**에는 추천안을, **외부 의존**에는 제공 주체와 영향을 함께 기록한다.
|
||||
5. 완료된 미결 사항과 제공 완료된 외부 의존 계약은 결정일과 결정 내용을 “결정 기록”에 추가한 뒤 관련 수용 기준까지 갱신한다.
|
||||
6. goal 실행의 objective·순서·완료 증거·범위는 `plan-task.md`의 `Phase Goal`과 `Goal 실행`을 기준으로 한다. goal 수행 중 제품 결정이 바뀌면 이 문서의 결정 기록과 요구사항을 먼저 갱신한다.
|
||||
@@ -49,7 +51,7 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
- 오디오 콘텐츠를 관리자 화면에서 즉시 재생해 검수해야 한다.
|
||||
- 예약 공개, 시리즈 연결과 순서, 게시글 고정, FanTalk 단일 답변 같은 도메인 규칙을 UI에서 명확히 안내해야 한다.
|
||||
- 데스크톱에서는 전체 운영을 수행하고 모바일에서는 조회와 긴급 응대가 가능해야 한다.
|
||||
- 최초 API Contract의 잘못되었거나 누락된 필드를 구현 전에 바로잡아야 한다.
|
||||
- 정식 OpenAPI 계약과 기존 PRD·구현 계획의 잘못되었거나 누락된 API 전제를 구현 전에 바로잡아야 한다.
|
||||
|
||||
## 3. Goals
|
||||
|
||||
@@ -57,7 +59,7 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
|
||||
- AI 캐릭터 목록 검색, 상세 조회, 생성, 수정, 비활성화를 제공한다.
|
||||
- 선택한 캐릭터 문맥에서 오디오 콘텐츠, 시리즈, 커뮤니티 게시글을 관리한다.
|
||||
- 선택한 캐릭터로 FanTalk에 한 번 답변하고 기존 답변을 수정할 수 있게 한다.
|
||||
- 선택한 캐릭터로 FanTalk에 한 번 답변할 수 있게 한다. 기존 답변 수정은 OpenAPI에 수정 endpoint가 추가된 뒤 활성화한다.
|
||||
- 오디오 콘텐츠와 커뮤니티 첨부 오디오를 관리자 화면에서 재생할 수 있게 한다.
|
||||
- 오디오 콘텐츠와 커뮤니티 게시글의 댓글·답글을 캐릭터 명의로 관리한다.
|
||||
- 비활성 리소스와 권한 오류를 안전하게 처리하고 의도하지 않은 변경을 방지한다.
|
||||
@@ -110,7 +112,7 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
3. 캐릭터 워크스페이스의 기본 정보, 오디오, 시리즈, 커뮤니티, FanTalk 탭을 이동한다.
|
||||
4. 활성 캐릭터라면 데스크톱·태블릿에서 리소스를 생성·수정·비활성화한다.
|
||||
5. 오디오 플레이어로 캐릭터가 올린 오디오를 검수한다.
|
||||
6. FanTalk에는 한 번만 답변하고 필요하면 기존 답변을 수정한다.
|
||||
6. FanTalk 목록 item에서 답변이 없을 때 한 번 답변한다. 기존 답변 수정은 수정 계약이 제공된 뒤 추가한다.
|
||||
7. 오디오 또는 커뮤니티 댓글에 캐릭터 명의로 댓글·답글을 작성하거나 운영 정책에 따라 삭제한다.
|
||||
8. 모바일에서는 리소스를 조회하고 오디오를 재생하며 댓글과 FanTalk 답변을 관리한다.
|
||||
9. JWT가 만료되거나 폐기되면 인증 정보를 지우고 로그인 화면으로 이동한다.
|
||||
@@ -137,16 +139,16 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
/community-posts
|
||||
/community-posts/new
|
||||
/fan-talks
|
||||
/fan-talks/:fanTalkId
|
||||
```
|
||||
|
||||
라우트 문자열은 구현 시 확정하되 다음 원칙은 고정한다.
|
||||
|
||||
- 캐릭터를 선택하지 않은 전역 화면은 로그인과 캐릭터 목록·생성뿐이다.
|
||||
- 캐릭터 리소스 화면은 모두 URL에 `characterId`를 포함한다.
|
||||
- 목록의 `search`, `status`, 답변 상태, `page`, `size`는 가능한 범위에서 URL query에 보존한다.
|
||||
- 계약이 제공하는 `searchTerm`, `search_word`, `page`, `size`와 제품 filter 상태는 URL query에 보존한다. 계약에 없는 server filter를 client 전체 결과 filter처럼 가장하지 않는다.
|
||||
- 상세 GET이 제공되는 주요 리소스의 목록과 상세 화면은 새로고침과 직접 링크 진입이 가능해야 한다.
|
||||
- 커뮤니티 게시글은 별도 상세·수정 route 없이 목록 행/카드에서 여는 Sheet를 사용한다. 페이지 새로고침은 목록을 다시 조회한다.
|
||||
- FanTalk도 별도 상세 GET이 없으므로 목록 item을 source로 Sheet 또는 panel을 열고 답변을 작성한다.
|
||||
- 존재하지 않거나 다른 캐릭터 소유인 하위 리소스는 서버 결과에 따라 오류 화면으로 처리한다.
|
||||
|
||||
### 7.2 캐릭터 워크스페이스
|
||||
@@ -182,7 +184,7 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
|---|---|---|
|
||||
| CHAR-001 | 확정 | 이름 검색, 페이지네이션이 있는 캐릭터 목록을 제공한다. |
|
||||
| CHAR-002 | 확정 | 캐릭터 상세, 생성, 수정, 비활성화를 제공한다. |
|
||||
| CHAR-003 | 확정 | 생성 입력은 `name`, `description`, 선택 이미지, 선택 `originalWorkId`다. |
|
||||
| CHAR-003 | 확정 | 생성 multipart는 필수 `image`와 필수 `request` part를 사용한다. `request`의 필수 입력은 `name`, `systemPrompt`, `description`이고 나머지 필드는 OpenAPI의 optional/nullable 정의를 따른다. |
|
||||
| CHAR-004 | 확정 | 생성 요청에는 `isActive`를 보내지 않는다. 최초 활성 상태는 백엔드가 결정한다. |
|
||||
| CHAR-005 | 확정 | `externalCharacterId`는 존재하지 않는 값이므로 모든 요청·응답·UI에서 제거한다. |
|
||||
| CHAR-006 | 확정 | 일반 수정 요청에서는 `isActive`를 생략하고 soft delete 요청에만 `isActive=false`를 보낸다. `isActive=true`는 전송하지 않는다. |
|
||||
@@ -190,16 +192,22 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
| CHAR-008 | 확정 | 캐릭터 생성 시 연결된 `creator(memberKind = AI_CHARACTER)` 생성은 백엔드가 함께 수행한다. |
|
||||
| CHAR-009 | 확정 | 캐릭터 이름·설명·이미지 변경 시 creator의 nickname·introduce·profile image 동기화는 현재 백엔드가 수행한다. |
|
||||
| CHAR-010 | 확정 | creator 상황에 따른 조건부 생성·동기화는 다음 백엔드 범위이며 현재 UI 범위가 아니다. |
|
||||
| CHAR-011 | 확정 | `creatorMemberId`, `creatorNickname` 등 응답으로 제공되는 creator 정보는 읽기 전용으로 표시한다. |
|
||||
| CHAR-012 | 확정 | 캐릭터 목록은 활성 캐릭터만 반환하며 활성 상태 filter/query를 제공하지 않는다. |
|
||||
| CHAR-013 | 외부 의존 | 원작 검색 선택기에 필요한 lookup API와 원작 미선택 직렬화 계약은 백엔드가 제공해야 한다. 제공 전에는 원작 선택 network integration을 구현하지 않는다. |
|
||||
| CHAR-014 | 확정 | 캐릭터 soft delete 성공 시 캐릭터 active-only 목록으로 이동하고 성공 알림을 표시한다. 현재 상세 화면에 머물지 않는다. |
|
||||
| CHAR-011 | 제외 | 현 OpenAPI의 Character 목록·상세 응답에는 `creatorMemberId`, `creatorNickname`이 없다. 계약에 추가되기 전에는 creator 정보 UI와 DTO를 만들지 않는다. |
|
||||
| CHAR-012 | 외부 의존 | OpenAPI는 `searchTerm`을 생략하면 활성 목록을 반환한다고 명시하지만, `searchTerm` 지정 시에는 “레거시 검색”만 명시해 active-only 여부가 불명확하다. 검색 결과 보장이 추가되기 전에도 client 활성 filter는 만들지 않고 서버 반환값을 표시한다. |
|
||||
| CHAR-013 | 외부 의존 | `originalWorkId`는 생성·수정 request에서 optional nullable이므로 미선택 시 key 생략과 `null`이 모두 계약상 가능하다. 원작 검색 선택기에 필요한 lookup API는 OpenAPI에 없으므로 제공 전에는 원작 선택 network integration을 구현하지 않는다. |
|
||||
| CHAR-014 | 확정 | 캐릭터 soft delete 성공 시 캐릭터 목록으로 이동해 재조회하고 성공 알림을 표시한다. 현재 상세 화면에 머물지 않는다. 비활성 항목 제외는 active-only 계약 제공 후 검증한다. |
|
||||
| CHAR-015 | 확정 | 목록은 `searchTerm`, `page`, `size`를 사용하고 `data.totalCount`, `data.content[]`를 소비한다. 목록 ID field는 `id`다. |
|
||||
| CHAR-016 | 확정 | 생성·수정 성공은 `data=null`이므로 생성 후 목록을 무효화해 이동하고, 수정 후 기존 `characterId` 상세와 목록을 다시 조회한다. 생성 응답에서 새 ID나 상세 DTO를 추정하지 않는다. |
|
||||
| CHAR-017 | 확정 | 상세의 `characterUUID`는 OpenAPI에 존재하는 읽기 전용 값이며, 제거된 `externalCharacterId`와 다른 field다. |
|
||||
| CHAR-018 | 확정 | 생성 request에는 `region`이 있지만 수정 request에는 없다. 수정 화면에서 region을 읽기 전용으로 표시하고 update payload에 보내지 않는다. |
|
||||
|
||||
#### 캐릭터 생성·수정 폼
|
||||
|
||||
- 이름과 설명은 명시적인 레이블과 필드 오류 영역을 가진다.
|
||||
- 이름, system prompt와 설명은 명시적인 레이블과 필드 오류 영역을 가진다.
|
||||
- OpenAPI의 optional scalar(`age`, `gender`, `mbti`, `speechPattern`, `speechStyle`, `appearance`, `region`, `originalTitle`, `originalLink`, `characterType`)와 배열(`tags`, `hobbies`, `values`, `goals`, `relationships`, `personalities`, `backgrounds`, `memories`)을 생성 form에서 편집할 수 있게 한다. `originalWorkId`는 lookup 계약 제공 후 선택기로 편집하고, 수정 request에 없는 `region`은 수정 화면에서 읽기 전용이다.
|
||||
- 이미지는 미리보기, 교체, 새로 선택한 파일의 선택 취소를 제공한다. 기존 이미지 자체를 제거하는 기능은 contract가 없어 현재 범위가 아니다.
|
||||
- 원작은 이름 검색형 Combobox로 선택한다. 미선택은 허용하되 multipart JSON에서 `null`을 보낼지 key를 생략할지는 API 계약으로 확정한다.
|
||||
- 생성 이미지는 필수다. 수정 이미지는 선택이며 미전송하면 기존 이미지를 유지한다.
|
||||
- 원작 lookup 계약이 제공되면 이름 검색형 Combobox로 선택한다. 미선택은 허용하고 serializer는 key 생략 또는 `null` 중 한 가지 canonical form을 contract test로 고정한다.
|
||||
- 비활성화는 폼 Switch가 아니라 영향 범위를 설명하는 확인 Dialog로 실행한다.
|
||||
- 저장 중 중복 제출을 막고 성공 후 상세 데이터를 다시 동기화한다.
|
||||
|
||||
@@ -207,18 +215,18 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
|
||||
| ID | 상태 | 요구사항 |
|
||||
|---|---|---|
|
||||
| AUDIO-001 | 확정 | 선택한 캐릭터의 오디오 목록·검색·상태 필터·상세·생성·수정·비활성화를 제공한다. |
|
||||
| AUDIO-002 | 확정 | 상태 값은 `OPEN`과 `SCHEDULED` 두 개뿐이다. |
|
||||
| AUDIO-003 | 확정 | `OPEN`은 현재 출시된 콘텐츠, `SCHEDULED`는 미래 출시 예약 콘텐츠다. |
|
||||
| AUDIO-004 | 확정 | 상태는 백엔드가 공개 시각을 기준으로 계산해 반환하고 프론트엔드는 재계산하지 않는다. |
|
||||
| AUDIO-005 | 확정 | 상태 필터를 보내지 않은 경우의 결과 집합도 백엔드가 결정해 반환한다. |
|
||||
| AUDIO-001 | 확정 | 선택한 캐릭터의 오디오 목록·제목 검색·상세·생성·수정·비활성화를 제공한다. |
|
||||
| AUDIO-002 | 제외 | 현 OpenAPI 목록·상세에는 `OPEN`, `SCHEDULED` status field가 없고 목록 status query도 없다. 상태 badge와 server status filter는 계약에 추가되기 전에는 제공하지 않는다. |
|
||||
| AUDIO-003 | 확정 | 공개 예약은 생성 request의 nullable `releaseDate`와 `timezone`으로 표현한다. 목록·상세에서는 OpenAPI가 반환한 `releaseDate` 문자열을 그대로 표시하고 별도 status enum을 만들지 않는다. |
|
||||
| AUDIO-004 | 확정 | 프론트엔드는 `releaseDate`로 `OPEN`·`SCHEDULED` 같은 API status를 재계산하거나 DTO에 추가하지 않는다. |
|
||||
| AUDIO-005 | 제외 | 현 OpenAPI에 status filter가 없으므로 status query를 보내거나 현재 page를 client에서 status별로 거르지 않는다. |
|
||||
| AUDIO-006 | 확정 | 생성 요청에는 `isActive`를 보내지 않는다. |
|
||||
| AUDIO-007 | 확정 | 일반 수정 요청에서는 `isActive`를 생략하고 soft delete 요청에만 `isActive=false`를 보낸다. `isActive=true`는 전송하지 않는다. |
|
||||
| AUDIO-008 | 확정 | 공개 방식은 “지금 즉시 공개”와 “예약 공개” 두 선택 버튼으로 제공한다. |
|
||||
| AUDIO-009 | 확정 | 생성 시 즉시 공개가 기본값이며 `releaseDateUtc=null`을 보낸다. 예약 날짜 입력은 비활성화하고 기존 값을 지운다. |
|
||||
| AUDIO-009 | 확정 | 생성 시 즉시 공개가 기본값이며 `releaseDate=null`과 `timezone="Asia/Seoul"`을 보낸다. 예약 날짜 입력은 비활성화하고 기존 값을 지운다. |
|
||||
| AUDIO-010 | 확정 | 예약 공개를 선택한 경우에만 날짜·시간을 입력할 수 있고 미래 시각이 필수다. |
|
||||
| AUDIO-011 | 확정 | 예약 시각은 Asia/Seoul로 입력·표시하고 API에는 UTC ISO-8601 `Z` 값으로 변환해 보낸다. |
|
||||
| AUDIO-012 | 확정 | 생성 시 cover image와 audio file은 필수이며 수정 시 교체 파일은 선택이다. |
|
||||
| AUDIO-011 | 확정 | 예약 시각은 Asia/Seoul로 입력·표시하고 생성 API에는 `yyyy-MM-dd HH:mm` 형식의 `releaseDate`와 `timezone="Asia/Seoul"`을 보낸다. UTC `Z` 값으로 변환하지 않는다. |
|
||||
| AUDIO-012 | 확정 | 생성 multipart의 `contentFile`, `coverImage`, `request`는 필수다. 수정은 `coverImage`와 `request`만 허용하므로 오디오 원본 파일 교체 UI를 제공하지 않는다. |
|
||||
| AUDIO-013 | 확정 | 오디오 확장자는 `.mp3`, `.aac`, `.m4a`를 허용한다. WAV는 허용하지 않는다. |
|
||||
| AUDIO-014 | 확정 | canonical MIME은 `audio/mpeg`, `audio/aac`, `audio/mp4`다. |
|
||||
| AUDIO-015 | 확정 | 오디오 파일의 운영 기준 최대 크기는 1,024MB이고 최대 재생 길이는 제한하지 않는다. |
|
||||
@@ -226,17 +234,22 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
| AUDIO-017 | 확정 | 업로드 진행률, 취소, 전체 재시도를 제공하고 resumable upload는 제공하지 않는다. |
|
||||
| AUDIO-018 | 확정 | 업로드 실패 후 입력한 폼 값과 선택 가능한 파일 상태를 최대한 유지한다. |
|
||||
| AUDIO-019 | 확정 | 가격 단위는 “캔”이고 0 이상의 정수다. 0은 무료이며 UI는 예: `1,000캔`으로 표시한다. |
|
||||
| AUDIO-020 | 확정 | 오디오를 여러 시리즈에 연결할 수 있도록 `seriesIds` 다중 선택을 제공한다. |
|
||||
| AUDIO-020 | 확정 | Audio 생성·수정 request에는 `seriesIds`가 없다. 시리즈 연결은 Audio form이 아니라 Series 콘텐츠 연결 endpoint와 Phase 5 UI에서 관리한다. |
|
||||
| AUDIO-021 | 확정 | 목록과 상세에서 오디오를 재생할 수 있다. |
|
||||
| AUDIO-022 | 확정 | 오디오 목록은 활성 오디오만 반환하며 활성 상태 filter/query를 제공하지 않는다. 기존 `status=OPEN|SCHEDULED` 공개 상태 필터는 유지한다. |
|
||||
| AUDIO-022 | 외부 의존 | 오디오 목록 request에는 활성 상태 query와 status query가 없고 응답에도 `isActive`가 없다. active-only 반환 보장은 OpenAPI에 명시돼야 하며 client-side 활성 filter는 만들지 않는다. |
|
||||
| AUDIO-023 | 확정 | 최대 크기는 decimal 1,024MB인 `1,024,000,000 bytes` 이하이며 `1,024,000,001 bytes`부터 거부한다. 오디오 콘텐츠와 커뮤니티 첨부 audio에 동일하게 적용한다. |
|
||||
| AUDIO-024 | 확정 | `audio/x-m4a`는 `.m4a` 파일에 한해 호환 MIME으로 허용한다. 실제 MP4/M4A 컨테이너·코덱 검증을 통과해야 하며 다른 확장자와의 조합은 거부한다. |
|
||||
| AUDIO-025 | 확정 | 오디오 soft delete 성공 시 선택 캐릭터의 오디오 active-only 목록으로 이동하고 성공 알림을 표시한다. 현재 상세 화면에 머물지 않는다. |
|
||||
| AUDIO-025 | 확정 | 오디오 soft delete 성공 시 선택 캐릭터의 오디오 목록으로 이동해 재조회하고 성공 알림을 표시한다. 현재 상세 화면에 머물지 않는다. 비활성 항목 제외는 active-only 계약 제공 후 검증한다. |
|
||||
| AUDIO-026 | 확정 | 재생 오류를 signed URL 만료로 구분하거나 추정하지 않는다. media error만으로 상세·목록을 자동 재조회하거나 자동 재생하지 않고, 일반 오류와 수동 재시도·페이지 새로고침 안내를 제공한다. |
|
||||
| AUDIO-027 | 확정 | 오디오 콘텐츠 생성 시 `themeId`는 필수이며, 프론트엔드는 `GET /api/v2/admin/ai-characters/audio-content-themes`로 테마 목록을 불러와 선택 UI를 제공한다. |
|
||||
| AUDIO-028 | 확정 | 오디오 콘텐츠 테마 목록 조회는 query/body 없이 호출하며 응답의 `themeId`, `themeName`, `imageUrl`만 사용한다. |
|
||||
| AUDIO-027 | 확정 | 오디오 콘텐츠 생성 시 유효한 `themeId`가 필수다. OpenAPI의 기본값 `0`은 binding 기본값일 뿐 domain에서 유효하지 않으므로 프론트엔드는 테마 선택을 강제한다. |
|
||||
| AUDIO-028 | 확정 | 오디오 콘텐츠 테마 목록 조회는 query/body 없이 호출하며 응답 `data[]`의 `id`, `theme`, `image`를 사용한다. |
|
||||
| AUDIO-029 | 확정 | 목록 검색 query는 `search_word`이며 검색어가 2자 이상일 때만 보낸다. 목록 응답은 `data.totalCount`, `data.items[]`를 사용한다. |
|
||||
| AUDIO-030 | 확정 | 상세 GET은 필수 `timezone=Asia/Seoul` query를 보낸다. |
|
||||
| AUDIO-031 | 확정 | 생성 성공은 `data.contentId`를 사용해 상세로 이동할 수 있다. 수정·soft delete 성공은 `data=null`이므로 기존 ID 기준 cache를 무효화한다. |
|
||||
| AUDIO-032 | 확정 | 생성 `request`는 필수 `title`, `detail`, `tags`, `price`와 OpenAPI의 optional field만 보낸다. 수정은 `title`, `detail`, `tags`, `price`, `isAdult`, `isActive`, `isPointAvailable`, `isCommentAvailable`만 변경할 수 있다. |
|
||||
| AUDIO-033 | 확정 | 생성 form은 OpenAPI의 `purchaseOption`, `limited`, `isAdult`, `isGeneratePreview`, `isOnlyRental`, `isPointAvailable`, `isCommentAvailable`, `isFullDetailVisible`, `previewStartTime`, `previewEndTime`, `languageCode`를 계약 enum·type과 default에 맞춰 제공한다. 계약에 없는 추가 상관관계 validation은 만들지 않는다. |
|
||||
|
||||
수정 화면은 즉시 공개로 재초기화하지 않는다. 서버의 기존 `releaseDateUtc`와 `status`로 공개 방식과 날짜를 초기화하고, 관리자가 바꾸지 않으면 기존 값을 유지한다.
|
||||
현 수정 계약에는 `releaseDate`, `timezone`, `themeId`, `contentFile`이 없다. 따라서 공개 예약·테마·오디오 원본 변경은 생성 화면에서만 제공하고 수정 화면에서는 읽기 전용으로 표시한다.
|
||||
|
||||
#### 관리자 오디오 플레이어
|
||||
|
||||
@@ -254,22 +267,27 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
|---|---|---|
|
||||
| SERIES-001 | 확정 | 목록·상세·생성·수정·비활성화, 콘텐츠 연결·해제, 시리즈 순서 변경을 제공한다. |
|
||||
| SERIES-002 | 확정 | 상태 enum은 `PROCEEDING`(연재중), `SUSPEND`(휴재중), `COMPLETE`(완결)이다. `OPEN`은 유효하지 않다. |
|
||||
| SERIES-003 | 확정 | 생성 시 state를 선택하거나 보내지 않는다. 초기 state는 백엔드가 결정하며 프론트엔드는 정확한 기본값을 알 필요 없이 생성 응답의 state를 그대로 표시한다. |
|
||||
| SERIES-003 | 확정 | 생성 시 state를 선택하거나 보내지 않는다. 성공 응답은 `data=null`이므로 생성 후 목록으로 이동해 서버가 결정한 state를 다시 조회한다. |
|
||||
| SERIES-004 | 확정 | 수정 시 state를 선택할 수 있으며 선택하지 않으면 필드를 생략해 이전 상태를 유지한다. |
|
||||
| SERIES-005 | 확정 | 연재 요일 enum은 `SUN`, `MON`, `TUE`, `WED`, `THU`, `FRI`, `SAT`, `RANDOM`이다. |
|
||||
| SERIES-006 | 확정 | `RANDOM`은 다른 요일과 함께 보낼 수 없다. 값은 RANDOM 단독 또는 하나 이상의 실제 요일 목록이어야 한다. |
|
||||
| SERIES-007 | 확정 | 장르는 이름 검색형 선택기로 고르고 API에는 `genreId`를 보낸다. |
|
||||
| SERIES-008 | 확정 | 활성 시리즈 전체를 별도 순서 변경 모드에서 불러와 최종 순서의 모든 `seriesIds`를 전송한다. |
|
||||
| SERIES-007 | 외부 의존 | 생성 시 유효한 `genreId`가 필요하고 OpenAPI 기본값 `0`은 domain에서 유효하지 않다. 장르 이름 검색 API는 OpenAPI에 없으므로 제공 전에는 장르 선택 network integration과 Series 생성을 완료할 수 없다. |
|
||||
| SERIES-008 | 확정 | `data.totalCount`, `data.items[]`를 page별로 읽어 서버가 반환한 시리즈 전체를 별도 순서 변경 모드에 표시하고 최종 순서의 모든 ID를 `{ "ids": [...] }`로 전송한다. active-only 여부는 `SERIES-012` 계약 제공 후 검증한다. |
|
||||
| SERIES-009 | 확정 | drag-and-drop 외에 키보드와 위/아래 버튼으로 순서를 바꿀 수 있어야 한다. |
|
||||
| SERIES-010 | 확정 | 일반 수정 요청에서는 `isActive`를 생략하고 soft delete 요청에만 `isActive=false`를 보낸다. `isActive=true`, 복원과 hard delete는 제공하지 않는다. |
|
||||
| SERIES-011 | 외부 의존 | 장르 이름 검색 API는 백엔드가 제공해야 한다. 제공 전에는 장르 선택 network integration을 구현하지 않는다. 생성 초기 state의 정확한 기본값은 프론트엔드 의존사항이 아니다. |
|
||||
| SERIES-012 | 확정 | 시리즈 목록은 활성 시리즈만 반환하며 활성 상태 filter/query를 제공하지 않는다. |
|
||||
| SERIES-013 | 확정 | 시리즈 soft delete 성공 시 선택 캐릭터의 시리즈 active-only 목록으로 이동하고 성공 알림을 표시한다. 현재 상세 화면에 머물지 않는다. |
|
||||
| SERIES-011 | 외부 의존 | 장르 이름 검색 endpoint·DTO는 백엔드가 제공해야 한다. 제공 전에는 장르 선택 network integration을 구현하지 않는다. 생성 초기 state의 정확한 기본값은 프론트엔드 의존사항이 아니다. |
|
||||
| SERIES-012 | 외부 의존 | 시리즈 목록 request에는 활성 상태 query가 없다. active-only 반환 보장은 OpenAPI에 명시돼야 하며 list item의 `isActive`를 client에서 숨기는 방식으로 대체하지 않는다. |
|
||||
| SERIES-013 | 확정 | 시리즈 soft delete 성공 시 선택 캐릭터의 시리즈 목록으로 이동해 재조회하고 성공 알림을 표시한다. 현재 상세 화면에 머물지 않는다. 비활성 항목 제외는 active-only 계약 제공 후 검증한다. |
|
||||
| SERIES-014 | 확정 | 생성 multipart의 `image`와 `request`는 필수다. 생성 request는 `keyword` 단일 문자열을 사용하며 `keywords` 배열을 보내지 않는다. |
|
||||
| SERIES-015 | 확정 | 목록은 enum `publishedDaysOfWeek`, `genreId`, enum `state`를 사용하지만 상세는 표시용 문자열 `publishedDaysOfWeek`, `genre`, `keywords`, 한국어 `state`를 사용한다. 상세 표시 문자열을 update enum으로 재사용하지 않는다. |
|
||||
| SERIES-016 | 확정 | 연결 후보는 `GET .../contents/search?search_word=...`, 연결은 `{ "contentIdList": [...] }`, 해제는 body 없는 DELETE를 사용한다. |
|
||||
| SERIES-017 | 외부 의존 | 상세 응답에는 update에 필요한 `genreId`, enum `publishedDaysOfWeek`, enum `state`가 없다. 직접 링크에서도 안전하게 수정 form을 초기화할 edit DTO 또는 별도 mapping 계약이 제공되기 전에는 상세 표시 문자열을 역변환하지 않고 수정 network integration을 완료하지 않는다. |
|
||||
| SERIES-018 | 확정 | 생성 request의 `keyword`는 수정 request에 없다. 수정 화면에서 상세의 `keywords`를 읽기 전용으로 표시하고 update payload에 보내지 않는다. |
|
||||
|
||||
#### 시리즈 콘텐츠 연결
|
||||
|
||||
- 현재 연결 콘텐츠를 검색·페이지네이션해 보여준다.
|
||||
- 연결 후보는 선택 캐릭터의 활성 오디오로 제한한다.
|
||||
- 현재 연결 콘텐츠는 `page`, `size`로 조회한다. 현 계약에는 연결 목록 검색 query가 없다.
|
||||
- 연결 후보는 `.../contents/search?search_word=...`의 반환값을 사용하며 선택 캐릭터·해당 시리즈 문맥을 벗어난 별도 Audio 후보 endpoint를 만들지 않는다.
|
||||
- 이미 연결된 콘텐츠를 중복 연결하지 않는다.
|
||||
- 연결 해제 전 대상 제목과 영향을 확인한다.
|
||||
- 연결/해제 성공 후 시리즈 상세와 콘텐츠 목록을 함께 갱신한다.
|
||||
@@ -283,25 +301,32 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
| COMMUNITY-003 | 확정 | 이미지와 오디오 파일은 선택 첨부다. |
|
||||
| COMMUNITY-004 | 확정 | 첨부 오디오가 있으면 목록 행/카드와 게시글 Sheet에서 재생할 수 있다. |
|
||||
| COMMUNITY-005 | 확정 | 일반 수정 요청에서는 `isActive`를 생략하고 soft delete 요청에만 `isActive=false`를 보낸다. `isActive=true`는 전송하지 않는다. |
|
||||
| COMMUNITY-006 | 확정 | 비활성 게시글은 반드시 `isFixed=false`, `fixedAtUtc=null` 상태여야 한다. |
|
||||
| COMMUNITY-006 | 확정 | soft delete request에는 `isActive=false`와 `isFixed=false`를 함께 보낸다. 현 목록 응답에는 `fixedAtUtc`가 없으므로 해당 field를 DTO·UI에 만들지 않는다. |
|
||||
| COMMUNITY-007 | 확정 | 가격은 오디오와 동일하게 0 이상의 정수 “캔” 단위를 사용한다. |
|
||||
| COMMUNITY-008 | 확정 | 커뮤니티 게시글 목록은 활성 게시글만 반환하며 활성 상태 filter/query를 제공하지 않는다. |
|
||||
| COMMUNITY-008 | 외부 의존 | 커뮤니티 목록 request에는 활성 상태 query가 없고 item에도 `isActive`가 없다. active-only 반환 보장은 OpenAPI에 명시돼야 하며 client-side filter는 만들지 않는다. |
|
||||
| COMMUNITY-009 | 확정 | 커뮤니티 전용 상세 GET과 상세·수정 직접 route를 추가하지 않는다. 목록 응답으로 행/카드의 Sheet를 열어 조회·수정·고정·비활성화·댓글 진입을 제공한다. |
|
||||
| COMMUNITY-010 | 확정 | 커뮤니티 게시글 soft delete 성공 시 열린 Sheet를 닫고 게시글 active-only 목록을 무효화·재조회해 해당 항목을 제거하며 성공 알림을 표시한다. |
|
||||
| COMMUNITY-011 | 확정 | 첨부 audio URL 갱신만을 위한 자동 요청은 하지 않으며 media error도 refetch trigger로 사용하지 않는다. 사용자 페이지 새로고침이나 mutation 후 cache 무효화 등 일반 목록 재조회가 발생하면 새 응답의 `audioSignedUrl`을 사용한다. |
|
||||
| COMMUNITY-010 | 확정 | 커뮤니티 게시글 soft delete 성공 시 열린 Sheet를 닫고 목록을 무효화·재조회하며 성공 알림을 표시한다. 해당 항목 제외는 active-only 계약 제공 후 검증한다. |
|
||||
| COMMUNITY-011 | 확정 | 첨부 audio URL 갱신만을 위한 자동 요청은 하지 않으며 media error도 refetch trigger로 사용하지 않는다. 사용자 페이지 새로고침이나 mutation 후 cache 무효화 등 일반 목록 재조회가 발생하면 새 응답의 `audioUrl`을 사용한다. |
|
||||
| COMMUNITY-012 | 확정 | 목록 GET은 필수 `timezone=Asia/Seoul`, `page`, `size`를 사용하고 `data`의 게시글 배열을 소비한다. 응답에 `totalCount`, `page`, `hasNext`가 없으므로 전체 건수·마지막 page를 추정하지 않는다. |
|
||||
| COMMUNITY-013 | 확정 | 생성 multipart는 optional `audioFile`, optional `postImage`, 필수 `request`를 사용하고 request에 필수 `content`, `isCommentAvailable`, `isAdult`와 optional `price`만 보낸다. |
|
||||
| COMMUNITY-014 | 확정 | 수정 multipart는 optional `postImage`와 필수 `request`만 허용한다. 수정에서 가격·첨부 audio 교체는 제공하지 않고, 고정은 `isFixed`, soft delete는 `isActive=false`로 처리한다. |
|
||||
| COMMUNITY-015 | 확정 | 생성·수정·고정·soft delete 성공은 `data=null`이므로 목록을 무효화·재조회하고 mutation 응답에 게시글 DTO가 있다고 가정하지 않는다. |
|
||||
|
||||
### 8.6 FanTalk
|
||||
|
||||
| ID | 상태 | 요구사항 |
|
||||
|---|---|---|
|
||||
| FANTALK-001 | 확정 | 기본 목록은 전체 FanTalk를 최신순으로 표시한다. |
|
||||
| FANTALK-002 | 확정 | 필터는 전체, 미답변, 답변 완료 세 가지다. |
|
||||
| FANTALK-001 | 확정 | 기본 목록은 backend가 반환한 순서를 유지한다. 현 계약에 sort query가 없으므로 client가 page 사이의 최신순을 재정렬하지 않는다. |
|
||||
| FANTALK-002 | 외부 의존 | 전체·미답변·답변 완료 server filter query가 OpenAPI에 없다. 전체 결과 filter 계약이 제공되기 전에는 현재 page만 거르는 filter를 완성 기능으로 제공하지 않는다. |
|
||||
| FANTALK-003 | 확정 | 하나의 FanTalk에는 답변을 한 번만 작성할 수 있다. |
|
||||
| FANTALK-004 | 확정 | 답변이 있으면 추가 작성은 차단하고 기존 답변 수정만 허용한다. |
|
||||
| FANTALK-004 | 외부 의존 | 답변이 있으면 추가 작성은 UI에서 차단한다. 기존 답변 수정 endpoint는 OpenAPI에 없으므로 계약 제공 전에는 수정 network integration을 구현하지 않는다. |
|
||||
| FANTALK-005 | 확정 | 답변 삭제는 현재 범위가 아니다. |
|
||||
| FANTALK-006 | 확정 | 데스크톱·태블릿·모바일 모두 답변 작성과 수정을 지원한다. |
|
||||
| FANTALK-007 | 외부 의존 | 제공된 계약에는 답변 POST만 있다. 목록·상세·답변 수정 endpoint와 DTO는 백엔드가 제공해야 하며, 제공 전에는 해당 network integration을 구현하지 않는다. |
|
||||
| FANTALK-006 | 확정 | 데스크톱·태블릿·모바일 모두 목록 조회와 답변 작성을 지원한다. 답변 수정은 수정 계약이 제공된 뒤 같은 viewport 범위에 추가한다. |
|
||||
| FANTALK-007 | 외부 의존 | 목록 GET과 답변 POST는 제공됐다. 별도 상세 GET, 답변 수정 endpoint·DTO, 답변 상태 filter와 sort 계약은 백엔드가 제공해야 하며 제공 전에는 해당 network integration을 구현하지 않는다. |
|
||||
| FANTALK-008 | 외부 의존 | 답변 1개 불변식의 원자적 강제와 중복 생성의 정확한 비2xx status/message key는 백엔드가 결정·제공해야 한다. |
|
||||
| FANTALK-009 | 확정 | 목록은 `page`, `size`를 사용하고 `data.fanTalkCount`, `data.fanTalks`, `data.page`, `data.size`, `data.hasNext`를 소비한다. 각 item의 `creatorReplies[]`로 답변 유무를 판단한다. |
|
||||
| FANTALK-010 | 확정 | 답변 작성은 `{ "content": string }`을 보내고 성공 응답의 `fanTalkId`, `replyId`, `creatorMemberId`, `content`, `createdAtUtc`를 사용한다. |
|
||||
| FANTALK-011 | 확정 | 별도 상세 endpoint가 없으므로 목록 item을 source로 collection Sheet 또는 panel을 열며 `/fan-talks/:fanTalkId` 직접 route를 만들지 않는다. |
|
||||
|
||||
### 8.7 댓글과 답글
|
||||
|
||||
@@ -375,7 +400,8 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
| 커뮤니티 목록·게시글 Sheet·오디오 재생 | 전체 | 전체 | 전체 |
|
||||
| 커뮤니티 등록·수정·고정·비활성화 | 전체 | 전체 | 미지원 |
|
||||
| 댓글·답글 관리 | 전체 | 전체 | 전체 |
|
||||
| FanTalk 조회·답변 작성·답변 수정 | 전체 | 전체 | 전체 |
|
||||
| FanTalk 조회·답변 작성 | 전체 | 전체 | 전체 |
|
||||
| FanTalk 답변 수정 | 계약 제공 후 전체 | 계약 제공 후 전체 | 계약 제공 후 전체 |
|
||||
|
||||
모바일에서 미지원인 기능은 좁은 화면에 데스크톱 폼을 억지로 노출하지 않는다. 화면에는 읽기 전용임을 알리고 전체 관리가 필요하면 데스크톱·태블릿 사용을 안내한다.
|
||||
|
||||
@@ -501,7 +527,7 @@ AI 캐릭터를 생성하고, AI 캐릭터가 사람 크리에이터처럼 콘
|
||||
- 오류: 서버의 한국어 message, 재시도, 필요한 경우 목록으로 이동
|
||||
- 저장 중: 제출 버튼 비활성화와 진행 표시
|
||||
- 저장 성공: toast와 최신 서버 응답 반영
|
||||
- soft delete 성공: 해당 resource의 active-only 목록으로 이동하고 성공 toast 표시
|
||||
- soft delete 성공: 해당 resource 목록을 무효화·재조회하고 필요한 화면 이동과 성공 toast 표시. 비활성 항목 제외는 active-only 계약 제공 후 검증
|
||||
- 필드 오류: 로컬 validation은 해당 필드 아래에 표시하고 `aria-invalid`, 오류와 입력 연결, 첫 오류 focus를 제공한다. 서버 오류는 `errorProperty`가 실제 필드명을 제공하는 계약일 때만 inline으로 연결한다.
|
||||
- 업로드: 파일별 진행률, 취소, 재시도
|
||||
|
||||
@@ -564,21 +590,31 @@ python3 .codex/skills/ui-ux-pro-max/scripts/search.py \
|
||||
|
||||
## 11. API 계약
|
||||
|
||||
현재 기준은 OpenAPI `3.1.0`, 문서 version `2.0.0`인
|
||||
`api-contract.openapi.json`의 15개 path·23개 operation이다.
|
||||
OpenAPI에 아직 포함되지 않은 기존 로그인·로그아웃은 `EXT-006 현재 구현
|
||||
기준 계약`에 별도로 기록하며, 정식 OpenAPI가 제공될 때까지 구현과 회귀
|
||||
검증의 임시 기준으로 사용한다.
|
||||
|
||||
### 11.1 공통 응답
|
||||
|
||||
모든 성공 응답은 `ApiResponse.ok(...)` wrapper를 사용한다.
|
||||
`/api/v2/admin/ai-characters` 아래 OpenAPI operation의 성공 응답은
|
||||
`success`, `message`, `data`, `errorProperty`를 사용한다.
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": null,
|
||||
"data": {}
|
||||
"data": {},
|
||||
"errorProperty": null
|
||||
}
|
||||
```
|
||||
|
||||
`/admin/member/login`과 `/member/logout` 성공 예시는 최상위 `errorProperty=null`도 포함한다. 공통 API client는 성공 응답에서 `errorProperty`가 없거나 `null`인 두 형태를 모두 수용한다.
|
||||
mutation에 따라 `data`는 상세 DTO, ID DTO 또는 `null`이다. 각 operation의
|
||||
response schema를 따르며 공통 client가 임의의 상세 응답으로 정규화하지
|
||||
않는다.
|
||||
|
||||
모든 오류는 의미에 맞는 비2xx status와 `ApiResponse.error(...)` wrapper를 사용한다.
|
||||
오류는 의미에 맞는 비2xx status와 다음 envelope를 사용한다.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -591,10 +627,13 @@ python3 .codex/skills/ui-ux-pro-max/scripts/search.py \
|
||||
|
||||
- 오류를 2xx로 normalize하지 않는다.
|
||||
- UI는 서버가 반환한 현지화된 한국어 `message`를 우선 사용한다.
|
||||
- 백엔드는 `Accept-Language: ko|en|ja`에 따라 번역하고 누락·미지원 언어는 KO로 fallback한다. security filter도 header를 직접 해석한다.
|
||||
- 모든 목록·검색 endpoint는 `page=0`, `size=20` 기본값과 size 최소 20, 최대 50 보정을 적용한다.
|
||||
- OpenAPI의 `Accept-Language`는 optional이고 기본값은 `ko`다. `ko|en|ja` 이외 값과 header 누락은 KO로 fallback하며, 프론트엔드는 일관되게 `ko`를 보낸다.
|
||||
- OpenAPI operation은 전역 `bearerAuth`를 사용한다. 로그인 이외의 관리자 API에는 `Authorization: Bearer {jwt-token}`을 보낸다.
|
||||
- 공통 `page`는 기본 `0`, 최소 `0`이고 공통 `size`는 기본 `20`, 최소 `1`이다. 전역 최대 `50`은 없다. FanTalk `size`만 설명에 따라 `20..50`으로 보정된다.
|
||||
- Audio 상세와 Community 목록은 필수 `timezone=Asia/Seoul` query를 보낸다.
|
||||
- `characterId`는 선택된 대상 캐릭터가 필요한 하위 resource endpoint에만 사용한다.
|
||||
- 캐릭터 목록·검색과 캐릭터 생성에는 path `characterId`가 없다.
|
||||
- `/admin/member/login`, `/member/logout`은 현 OpenAPI 범위 밖의 기존 인증 계약이다. 두 endpoint의 현재 구현 기준은 `11.5 EXT-006`에 기록하고 Phase 1 구현과 회귀 검증을 유지하되, 정식 OpenAPI operation으로 표기하지 않는다.
|
||||
|
||||
### 11.2 오류 처리 매핑
|
||||
|
||||
@@ -603,67 +642,121 @@ python3 .codex/skills/ui-ux-pro-max/scripts/search.py \
|
||||
| 400 | binding, target 미존재, creator 불변식 등 invalid request | 로컬 검증은 inline, 서버 `errorProperty`가 필드명을 제공할 때만 inline, 그 외 화면 Alert |
|
||||
| 401 | JWT 없음·잘못됨·만료·폐기 | 인증 제거 후 로그인 이동 |
|
||||
| 403 | 비ADMIN 또는 stale ADMIN claim | 접근 거부 화면 |
|
||||
| 404 | 신규 prefix 미매핑 경로 | 찾을 수 없음과 목록 이동 |
|
||||
| 404 | path 또는 target 미존재 | 찾을 수 없음과 가능한 목록 이동 |
|
||||
| 405 | 지원하지 않는 method | 서버 message와 재시도 불가 안내 |
|
||||
| 406 | 허용되지 않는 표현 또는 응답 조건 | 서버 message와 요청 조건 확인 안내 |
|
||||
| 415 | 지원하지 않는 media type | 파일 필드 오류와 허용 형식 안내 |
|
||||
| 500 | 예상하지 못한 오류 | 일반 오류, request ID가 있으면 함께 표시, 재시도 |
|
||||
|
||||
Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와 KO/EN/JA message key를 확정해야 한다. 신규 prefix 전용 처리를 legacy/public endpoint로 확장하지 않는다.
|
||||
OpenAPI는 공통 status와 `ApiErrorResponse` shape만 제공하고 도메인별 정확한
|
||||
message key를 열거하지 않는다. 특정 message key 분기가 필요한 기능은
|
||||
구현 전에 backend 계약을 추가로 받아야 하며, 그전에는 status와
|
||||
`errorProperty`가 제공된 경우만 공통 처리한다.
|
||||
|
||||
### 11.3 제공된 endpoint 목록
|
||||
|
||||
모든 query, multipart part, request/response field를 보존한 보정 후 계약은 [api-contract.md](./api-contract.md)를 기준으로 한다.
|
||||
AI 캐릭터 관리자 domain의 query, multipart part, request/response field와
|
||||
오류 응답은 [api-contract.openapi.json](./api-contract.openapi.json)을
|
||||
기준으로 한다. 인증 두 건은 현재 구현·test 기준을 백엔드 정식화 입력으로
|
||||
함께 표시한 것이며 현 OpenAPI operation에는 포함되지 않는다.
|
||||
|
||||
| 영역 | Method | Path | 계약 상태 |
|
||||
|---|---|---|---|
|
||||
| 인증 | POST | `/admin/member/login` | 제공됨, body는 email/password, 성공 시 token/ADMIN role |
|
||||
| 인증 | POST | `/member/logout` | 제공됨, 공통 endpoint, Bearer header, body 없음 |
|
||||
| 캐릭터 | GET, POST | `/api/v2/admin/ai-characters` | 제공됨, 필드 보정 필요 |
|
||||
| 캐릭터 | GET, PUT | `/api/v2/admin/ai-characters/{characterId}` | 제공됨, 필드 보정 필요 |
|
||||
| 인증 | POST | `/admin/member/login` | 현재 구현·test 기준, OpenAPI 미포함(`EXT-006`) |
|
||||
| 인증 | POST | `/member/logout` | 현재 구현·test 기준, OpenAPI 미포함(`EXT-006`) |
|
||||
| 캐릭터 | GET, POST | `/api/v2/admin/ai-characters` | 제공됨 |
|
||||
| 캐릭터 | GET, PUT | `/api/v2/admin/ai-characters/{characterId}` | 제공됨 |
|
||||
| 오디오 테마 | GET | `/api/v2/admin/ai-characters/audio-content-themes` | 제공됨, query/body 없음 |
|
||||
| 오디오 | GET, POST | `/api/v2/admin/ai-characters/{characterId}/audio-contents` | 제공됨, 생성 필드 보정 필요 |
|
||||
| 오디오 | GET, POST | `/api/v2/admin/ai-characters/{characterId}/audio-contents` | 제공됨 |
|
||||
| 오디오 | GET, PUT | `.../audio-contents/{contentId}` | 제공됨 |
|
||||
| 시리즈 | GET, POST | `/api/v2/admin/ai-characters/{characterId}/series` | 제공됨, enum 보정 필요 |
|
||||
| 시리즈 | GET, PUT | `.../series/{seriesId}` | 제공됨, enum 보정 필요 |
|
||||
| 시리즈 콘텐츠 | GET, POST | `.../series/{seriesId}/contents` | 제공됨 |
|
||||
| 시리즈 콘텐츠 | DELETE | `.../series/{seriesId}/contents/{contentId}` | 제공됨 |
|
||||
| 시리즈 | GET, POST | `/api/v2/admin/ai-characters/{characterId}/series` | 제공됨 |
|
||||
| 시리즈 순서 | PUT | `.../series/orders` | 제공됨 |
|
||||
| 커뮤니티 | GET, POST | `.../{characterId}/community-posts` | 제공됨, 생성 필드 보정 필요 |
|
||||
| 시리즈 | GET, PUT | `.../series/{seriesId}` | 제공됨 |
|
||||
| 시리즈 콘텐츠 | GET, POST | `.../series/{seriesId}/contents` | 제공됨 |
|
||||
| 시리즈 연결 후보 | GET | `.../series/{seriesId}/contents/search` | 제공됨 |
|
||||
| 시리즈 콘텐츠 | DELETE | `.../series/{seriesId}/contents/{contentId}` | 제공됨 |
|
||||
| 커뮤니티 | GET, POST | `.../{characterId}/community-posts` | 제공됨 |
|
||||
| 커뮤니티 | PUT | `.../community-posts/{postId}` | 제공됨 |
|
||||
| FanTalk 목록 | GET | `.../{characterId}/fan-talks` | 제공됨 |
|
||||
| FanTalk 답변 | POST | `.../{characterId}/fan-talks/{fanTalkId}/replies` | 제공됨 |
|
||||
|
||||
### 11.4 API 계약 보정사항
|
||||
### 11.4 OpenAPI 소비 시 주의사항
|
||||
|
||||
이 표는 최초 API Contract보다 우선한다.
|
||||
아래 표는 삭제된 Markdown 계약을 기준으로 작성된 기존 문서·구현 계획을
|
||||
OpenAPI로 전환할 때 반드시 반영할 차이다. 제품 정책이 OpenAPI payload에
|
||||
없는 field를 추가하는 근거로 사용돼서는 안 된다.
|
||||
|
||||
| 항목 | 최초 계약 | 확정 보정 |
|
||||
| 영역 | OpenAPI 계약 | 프론트엔드 처리 |
|
||||
|---|---|---|
|
||||
| Character `externalCharacterId` | 요청·응답에 존재 | 존재하지 않는 필드이므로 전부 제거 |
|
||||
| 생성 `isActive` | Character, Audio, Community 예시에 존재 | 모든 생성 요청에서 제거, 서버가 초기값 결정 |
|
||||
| 수정 `isActive` | 수정 예시에 존재 | 일반 수정에서는 key를 생략하고 soft delete에만 `false`를 보낸다. `true`는 전송하지 않는다. |
|
||||
| Audio status | 예시에 `OPEN` | 허용값은 `OPEN`, `SCHEDULED`이며 서버 계산 |
|
||||
| Audio create `themeId` | 누락 | 생성 시 필수이며 오디오 테마 목록 endpoint에서 선택한 `themeId`를 보낸다. |
|
||||
| Series state | 예시에 `OPEN` | `PROCEEDING`, `SUSPEND`, `COMPLETE`만 허용 |
|
||||
| Series 생성 state | 요청 예시에 `OPEN` | 생성 요청에서 state 제거 |
|
||||
| Series 수정 state | 필수처럼 표현 | 선택 필드, 미선택 시 생략하여 기존 값 유지 |
|
||||
| Series 요일 | `MONDAY` 등 장문 값 | `SUN`~`SAT`와 `RANDOM` 사용 |
|
||||
| RANDOM | 규칙 없음 | 단독만 허용, 다른 요일과 조합 금지 |
|
||||
| Character creator | 응답 연결만 표현 | 생성 시 AI_CHARACTER creator 동시 생성, 프로필 동기화는 백엔드 담당 |
|
||||
| FanTalk 답변 | POST만 표현 | 한 번만 생성, 기존 답변 수정 가능, 삭제 불가 |
|
||||
| Character 목록 | query `searchTerm`; `data={totalCount,content}`; item ID `id` | `search`·`items`·`hasNext`로 바꾸지 않는다. |
|
||||
| Character 생성 | 필수 multipart `image`, `request`; request의 필수 `name`, `systemPrompt`, `description`; 성공 `data=null` | 생성 후 목록을 재조회하고 새 ID를 응답에서 추정하지 않는다. |
|
||||
| Character 상세 | `characterUUID`, `originalWork`를 포함하고 creator field는 없음 | `characterUUID`를 `externalCharacterId`로 취급하지 않고 creator UI는 만들지 않는다. |
|
||||
| Audio 목록 | query `search_word`; `data={totalCount,items}`; status query/field 없음 | 2자 이상 제목 검색만 보내고 status filter·badge를 만들지 않는다. |
|
||||
| Audio 테마 | `data=[{id,theme,image}]` | `themeId`·`themeName`·`imageUrl`로 역직렬화하지 않는다. |
|
||||
| Audio 생성 | multipart `contentFile`, `coverImage`, `request`; `releaseDate`는 `yyyy-MM-dd HH:mm`; 성공 `data.contentId` | `audioFile`, `releaseDateUtc`, `seriesIds`를 보내지 않는다. |
|
||||
| Audio 수정 | optional `coverImage`와 제한된 request field만 제공 | content file, 공개 예약, theme, series 연결 수정 UI를 제공하지 않는다. |
|
||||
| Series 생성 | 필수 `image`; request의 `keyword`는 문자열; 성공 `data=null` | `keywords` 배열을 보내지 않고 목록으로 이동해 재조회한다. |
|
||||
| Series 상세 | 요일·장르·keywords·state가 표시용 문자열 | 목록 enum 또는 update payload 값으로 재사용하지 않는다. |
|
||||
| Series 연결·순서 | 후보 `contents/search`; 연결 `{contentIdList}`; 순서 `{ids}` | `{contentIds}`, `{seriesIds}`를 보내지 않는다. |
|
||||
| Community 목록 | 필수 `timezone`; `data`는 배열이고 pagination metadata 없음 | `audioUrl`을 사용하고 total/hasNext를 추정하지 않는다. |
|
||||
| Community 생성·수정 | 생성 part `postImage`/`audioFile`; 수정은 `postImage`만 교체 가능; mutation `data=null` | `image`, `audioSignedUrl`, 수정 audio/price field를 만들지 않는다. |
|
||||
| FanTalk | 목록 GET과 답변 POST만 제공 | 목록 item 기반 UI와 답변 생성만 구현하고 상세·수정·filter/sort는 외부 의존으로 둔다. |
|
||||
|
||||
### 11.5 백엔드 제공 대기 계약
|
||||
|
||||
아래 항목은 프론트엔드 인터뷰로 결정할 Open Question이 아니라 **외부 의존**이다. P0의 request/response/error 계약을 제공받기 전에는 해당 기능의 network integration을 구현하지 않는다. P1은 추정하지 않고 출시 전 계약과 검증을 맞춘다. UI shell과 계약에 의존하지 않는 표현 작업은 병행할 수 있다.
|
||||
아래 항목은 프론트엔드 인터뷰로 결정할 Open Question이 아니라 **외부
|
||||
의존**이다. P0 계약을 제공받기 전에는 영향을 받는 network integration을
|
||||
구현하지 않는다. UI shell과 계약에 의존하지 않는 표현 작업은 병행할 수
|
||||
있다. 단, `EXT-006`은 이미 구현·검증된 기존 인증 endpoint의 정식 문서화
|
||||
의존이므로 Phase 3~9 진행을 차단하지 않는다.
|
||||
|
||||
| 우선순위 | 백엔드 제공 필요 계약 | 프론트엔드 영향 |
|
||||
|---:|---|---|
|
||||
| P0 | FanTalk 목록·상세·답변 수정·유일성 | 목록·상세·수정 연동과 동시 중복 답변 처리 대기 |
|
||||
| P0 | 오디오·커뮤니티 댓글 CRUD와 팬 댓글 삭제 권한 오류 | 댓글·답글 연동과 권한별 오류 처리 대기 |
|
||||
| P0 | 원작·장르 검색과 originalWork 미선택 직렬화 | Character·Series 선택기 연동 대기 |
|
||||
| P0 | 시리즈 연결 후보 | 연결 가능한 활성 오디오 선택기 연동 대기 |
|
||||
| P0 | 시리즈 전체 순서의 50개 초과 로딩·누락 ID·동시 충돌 | 전체 순서 저장 연동 대기 |
|
||||
| P1 | price 최대값 | 현재 0 이상 정수 규칙만 적용하며 상한 계약 제공 시 Audio·Community schema와 경계값 test 갱신 |
|
||||
| P0 | 신규 도메인 오류 | 기능별 정확한 비2xx status와 KO/EN/JA message key 제공 대기 |
|
||||
| ID | 우선순위 | 백엔드 제공 필요 계약 | 프론트엔드 영향 |
|
||||
|---|---:|---|---|
|
||||
| EXT-001 | P0 | 원작 검색 lookup | Character 원작 선택기 network integration 대기 |
|
||||
| EXT-002 | P0 | 장르 검색 lookup | 유효한 `genreId` 선택이 필요한 Series 생성 integration 대기 |
|
||||
| EXT-003 | P0 | Series 수정 form 초기화용 edit DTO 또는 표시 문자열 mapping | 직접 링크에서 genreId·요일 enum·state enum을 안전하게 복원하는 수정 integration 대기 |
|
||||
| EXT-004 | P0 | FanTalk 상세·답변 수정·답변 상태 filter·sort·유일성 오류 | 목록 item 밖의 상세·수정, 전체 결과 filter와 동시 중복 답변 처리 대기 |
|
||||
| EXT-005 | P0 | 오디오·커뮤니티 댓글 CRUD와 팬 댓글 삭제 권한 오류 | 댓글·답글 연동과 권한별 오류 처리 대기 |
|
||||
| EXT-006 | P0 비차단 | 현재 구현된 `POST /admin/member/login`, `POST /member/logout`의 정식 OpenAPI 포함 또는 별도 버전 고정 계약 | 현재 구현과 Phase 1 회귀는 유지하며 Phase 3~9를 차단하지 않는다. 신규 인증 변경과 전체 계약의 단일 추적성만 정식 계약 제공 대기다. |
|
||||
| EXT-007 | P0 | Character 검색 결과와 Audio·Series·Community 목록의 active-only 반환 보장 | soft delete 뒤 비활성 항목이 서버 목록에서 제외된다는 수용 기준 검증 대기 |
|
||||
| EXT-008 | P1 | Community pagination의 total/hasNext 또는 종료 규칙 | 신뢰할 수 있는 전체 건수와 마지막 page UI 대기 |
|
||||
| EXT-009 | P1 | price 최대값 | 현재 0 이상 정수 규칙만 적용하며 상한 계약 제공 시 Audio·Community schema와 경계값 test 갱신 |
|
||||
| EXT-010 | P1 | 파일 크기·MIME·crop·container/codec의 backend 검증 계약 | PRD의 client 사전 검증은 유지하되 server와 동일 경계라는 완료 주장은 계약 제공 후 검증 |
|
||||
| EXT-011 | P0 | 도메인별 오류 | 기능별 정확한 비2xx status와 message key 분기가 필요한 흐름 대기 |
|
||||
|
||||
#### EXT-006 현재 구현 기준 계약
|
||||
|
||||
다음 내용은 현재 프론트엔드 adapter·contract test·E2E가 사용하는 기존
|
||||
인증 계약이다. 백엔드는 이를 정식 OpenAPI operation으로 옮기거나 별도
|
||||
버전 고정 계약으로 제공할 수 있다. 정식 계약이 제공되기 전에도 현재
|
||||
로그인·로그아웃 구현은 유지하며 Phase 3~9를 진행한다.
|
||||
|
||||
| 항목 | Admin Login | Member Logout |
|
||||
|---|---|---|
|
||||
| Method·Path | `POST /admin/member/login` | `POST /member/logout` |
|
||||
| `Accept-Language` | `ko` | `ko` |
|
||||
| `Content-Type` | `application/json` | body 없음 |
|
||||
| Authorization | 보내지 않음 | `Bearer {jwt-token}` 필수 |
|
||||
| Request body | `{ "email": string, "password": string }` | 없음 |
|
||||
| 성공 `data` | `{ "token": string, "role": "ADMIN" }` | `{}` |
|
||||
| 현재 client 처리 | 유효한 token·ADMIN role만 session으로 저장 | 성공·비2xx·network 오류 모두 local session 제거 후 `/login` 이동 |
|
||||
|
||||
성공 응답은 현재 다음 envelope를 사용한다.
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": null,
|
||||
"data": {},
|
||||
"errorProperty": null
|
||||
}
|
||||
```
|
||||
|
||||
- 로그인 성공에서는 `data`가 `{ "token": "jwt-token", "role": "ADMIN" }`이다.
|
||||
- 로그아웃 성공에서는 `data`가 `{}`다.
|
||||
- 현재 contract fixture는 로그인 JSON binding 실패 `400`, 지원하지 않는 media type `415`, 로그아웃의 Bearer 없음·잘못됨·폐기 `401`, 비ADMIN `403`, body 전송 `400`을 검증한다.
|
||||
- 정확한 backend 오류 message key와 추가 status는 정식 계약 제공 시 확정한다. 제공 전에는 기존 공통 envelope와 서버 `message` 우선 표시를 유지한다.
|
||||
- 구현 근거는 `src/features/auth/api/auth-api.ts`, `src/features/auth/model/auth-session.tsx`, `src/features/auth/tests/auth-api.test.ts`, `src/features/auth/tests/auth-session.test.tsx`, `src/shared/mocks/__tests__/auth-handlers.test.ts`, `tests/e2e/auth.spec.ts`다.
|
||||
|
||||
## 12. 보안과 데이터 취급
|
||||
|
||||
@@ -686,7 +779,7 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
|
||||
|
||||
## 13. 성능과 품질 요구사항
|
||||
|
||||
- 목록은 서버 페이지네이션을 사용하고 무제한 전체 로드를 피한다. 단, 시리즈 순서 변경 모드는 계약상 활성 시리즈 전체를 명시적으로 로드한다.
|
||||
- 목록은 계약에 있는 서버 page/size를 사용하고 무제한 전체 로드를 피한다. 시리즈 순서 변경 모드는 `totalCount`를 기준으로 모든 page를 읽는다. Community는 종료 metadata 계약이 제공되기 전까지 전체 건수·마지막 page를 표시하지 않는다.
|
||||
- 이미지에는 고정 aspect ratio와 크기를 예약해 layout shift를 줄이고 목록 이미지는 lazy load한다.
|
||||
- 필터 변경 중 기존 데이터를 유지해 화면 깜빡임을 줄인다.
|
||||
- 파일 업로드 외의 일반 mutation은 중복 제출을 막는다.
|
||||
@@ -694,7 +787,7 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
|
||||
- 날짜, 가격, 상태 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와 함께 변경한다.
|
||||
- domain fixture와 browser handler는 `api-contract.openapi.json`의 제공 계약에서 파생하고 contract test와 함께 변경한다.
|
||||
|
||||
## 14. 성공 기준
|
||||
|
||||
@@ -705,12 +798,12 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
|
||||
- 로그인 성공 후 JWT와 ADMIN role이 `sessionStorage`에만 저장되어 같은 탭의 새로고침에서 복원되고, `localStorage`, IndexedDB, cookie에는 기록되지 않으며 로그아웃과 401에서 제거된다.
|
||||
- 로그아웃 요청이 관리자 전용 경로가 아닌 `POST /member/logout`에 Bearer header와 body 없이 전송된다.
|
||||
- 로그아웃 API가 성공하거나 네트워크·비2xx 오류로 실패해도 로컬 session이 제거되고 로그인 화면으로 이동하며, 실패한 경우 경고가 표시되고 session이 복원되지 않는다.
|
||||
- 캐릭터 생성 요청에 `isActive`와 `externalCharacterId`가 포함되지 않는다.
|
||||
- 캐릭터 생성 multipart가 필수 `image`와 `request`를 보내고 request에 `name`, `systemPrompt`, `description`이 포함되며 `isActive`와 `externalCharacterId`는 포함되지 않는다.
|
||||
- Character, Audio, Series, Community의 일반 수정은 `isActive`를 생략하고 soft delete에만 `isActive=false`를 보내며 `true`는 전송하지 않는다.
|
||||
- Character, Audio, Series의 soft delete가 성공하면 해당 active-only 목록 cache를 갱신해 비활성화한 항목을 표시하지 않고 목록으로 이동하며 성공 알림을 표시한다. Community는 열린 Sheet를 닫고 현재 active-only 목록을 갱신해 해당 항목을 제거한다.
|
||||
- Character, Audio, Series의 soft delete가 성공하면 목록 cache를 무효화·재조회하고 목록으로 이동하며 성공 알림을 표시한다. Community는 열린 Sheet를 닫고 목록을 재조회한다. 서버 목록에서 비활성 항목이 제외되는지는 active-only 계약 제공 후 검증한다.
|
||||
- 캐릭터 수정이 creator profile 동기화 결과와 함께 다시 조회된다.
|
||||
- 워크스페이스 진입·복원용 상세 성공 응답으로 `isActive=false`인 캐릭터를 받은 경우 프론트엔드가 모든 하위 mutation 진입점을 차단한다. soft delete 성공 직후에는 active-only 목록 이동을 우선하고, 상세 요청의 비2xx 응답은 공통 오류 처리로 표시한다.
|
||||
- 오디오 생성에서 즉시 공개는 `releaseDateUtc=null`, 예약 공개는 미래 UTC 시각을 보낸다.
|
||||
- 워크스페이스 진입·복원용 상세 성공 응답으로 `isActive=false`인 캐릭터를 받은 경우 프론트엔드가 모든 하위 mutation 진입점을 차단한다. soft delete 성공 직후에는 목록 이동·재조회를 우선하고, 상세 요청의 비2xx 응답은 공통 오류 처리로 표시한다.
|
||||
- 오디오 생성에서 즉시 공개는 `releaseDate=null`, 예약 공개는 미래 Asia/Seoul 시각을 `yyyy-MM-dd HH:mm`로 보내고 `timezone="Asia/Seoul"`을 포함한다.
|
||||
- MP3, AAC, M4A 업로드의 진행률·취소·재시도와 `1,024,000,000 bytes` 허용·`1,024,000,001 bytes` 거부 경계 검증이 동작한다.
|
||||
- 오디오 콘텐츠와 커뮤니티 첨부 audio가 동일한 확장자·MIME·최대 크기·재생 길이 정책을 사용한다.
|
||||
- `.m4a`는 `audio/mp4`와 `audio/x-m4a`를 허용하되 호환 MIME도 실제 MP4/M4A container·codec 검증을 통과해야 한다.
|
||||
@@ -722,15 +815,15 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
|
||||
- 커뮤니티 GIF는 crop Dialog를 열지 않고 원본 비율과 animation을 유지해 등록하며, 원본 가로가 800px을 초과하면 제출 전에 거부한다.
|
||||
- crop Dialog에서 이동·확대/축소·초기화·미리보기·취소/적용을 keyboard와 pointer로 완료할 수 있다.
|
||||
- 오디오 목록·상세와 커뮤니티 목록 항목의 첨부 오디오가 재생된다.
|
||||
- 오디오 콘텐츠 생성 시 테마 목록을 query/body 없이 조회하고 선택한 `themeId`를 request JSON에 포함한다.
|
||||
- 오디오 콘텐츠 생성 시 테마 목록 `data[]`의 `id`, `theme`, `image`를 query/body 없이 조회하고 선택한 `id`를 request의 `themeId`에 포함한다.
|
||||
- media error를 signed URL 만료로 구분하지 않고 일반 재생 오류를 표시한다. 오류만으로 API를 자동 재조회하거나 자동 재생하지 않으며 사용자가 수동 재시도하거나 페이지를 새로고침할 수 있다.
|
||||
- 커뮤니티는 전용 상세 GET이나 상세·수정 route 없이 목록 응답 기반 Sheet에서 조회·수정·고정·비활성화·댓글 진입을 제공한다.
|
||||
- Series에 `OPEN`이나 `MONDAY` 같은 잘못된 값을 보내지 않는다.
|
||||
- Series 생성에는 state를 보내지 않고 수정 미선택 시 state를 생략한다.
|
||||
- Series 생성에는 state를 보내지 않고 `keyword` 문자열을 사용하며, 수정에서 state를 바꾸지 않으면 field를 생략한다.
|
||||
- RANDOM과 실제 요일을 동시에 선택할 수 없다.
|
||||
- FanTalk 답변이 있으면 두 번째 POST가 UI에서 차단되고 수정 동작만 제공되며, 직접·동시 요청도 백엔드가 원자적으로 거부한다.
|
||||
- 댓글은 2단계를 넘지 않고 작성자에 따른 수정·삭제 권한이 구분된다.
|
||||
- 모바일에서 조회·오디오 재생·댓글 관리·FanTalk 답변 작성/수정이 가능하다.
|
||||
- FanTalk 목록 item의 `creatorReplies`에 답변이 있으면 두 번째 POST를 UI에서 차단한다. 답변 수정·전체 결과 filter·직접 또는 동시 중복 요청 거부는 외부 계약이 제공된 뒤 수용 기준을 활성화한다.
|
||||
- 댓글 계약이 제공된 뒤 댓글은 2단계를 넘지 않고 작성자에 따른 수정·삭제 권한이 구분된다.
|
||||
- 모바일에서 조회·오디오 재생과 FanTalk 답변 작성이 가능하다. 댓글 관리와 FanTalk 답변 수정은 각 외부 계약 제공 후 같은 capability로 활성화한다.
|
||||
- `npm run dev:mock`에서 실제 backend 요청 없이 제공 계약 범위의 최종 UI happy path를 확인할 수 있고 mock mode 안내가 표시된다.
|
||||
- 기본 `npm run dev`에서는 실제 개발 API를 사용하며 404·network error가 mock 응답으로 바뀌지 않는다.
|
||||
- production build에는 browser mock이 활성화되지 않고 mock mode 설정을 허용하지 않는다.
|
||||
@@ -750,11 +843,11 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
|
||||
- 모든 핵심 route의 axe 기반 자동 검사에서 critical·serious 접근성 위반이 0건이다.
|
||||
- `ui-ux-pro-max`의 loading, reduced motion, z-index, touch 검증 항목을 확인한다.
|
||||
|
||||
## 15. Open Questions
|
||||
## 15. Open Questions와 결정 절차
|
||||
|
||||
| ID | 상태 | 결정 필요 사항 | 현재 권고 |
|
||||
|---|---|---|---|
|
||||
| OQ-009 | 미결 | 각 페이지의 문자열 최대 길이와 배열 최대 개수 | 초기 UI를 만든 뒤 Character의 이름·설명, Audio의 제목·설명·`seriesIds`, Series의 제목·소개·요일·keywords·writer·studio·연결 `contentIds`·순서 `seriesIds`, Community 본문, FanTalk 답변과 댓글을 페이지별로 검토해 권고값을 작성한다. 백엔드 호환 확인 후 확정하며, 그 전에는 제공 계약에 없는 임의의 최대값을 추가하지 않는다. 확정 시 PRD·API Contract·schema·경계값 test를 함께 갱신한다. |
|
||||
| OQ-009 | 확정 | 각 페이지의 문자열 최대 길이와 배열 최대 개수 | 초기 UI를 구현한 뒤 실제 페이지에서 문자열 입력 공간과 반복 항목 사용성을 검토해 최대 길이·최대 개수의 권고값을 정한다. backend 호환 확인 후 PRD·OpenAPI 계약·schema·경계값 test를 같은 변경에서 갱신한다. 그전에는 계약에 없는 임의의 최대값을 추가하지 않는다. |
|
||||
| OQ-010 | 미결 | 감사 로그 UI 제공 여부 | backend 기록 우선, 조회 UI는 후속 범위 |
|
||||
|
||||
## 16. 결정 기록
|
||||
@@ -795,3 +888,9 @@ Phase 2~6에서 추가되는 오류는 구현 전에 정확한 비2xx status와
|
||||
| 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 연동 완료를 분리한다. |
|
||||
| 2026-07-28 | 삭제된 `api-contract.md`를 `api-contract.openapi.json`으로 대체하고, endpoint·query·multipart·request/response·오류는 OpenAPI를 단일 진실 원천으로 사용한다. OpenAPI에 없는 제품·UI 정책만 PRD가 소유한다. |
|
||||
| 2026-07-28 | 새 OpenAPI에 맞춰 Character 생성 image/systemPrompt 필수, Audio의 `search_word`·`contentFile`·`releaseDate`·테마 field, Series의 `keyword`·`contentIdList`·`ids`, Community의 `timezone`·`postImage`·`audioUrl`, FanTalk 목록 GET을 구현 기준으로 정정한다. |
|
||||
| 2026-07-28 | OpenAPI에 없는 인증 정식 명세, 원작·장르 lookup, Series 수정 form의 edit DTO, 목록 active-only 보장, FanTalk 상세·답변 수정·filter/sort·유일성 오류, 댓글 CRUD와 도메인별 오류 key는 외부 의존으로 관리하며 추정 구현하지 않는다. |
|
||||
| 2026-07-28 | OQ-009는 초기 UI를 먼저 구현하고 실제 페이지를 보며 문자열 최대 길이와 배열 최대 개수를 제안한 뒤 backend 호환을 확인해 확정하는 절차로 종결한다. 실제 최대값 확정 전에는 임의 상한을 추가하지 않는다. |
|
||||
| 2026-07-28 | `EXT-006`에 현재 구현된 `POST /admin/member/login`과 `POST /member/logout`의 request·response·인증·client 처리 기준을 기록한다. 정식 OpenAPI 포함은 비차단 외부 의존이며 기존 인증 구현과 Phase 3~9 진행을 유지한다. |
|
||||
| 2026-07-28 | Phase 3부터는 OpenAPI 제공 범위를 먼저 구현해 Phase 9 활성 범위 Gate까지 진행한다. 미제공 계약은 영향을 받는 기능만 후속 범위로 남기고, 계약 도착 후 별도 vertical slice와 관련 Phase Gate·Phase 9를 다시 실행한다. |
|
||||
|
||||
Reference in New Issue
Block a user