Files
sodalive-backend-spring-boot/docs/20260724_AI캐릭터_관리자_API/prd.md

260 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PRD: AI 캐릭터 관리자 API
## 1. Overview
운영자가 AI 캐릭터용 Member로 직접 로그인하지 않고, `ADMIN` 권한으로 선택한 AI 캐릭터의 크리에이터 채널 자산을 대리 관리하는 신규 v2 관리자 API를 제공한다.
---
## 2. Problem
- AI 캐릭터용 `Member(memberKind = AI_CHARACTER)`는 직접 로그인할 수 없어야 하지만, 운영자는 캐릭터의 콘텐츠, 시리즈, 커뮤니티, FanTalk 답변을 관리해야 한다.
- 기존 기능은 `creatorMember.id` 기반으로 흩어져 있으며, 관리자 frontend가 레거시 endpoint를 조합하면 권한, 소유권, soft delete 의미가 일관되지 않을 수 있다.
- 기존 creator/admin service 일부에는 소유권 검증이 약한 경로가 있어, 단순 위임만으로는 다른 캐릭터나 HUMAN creator 자원을 변경할 위험이 있다.
- 기존 legacy/public API 계약은 유지해야 하므로 신규 관리자 표면은 별도 v2 경계로 제공되어야 한다.
---
## 3. Goals
- 신규 prefix `/api/v2/admin/ai-characters/**`는 JWT `auth` claim의 `ROLE_ADMIN`과 JWT subject로 조회한 현재 DB
`Member.role == ADMIN`을 모두 만족하는 요청만 허용한다.
- 모든 신규 target endpoint는 외부 대상 식별자로 `characterId`를 받고, 서버가 `ChatCharacter.creatorMember`를 내부 행위자로 해석한다. 단, 캐릭터 목록/검색은 아직 선택된 target이 없어 `characterId`를 받지 않고, 캐릭터 생성은 새 `ChatCharacter`를 만드는 endpoint라 path `characterId`를 받지 않는다.
- target 해석 시 `ChatCharacter` 존재, `creatorMember` 존재, `creatorMember.role == CREATOR`, `creatorMember.memberKind == AI_CHARACTER`를 모두 검증한다.
- 검증 실패 시 4xx로 거부하고 DB, S3, 외부 캐릭터 API, 이벤트 발행 등 후속 부작용을 만들지 않는다.
- 캐릭터, 오디오 콘텐츠, 시리즈, 커뮤니티 게시글, FanTalk 답변, 오디오 signed URL을 신규 관리자 API에서 관리한다.
- 기존 legacy/public endpoint의 URI, 성공·오류 HTTP status, response body, message/i18n을 포함한 외부 계약은 변경하지 않는다.
단, 캐릭터 관리자 frontend가 기존 관리자 인증을 재사용할 수 있도록 `/admin/member/login`, `/member/logout`의 CORS 허용
Origin만 path-specific으로 확장한다.
- 내부 구현은 신규 v2 controller/facade/application 경계를 두고, 기존 entity/repository/S3/CloudFront/event 컴포넌트는 테스트로 고정한 뒤 선택적으로 재사용한다.
---
## 4. Non-Goals
- 이번 PRD는 관리자 API backend 요구사항과 구현 계획만 포함하며, 관리자 UI/frontend 구현은 포함하지 않는다.
- AI 캐릭터용 Member의 access token, refresh token, 임시 세션, impersonation 로그인은 만들지 않는다.
- `creatorMemberId`를 관리자 frontend의 필수 입력으로 노출하지 않는다.
- HUMAN creator를 이 API로 대리 관리하지 않는다.
- 위 두 공유 인증 경로의 CORS 허용 Origin 확장 외 기존 legacy/public endpoint 변경, 폐기, deprecation, schema 변경은 포함하지
않는다.
- 기존 external character API business contract 변경은 포함하지 않으며, 변경이 필요하면 재확인한다.
- 기존 soft delete 의미 변경은 포함하지 않으며, 변경이 필요하면 재확인한다.
- 물리 삭제와 연관 데이터 cascade 삭제는 포함하지 않는다.
- 신규 DB schema/DDL 또는 `ChatCharacter`-`Member` 관계 모델 변경은 포함하지 않는다.
- 라이브, DM, 후원, 정산, 알림 설정, 랭킹 관리, 콘텐츠 구매/좋아요/댓글, 커뮤니티 구매/좋아요/댓글 관리는 포함하지 않는다.
- FanTalk 원글 작성, 일반 사용자 대리 작성, nested reply 작성은 포함하지 않는다.
- `AudioContentCloudFront` 복사/이동, signed URL 신규 dependency 추가는 포함하지 않는다.
---
## 5. Target Users
- 운영자: AI 캐릭터를 대신해 캐릭터 프로필, 콘텐츠, 시리즈, 커뮤니티 게시글, FanTalk 답변을 관리하는 관리자
- 관리자 frontend: 신규 v2 AI 캐릭터 관리자 API만으로 In-Scope 작업을 수행해야 하는 클라이언트
- 서버 개발자: 기존 creator 기능을 회귀시키지 않으면서 AI 캐릭터 대리 관리 경계를 유지해야 하는 개발자
---
## 6. User Stories
- 운영자는 AI 캐릭터 목록을 검색하고 상세 정보를 확인한 뒤 생성, 수정, 비활성화하고 싶다.
- 운영자는 선택한 AI 캐릭터 소유 오디오 콘텐츠를 조회, 생성, 수정, soft delete하고 관리자 화면에서 재생 가능한 signed URL을 받고 싶다.
- 운영자는 선택한 AI 캐릭터 소유 시리즈를 조회, 생성, 수정, soft delete하고 콘텐츠 연결/해제/순서를 관리하고 싶다.
- 운영자는 선택한 AI 캐릭터 소유 커뮤니티 게시글을 작성, 수정, 고정/해제, soft delete하고 싶다.
- 운영자는 선택한 AI 캐릭터가 자신의 활성 root FanTalk에 creator reply를 작성하게 하고 싶다.
- 서버는 다른 AI 캐릭터나 HUMAN creator의 resource ID가 전달되면 변경 없이 4xx로 거부해야 한다.
---
## 7. Core Features
### Feature A. 공통 인증, 인가, target 해석
#### Requirements
- 모든 신규 prefix endpoint는 JWT `ROLE_ADMIN`과 현재 DB `Member.role == ADMIN`을 독립적으로 모두 검증한다.
- JWT가 없거나 잘못됐거나 만료·폐기된 경우는 401, JWT role과 현재 DB role 중 하나라도 ADMIN이 아닌 경우는 403으로
처리한다.
- JWT에는 `ROLE_ADMIN`이 남아 있지만 현재 DB role이 강등된 stale claim도 403으로 거부한다.
- 모든 domain write/read는 `characterId``ChatCharacter`를 조회한 뒤 연결된 `creatorMember`를 사용한다.
- `creatorMember`는 도메인 소유권/작성자 판단에만 사용하고 Spring Security principal로 교체하지 않는다.
- `creatorMember` 누락, role 불일치, memberKind 불일치 요청은 4xx로 거부한다.
- 요청 중 누락 Member 생성, role/memberKind 자동 보정 같은 lazy repair는 하지 않는다.
#### Edge Cases
- stale claim을 포함한 인증·인가 실패는 target resolver와 domain use-case 실행 전에 종료되어야 한다.
- 유효하지 않은 `characterId` 요청은 DB write, S3 upload/delete, 외부 캐릭터 API 호출, 이벤트 발행 없이 실패해야 한다.
- 다른 AI 캐릭터 또는 HUMAN creator 소유 resource ID는 조회/수정/삭제/연결/답변 모두 거부해야 한다.
### Feature B. AI 캐릭터 관리
#### Requirements
- 목록 조회, 검색, 상세 조회, 생성, 수정, 삭제 의미의 비활성화(`isActive=false`)를 제공한다.
- 레거시 플랫폼 관리자와 중복 이름 검증, 외부 캐릭터 API 연동, 대표 이미지 저장, 원작 연결, 언어 감지/번역 이벤트, AI 캐릭터용 `creatorMember` 생성 및 표시 정보 동기화 동작 parity를 유지한다.
- 삭제는 soft delete이며 row, 연결 Member, 콘텐츠를 물리 삭제하지 않는다.
#### Edge Cases
- 중복 이름, 외부 캐릭터 API 실패, 이미지 저장 실패는 기존 관리자 동작을 특성화 테스트로 고정한 뒤 유지한다.
- 비활성화 실패 시 일부 관계만 변경된 상태로 남기지 않는다.
### Feature C. 오디오 콘텐츠 관리 및 signed URL
#### Requirements
- 캐릭터 소유 콘텐츠 목록/검색/상세 조회, 생성, 수정, 기존 삭제 동작에 따른 soft delete를 제공한다.
- 콘텐츠 생성 화면에서 사용할 활성 콘텐츠 테마(카테고리) 목록 조회를 제공한다. 기존 크리에이터 관리자 콘텐츠 등록 화면의 테마 조회와 같은 기능이며, 신규 v2 응답 필드는 `themeId`, `themeName`, `imageUrl`로 명확히 구분한다.
- 기존 크리에이터 콘텐츠 관리의 검증, 파일 처리, content upload/processing pipeline, 가격, 공개/예약, 번역/알림 등 business behavior parity를 유지한다.
- 관리자 화면 재생용 signed URL을 콘텐츠 목록/상세 응답에 제공한다.
- signed URL은 공통 `AudioContentCloudFront`를 재사용하고 기존 크리에이터 관리자와 같은 만료 정책을 따른다.
- 기존 signed URL 구현을 재사용하기 전에 creator admin 만료 계산식과 만료 계산·path 처리에서 실제로 관찰되는 edge case를 통과하는 특성화 테스트로 고정한다.
- 응답에 private object path나 서명 키 정보를 노출하지 않는다.
#### Edge Cases
- 콘텐츠 소유자가 target `creatorMember`와 다르면 조회/수정/삭제 모두 거부한다.
- 커뮤니티 오디오의 기존 30분 signed URL 정책은 이 콘텐츠 재생 정책과 임의 통합하지 않는다.
### Feature D. 시리즈 관리
#### Requirements
- 목록/상세 조회, 생성, 수정, `isActive=false` soft delete를 제공한다.
- 콘텐츠 연결/해제, 시리즈 콘텐츠 조회/검색, 순서 관리를 제공한다.
- 기존 creator series 관리의 생성/수정/soft delete, 콘텐츠 연결/해제, 조회/검색, 순서 관리 behavior를 먼저 통과하는 특성화 테스트로 고정하고 신규 v2 경로에서 parity를 유지한다.
- 시리즈와 연결 콘텐츠는 모두 동일한 `creatorMember` 소유여야 한다.
- 기존 `updateSeriesOrders(ids)`처럼 소유권 없는 ID-only 갱신은 신규 v2 경로에서 허용하지 않는다.
- 시리즈 콘텐츠 조회는 관리자 연결 작업을 위해 검색어 기반 필터를 제공한다.
#### Edge Cases
- 순서 변경 요청의 모든 series/content ID는 target character 소유 검증을 통과해야 한다.
- inactive series는 일반 활성 조회에서 제외한다.
### Feature E. 커뮤니티 게시글 관리
#### Requirements
- 등록, 수정, 공지 고정/해제(`isFixed`), 수정 요청의 `isActive=false` soft delete를 제공한다.
- soft delete 시 현재 동작처럼 `isFixed=false`, `fixedAt=null`을 적용한다.
- 기존 최대 고정 게시글 수 3개, 이미지/오디오/유료 게시글 검증, 알림/최근 소식 side effect를 유지한다.
- 관리자 UI에 필요한 조회는 기존 v2 커뮤니티 조회 로직을 무비판적으로 복제하지 않고 신규 관리자 facade/endpoint에서 안전하게 재사용하거나 최소 query adapter를 둔다.
#### Edge Cases
- 고정 게시글이 이미 3개인 상태에서 추가 고정은 기존 정책대로 실패한다.
- soft delete된 고정 게시글은 고정 상태와 시간이 반드시 제거되어야 한다.
### Feature F. FanTalk 답변
#### Requirements
- 선택한 AI 캐릭터가 자신의 활성 root FanTalk에 creator reply를 작성한다.
- 요청은 `characterId`와 대상 root `fanTalkId`를 포함한다.
- 대상 FanTalk가 존재하고 활성 상태이며, 대상 creator가 해석된 `creatorMember`와 일치하는 root 글인지 검증한다.
- 언어 감지와 기존 응답 DTO 의미 등 검증 가능한 business behavior를 유지하고, 저장된 답변의 writer/creator는 해석된 `creatorMember`와 일관되어야 한다.
#### Edge Cases
- 다른 캐릭터의 FanTalk, reply에 대한 nested reply, 비활성 FanTalk, 미존재 FanTalk에는 답변하지 않는다.
- 실패 시 reply 저장과 이벤트 발행이 없어야 한다.
---
## 8. API Expectations
- 신규 endpoint prefix는 기존 공개 `/api/v2/creator-channels/*`와 legacy `/admin/*`, `/creator-admin/*`를 변경하지 않기 위해 `/api/v2/admin/ai-characters`를 기본안으로 한다.
- 성공 응답은 `ApiResponse.ok(...)`, API application/controller/security filter 오류는 오류 의미에 맞는 HTTP status와
`ApiResponse.error(...)`를 사용한다.
- 이 API 오류 응답은 `success=false`와 현지화된 `message`를 포함하며 2xx로 normalize하지 않는다.
- `Accept-Language: ko|en|ja`에 따라 KO/EN/JA 메시지를 반환하고, 없거나 지원하지 않는 언어는 KO로 fallback한다.
- security filter 단계의 오류도 MVC interceptor에 의존하지 않고 `Accept-Language`를 직접 해석해 동일한 응답 계약을 따른다.
- 신규 prefix는 캐릭터 관리자 frontend Origin `http://localhost:8888`,
`https://test-character-admin.sodalive.net`, `https://character-admin.sodalive.net`만 허용한다.
- 기존 범용 관리자 frontend와 creator frontend Origin을 캐릭터 관리자 Origin 대신 허용하지 않는다.
- 공유 인증 경로 `/admin/member/login`, `/member/logout`는 기존 전역 Origin과 위 캐릭터 관리자 Origin의 합집합만 허용한다.
이 path-specific 확장은 다른 legacy/public 경로의 CORS 허용 범위를 변경하지 않는다.
- 위 관리자 Origin의 신규 prefix 오류와 preflight는 404 fallback 및 실제 mapped endpoint의 405/406/415 경로를 포함해 기존
전역 CORS 응답 계약을 유지하며, 두 공유 인증 경로에서도 허용·거부 Origin을 검증한다.
- 허용되지 않은 Origin, method 또는 header를 Spring CORS 계층에서 정책 거부하는 경우는 handler 진입 전 403으로 종료되는
브라우저 보안 경계다. 이 403의 body, content type, 현지화 및 `ApiResponse.error` envelope는 신규 API 오류 계약의 예외로
두고 외부 계약으로 고정하지 않는다.
- 표준 HTTP method가 MVC까지 도달했지만 해당 mapping이 없으면 기존 Spring MVC의 405와 `Allow` header를 유지한다.
- `StrictHttpFirewall`이 신규 prefix에서 비표준 HTTP method 또는 위험 URL을 `RequestRejectedException`으로 거부하면,
캐릭터 관리자 허용 Origin에는 CORS header를 포함한 400 `common.error.invalid_request`와 현지화된 `ApiResponse.error`
반환한다. 허용되지 않은 Origin은 기존 Spring CORS 정책과 같이 body 계약 없는 403으로 종료한다.
- `SecurityConfig`는 기존 `AiCharacterAdminSecurityErrorHandler`를 global `RequestRejectedHandler`로 등록하되 신규 prefix만 위
400/CORS 계약으로 처리하고, legacy/public은 `DefaultRequestRejectedHandler`에 위임해 기존 `RequestRejectedException` 동작을
유지한다. Spring 5.3의 비표준 method enum 한계 때문에 CORS 검사 request만 `GET` wrapper를 사용하며 실제 firewall method
허용 범위는 확장하지 않고 `setUnsafeAllowAnyHttpMethod(true)`도 사용하지 않는다.
- Phase 1 공통 오류는 인증 정보 없음·잘못됨·만료·폐기 401 `common.error.bad_credentials`, JWT 또는 현재 DB role의
ADMIN 불충족 403 `common.error.access_denied`, request/target 미존재·불변식 위반 400, 신규 prefix 미매핑 경로 404, 지원하지
않는 HTTP method 405, 응답 media type 406, 요청 media type 415를 `common.error.invalid_request`로 고정한다. 405는 표준
`Allow` header를, 415는 표준 `Accept` header를 유지한다. controller mapping의 필수 path variable 선언이 누락된
`MissingPathVariableException`과 예상하지 못한 controller/JWT filter 오류는 500 `common.error.unknown`으로 고정한다.
- malformed JSON의 `HttpMessageNotReadableException`, handler에 전달된 `MethodArgumentNotValidException`, multipart 필수 part
누락의 `MissingServletRequestPartException`은 각각 400 `common.error.invalid_request`와 KO/EN/JA `ApiResponse.error`를 반환한다.
- 이후 phase의 domain/client/server 오류는 각 task에서 정확한 HTTP status와 KO/EN/JA message key를 먼저 정의하고 같은
envelope를 적용한다.
- 신규 prefix 전용 오류 처리는 legacy/public endpoint의 기존 성공·오류 응답에 적용하지 않는다.
- page 기반 조회는 기존 v2 탭 API 관례를 따라 `page` 기본값 0, `size` 기본값 20, 최소 20, 최대 50 보정을 기본안으로 하며, 경계값 보정은 구현 task와 테스트에 포함한다.
- multipart 생성/수정은 기존 admin/creator-admin 관례대로 파일 part와 `request` JSON string part를 사용한다.
- `GET /api/v2/admin/ai-characters/audio-content-themes`는 request body 없이 활성 콘텐츠 테마 목록을 반환한다. 성공 응답 `data``[{ "themeId": 11, "themeName": "ASMR", "imageUrl": "https://cdn.example.com/audio-content-theme/asmr.png" }]` 형태이며, 기존 내부/legacy DTO의 `id`, `theme`, `image` 필드명을 외부 계약으로 노출하지 않는다.
- 오디오 콘텐츠 생성 `request` JSON에는 콘텐츠 테마 선택값인 `themeId`와 기존 `CreateAudioContentRequest`의 생성 필드 전체를 포함한다. v2는 `detail` 대신 `description`, `releaseDate` 대신 UTC ISO-8601 `releaseDateUtc`를 외부 계약으로 사용하고 legacy pipeline 호출 시 변환한다.
- 오디오 콘텐츠 목록 응답은 현 v2 관리자 목록 계약을 유지한다. 상세 응답은 기존 `GetAudioContentDetailResponse`의 필드 전체를 v2 상세 DTO에 포함하되, 구매·좋아요·핀·추천·댓글 목록처럼 viewer 상태가 필요한 필드는 관리자 상세에서 안전한 기본값을 반환한다.
- request/response DTO는 신규 v2 AI character admin API 전용 DTO로 두고 legacy/public DTO를 외부 계약으로 재노출하지 않는다.
---
## 9. Technical Constraints
- Kotlin, Java 17, Spring Boot 2.7.14, Gradle Wrapper를 유지한다.
- 신규 dependency를 추가하지 않는다.
- 신규 DB schema/DDL을 만들지 않는다.
- 기존 v2 API 조립 계층과 domain/application 의존 방향을 따른다.
- controller 내부 호출, 서버 내부 legacy HTTP 호출, 기존 controller 역참조는 하지 않는다.
- 신규 v2 application/domain 계층은 기존 controller와 v2 API response DTO를 역참조하지 않는다.
- 기존 business method를 재사용하기 전 특성화/회귀 테스트를 작성한다.
- 특성화/회귀 테스트는 신규 v2 use-case의 미구현 RED 테스트와 분리하고, 기존 legacy/creator-admin 구현을 대상으로 먼저 통과해야 한다.
- 단순 복사-붙여넣기 대신 필요한 최소 추출 또는 v2 use-case 재개발을 선택한다.
---
## 10. Metrics
- 신규 endpoint별 또는 controller slice별 JWT role × 현재 DB role 인가 매트릭스와 stale ADMIN claim 403 테스트 존재 여부
- 신규 prefix의 각 API 오류 분기에 정확한 HTTP status, `ApiResponse.error`, KO/EN/JA와 405 `Allow`/415 `Accept` header 테스트
존재 여부
- 신규 prefix 실제 mapped endpoint 및 공유 인증 경로의 허용·거부 Origin/preflight 테스트 존재 여부
- 신규 prefix의 표준 method 미매핑 405 `Allow` 유지와 `RequestRejectedException` 400/i18n/`ApiResponse.error`/허용 Origin CORS
header, 미허용 Origin body 계약 없는 403 테스트 존재 여부
- legacy/public firewall 동작 불변 및 `setUnsafeAllowAnyHttpMethod(true)` 미사용 확인 여부
- core controller security/error 계약의 production `@SpringBootTest` full-context 실행과 Redis token fixture cleanup 확인 여부
- target resolver 조회 직후 `creatorMember` 초기화와 fetch join 제거 시 실패하는 non-vacuous 회귀 테스트 존재 여부
- `HttpMessageNotReadableException`, `MethodArgumentNotValidException`, `MissingServletRequestPartException`의 exact exception
type과 KO/EN/JA 400 envelope 직접 검증 여부
- target/ownership 실패 시 no-side-effect 테스트 존재 여부
- character/content/series/community/FanTalk slice별 targeted test 통과 여부
- signed URL TTL 계산식·edge case parity 및 private path 비노출 테스트 통과 여부
- 기존 legacy/public endpoint의 성공·오류 status/body/message 회귀 테스트 통과 여부
---
## 11. Acceptance Criteria
- JWT `ROLE_ADMIN`과 현재 DB `Member.role == ADMIN`을 모두 만족하는 요청만 유효한 AI character 대상으로 신규 endpoint를
호출할 수 있다.
- 비로그인 또는 잘못된 JWT 요청은 401이고, JWT 비ADMIN + DB ADMIN과 JWT ADMIN + DB 비ADMIN stale claim은 모두 403이다.
- 신규 prefix의 API application/controller/security filter 오류는 정확한 비2xx status, `ApiResponse.error`,
`Accept-Language`에 따른 KO/EN/JA message를 반환한다. Spring CORS 계층의 정책 거부 403 body는 이 envelope 계약의 예외다.
- 지원하지 않는 HTTP method는 405와 `Allow` header, 응답 media type은 406, 요청 media type은 415와 `Accept` header를
반환하고, `MissingPathVariableException`은 500 `common.error.unknown`을 반환한다.
- 표준 HTTP method가 MVC에 도달한 뒤 mapping이 없을 때는 기존 405와 `Allow` header를 유지한다. 신규 prefix의 비표준 HTTP
method 또는 위험 URL이 `StrictHttpFirewall`에서 `RequestRejectedException`으로 거부되면 허용된 캐릭터 관리자 Origin에는
CORS header와 현지화된 400 `common.error.invalid_request` `ApiResponse.error`를, 미허용 Origin에는 body 계약 없는 403을
반환한다. legacy/public firewall 동작은 변하지 않고 `setUnsafeAllowAnyHttpMethod(true)`는 사용하지 않는다.
- 신규 prefix는 캐릭터 관리자 Origin만 허용하고, `/admin/member/login`, `/member/logout`는 기존 전역 Origin과 캐릭터 관리자
Origin의 합집합을 허용한다. 실제 mapped endpoint와 공유 인증 경로의 CORS 허용·거부가 테스트로 고정된다.
- core controller security/error 계약은 production `@SpringBootTest` full context에서 검증하고 Redis token fixture를 테스트 후
정리해 다음 테스트에 남기지 않는다.
- target resolver의 repository 조회 결과는 반환 직후 `creatorMember`가 초기화되어 있어야 하며, fetch join 제거 시 실패하는
회귀 테스트로 고정한다.
- malformed JSON, handler에 전달된 `MethodArgumentNotValidException`, multipart 필수 part 누락은 각각 정확한 MVC exception
type과 현지화된 400 `ApiResponse.error` 계약을 만족한다.
- character 미존재, creatorMember 미존재, role 불일치, memberKind 불일치 요청은 4xx이며 아무 side effect도 남기지 않는다.
- 다른 character 소유 resource ID를 사용한 조회/수정/삭제/연결/답변은 4xx로 거부된다.
- 캐릭터 생성/수정/비활성화는 레거시 관리자 behavior parity를 유지한다.
- 콘텐츠 생성/수정/soft delete와 signed URL 응답은 기존 creator/admin behavior parity를 유지한다.
- 시리즈 CRUD, 콘텐츠 연결/해제/순서 변경은 target character 소유 범위를 벗어나지 않는다.
- 커뮤니티 생성/수정/고정/해제/soft delete는 target creatorMember 소유 게시글에만 적용된다.
- FanTalk 답변은 target AI character 자신의 활성 root FanTalk에만 저장된다.
- 기존 legacy/public endpoint 테스트가 통과하고, 두 공유 인증 경로의 CORS 허용 Origin 확장 외 성공·오류
status/body/message를 포함한 request/response contract가 변경되지 않는다.
- 신규 dependency, 신규 DDL, 관련 없는 리팩터링이 없다.
---
## 12. Open Questions
- 없음.