# PRD: AI 캐릭터 크리에이터 기능 최소 연결 ## 1. Overview `ChatCharacter`가 기존 크리에이터 기능을 최소 변경으로 사용할 수 있도록, 모든 `ChatCharacter`를 `Member(role = CREATOR)`와 1:1로 연결하고 실제 사람 Member와 AI 캐릭터용 Member를 `memberKind`로 구분한다. --- ## 2. Problem - 현재 `ChatCharacter`는 AI 대화 주체로만 동작하고, 라이브/콘텐츠/커뮤니티/채널 후원/정산/알림 등 크리에이터 기능의 주체가 될 수 없다. - 기존 크리에이터 기능은 대부분 `Member(role = CREATOR)`와 `member.id` 기반 `creatorId`를 전제로 구현되어 있다. - `ChatCharacter`를 독립 소유자로 직접 도입하면 콘텐츠, 라이브, 후원, 정산, 랭킹, 알림, 차단, 팔로우 등 넓은 범위의 소유자 모델 변경이 필요하다. - 이번 변경은 기존 `Member` 기반 크리에이터 기능을 유지하면서, AI 캐릭터가 크리에이터 기능을 사용할 수 있는 최소 연결 구조가 필요하다. --- ## 3. Goals - `Member`에 `MemberKind`를 추가해 실제 사람 Member와 AI 캐릭터용 Member를 구분한다. - 모든 기존 `ChatCharacter`에 대응되는 `Member(role = CREATOR, memberKind = AI_CHARACTER)`를 생성하고 1:1로 연결할 수 있는 마이그레이션 SQL을 준비한다. - 신규/기존 `ChatCharacter`는 크리에이터 기능 주체인 `creatorMember`를 가진다. - `memberKind = AI_CHARACTER`인 Member는 로그인할 수 없도록 차단한다. - `memberKind = AI_CHARACTER`인 Member는 유저-크리에이터 DM 생성 대상이 될 수 없도록 차단한다. - `memberKind = AI_CHARACTER`인 Member는 로그인과 DM을 제외하고 `Member(role = CREATOR)`가 사용할 수 있는 기존 크리에이터 기능을 사용할 수 있어야 한다. - AI 캐릭터용 Member의 표시 정보는 연결된 `ChatCharacter`의 이름, 프로필 이미지, 소개를 스냅샷으로 복사해 기존 Member 기반 화면과 쿼리를 재사용한다. - 기존 사람 크리에이터의 콘텐츠/라이브/커뮤니티/채널 후원/정산/알림 동작은 유지한다. --- ## 4. Non-Goals - 이번 범위에서 `ChatCharacter`를 `Member`와 동급의 별도 소유자 타입으로 만들지 않는다. - 이번 범위에서 `creator_identity` 같은 공통 크리에이터 소유자 테이블을 도입하지 않는다. - 이번 범위에서 공개 API의 기존 `creatorId = member.id` 의미를 변경하지 않는다. - 이번 범위에서 크리에이터 검색 결과 카테고리 개편은 구현하지 않는다. - 이번 범위에서 `Member:ChatCharacter = 1:N` 관계를 허용하지 않는다. - 이번 범위에서 AI 캐릭터용 Member의 직접 로그인, 직접 크리에이터 관리자 접속, 직접 DM 기능은 허용하지 않는다. - 이번 범위에서 AI 캐릭터용 콘텐츠/라이브/커뮤니티 대리 생성 API를 새로 설계하지 않는다. - 이번 범위에서 기존 정산 산식, 정산 비율, 랭킹 점수 산식은 변경하지 않는다. - 이번 범위에서 AI 캐릭터용 Member를 정산 관리자 화면에서 사람 크리에이터와 별도 목록 또는 별도 필터로 분리하지 않는다. --- ## 5. Target Users - 일반 사용자: AI 캐릭터와 AI 대화를 하고, AI 캐릭터 채널에 후원하거나 AI 캐릭터 콘텐츠를 소비하는 회원 - 사람 크리에이터: 필요 시 자신의 `Member`에 연결된 `ChatCharacter`를 통해 AI 대화 기능을 제공하는 크리에이터 - 운영/정산 담당자: AI 캐릭터용 Member를 기존 크리에이터 정산 흐름에서 식별하고 처리해야 하는 담당자 --- ## 6. User Stories - 사용자는 모든 활성 `ChatCharacter`와 AI 대화를 시작하고 싶다. - 사용자는 AI 캐릭터 채널에도 기존 크리에이터 채널처럼 채널 후원을 하고 싶다. - 사용자는 AI 캐릭터가 업로드한 콘텐츠를 기존 콘텐츠와 같은 방식으로 보고 싶다. - 시스템은 AI 캐릭터용 Member가 실제 사람 계정처럼 로그인하거나 DM 대상이 되는 것을 막고 싶다. - 운영자는 사람 크리에이터와 AI 캐릭터용 크리에이터 Member를 데이터에서 명확히 구분하고 싶다. --- ## 7. Core Features ### Feature A. `MemberKind` 도입 #### Requirements - `Member`에 `memberKind` 필드를 추가한다. - `MemberKind` 값은 최소 다음 2개를 가진다. - `HUMAN`: 실제 사람 Member - `AI_CHARACTER`: AI 캐릭터 기능을 위해 생성된 내부 크리에이터 Member - `memberKind`는 `NOT NULL`이며 기본값은 `HUMAN`이다. - 기존 모든 Member 데이터는 DDL 기본값에 의해 `memberKind = HUMAN`이 된다. - 일반 회원가입, 관리자, 에이전트, 콘텐츠 관리자 등 실제 사람 계정은 `memberKind = HUMAN`을 사용한다. - `memberKind = AI_CHARACTER`인 Member도 `role = CREATOR`를 가진다. - 크리에이터 기능 가능 여부는 기존처럼 기본적으로 `role = CREATOR`를 기준으로 유지한다. - 사람 크리에이터 전용 기능 가능 여부는 `role = CREATOR`와 `memberKind = HUMAN`을 함께 기준으로 판단한다. - `memberKind = AI_CHARACTER`인 Member는 로그인과 DM을 제외한 팔로우, 채널 후원, 콘텐츠, 커뮤니티, 라이브, 정산, 알림 등 기존 CREATOR 기능의 대상이 될 수 있다. #### Edge Cases - `memberKind = HUMAN`만으로 사람 크리에이터 여부를 판단하면 안 되며, 반드시 `role = CREATOR` 조건을 함께 확인해야 한다. - `memberKind = AI_CHARACTER`인 Member는 반드시 `role = CREATOR`여야 한다. --- ### Feature B. `ChatCharacter`와 `Member` 1:1 연결 #### Requirements - `ChatCharacter`가 관계의 주인이며 `creatorMember`를 가진다. - 관계는 초기에는 1:1로 제한한다. - DB에는 `chat_character.creator_member_id`를 추가한다. - `chat_character.creator_member_id`는 `member.id`를 참조한다. - `chat_character.creator_member_id`에는 unique 제약을 둔다. - `ChatCharacter.creatorMember.role`은 반드시 `CREATOR`여야 한다. - 기존 모든 `ChatCharacter`는 마이그레이션 후 `creatorMember`가 있어야 한다. - 기존 `ChatCharacter` 중 실제 사람 크리에이터와 연결해야 하는 데이터는 이번 마이그레이션 대상에 없다고 본다. - 기존 모든 `ChatCharacter`는 새 `Member(role = CREATOR, memberKind = AI_CHARACTER)`를 생성해 연결한다. #### Edge Cases - 이미 연결된 `ChatCharacter`에 중복 `creatorMember`가 배정되면 안 된다. - 하나의 `Member`에 여러 `ChatCharacter`가 연결되면 안 된다. - 비활성 `ChatCharacter`도 기존 데이터 정합성을 위해 `creatorMember` 연결 대상에 포함한다. --- ### Feature C. 기존 `ChatCharacter`용 Member 생성 마이그레이션 #### Requirements - 운영 DB 반영용 MySQL 기준 DDL과 backfill SQL을 작성한다. - 마이그레이션 SQL은 기존 `ChatCharacter`별로 AI 캐릭터용 `Member`를 생성할 수 있어야 한다. - 생성되는 AI 캐릭터용 Member는 다음 정책을 따른다. - `role = CREATOR` - `memberKind = AI_CHARACTER` - `email = null` - `password = ""` - `nickname`은 기본적으로 `ChatCharacter.name` 기준 - `profileImage`는 기본적으로 `ChatCharacter.imagePath` 기준 - `introduce`는 기본적으로 `ChatCharacter.description` 기준 - AI 캐릭터용 Member의 `nickname`, `profileImage`, `introduce`는 기존 콘텐츠/라이브/커뮤니티/후원/정산/알림/팔로우/AGENT 소속 화면에서 별도 `ChatCharacter` JOIN 없이 표시하기 위한 스냅샷이다. - backfill 후 `chat_character.creator_member_id`가 없는 row가 0건인지 검증하는 SQL을 포함한다. - 검증 완료 후 `chat_character.creator_member_id`를 `NOT NULL`로 전환할 수 있어야 한다. #### Edge Cases - `ChatCharacter.name`이 중복되더라도 Member 생성이 가능해야 한다. - AI 캐릭터용 Member의 로그인 차단은 `email/password` 값이 아니라 `memberKind = AI_CHARACTER` 정책으로 보장해야 한다. - 기존 `ChatCharacter`의 사람 크리에이터 수동 매핑은 이번 범위에서 제공하지 않는다. --- ### Feature D. AI 캐릭터 표시 정보 동기화 #### Requirements - AI 캐릭터용 Member의 표시 정보는 연결된 `ChatCharacter` 값을 기준으로 유지한다. - `ChatCharacter.name`은 `Member.nickname`에 동기화한다. - `ChatCharacter.imagePath`는 `Member.profileImage`에 동기화한다. - `ChatCharacter.description`은 `Member.introduce`에 동기화한다. - `ChatCharacter` 생성 시 AI 캐릭터용 Member를 함께 생성하는 경우 같은 transaction 안에서 표시 정보를 복사한다. - `ChatCharacter` 수정 시 연결된 AI 캐릭터용 Member의 표시 정보도 같은 transaction 안에서 갱신한다. - `memberKind = AI_CHARACTER`인 Member의 표시 정보는 직접 수정 API가 아니라 `ChatCharacter` 생성/수정 흐름을 기준으로 관리한다. #### Edge Cases - 동기화 대상 `creatorMember`가 없으면 저장을 실패시켜 데이터 불일치를 막아야 한다. - 연결된 `creatorMember.memberKind != AI_CHARACTER`인 경우, 사람 크리에이터의 프로필을 덮어쓰지 않도록 동기화 대상에서 제외하거나 별도 정책을 명확히 적용해야 한다. - 기존 Member 기반 화면은 AI 캐릭터 표시 정보를 조회할 때 별도 `ChatCharacter` JOIN을 추가하지 않는다. --- ### Feature E. AI 캐릭터용 Member 로그인 차단 #### Requirements - `memberKind = AI_CHARACTER`인 Member는 모든 일반 로그인 흐름에서 인증 성공 상태가 되면 안 된다. - 크리에이터 관리자 로그인 흐름에서도 `memberKind = AI_CHARACTER`인 Member는 로그인할 수 없어야 한다. - 소셜 로그인 또는 토큰 재발급 흐름에서 AI 캐릭터용 Member가 세션/토큰을 얻을 수 있는 경로가 있으면 차단한다. - 차단 시 기존 인증 실패 응답 패턴을 우선 재사용한다. - AI 캐릭터용 Member는 로그인에 사용하지 않으므로 `email`은 `null`을 허용하고, `password`는 기존 소셜 회원 생성 패턴과 같이 빈 문자열을 사용할 수 있다. - 로그인 차단은 `email/password` 값이 아니라 `memberKind = AI_CHARACTER` 정책으로 보장한다. #### Edge Cases - 기존 토큰을 이미 가진 AI 캐릭터용 Member가 있을 수 없도록 마이그레이션 시점과 배포 순서를 점검한다. - 후속 범위에서 관리자/콘텐츠 관리자가 AI 캐릭터용 콘텐츠를 등록하더라도, AI 캐릭터용 Member 자체가 로그인하는 것은 허용하지 않는다. --- ### Feature F. AI 캐릭터용 Member DM 차단 #### Requirements - 유저-크리에이터 DM 생성 대상이 `memberKind = AI_CHARACTER`이면 DM 방을 생성하지 않는다. - 기존 사람 크리에이터는 `ChatCharacter` 연결 여부와 무관하게 DM이 가능해야 한다. - DM 차단 기준은 `ChatCharacter` 연결 여부가 아니라 `Member.memberKind`이다. #### Edge Cases - `memberKind = HUMAN`인 사람 크리에이터가 `ChatCharacter`를 가진 경우에도 DM은 가능해야 한다. - `memberKind = AI_CHARACTER`인 Member가 `role = CREATOR`이더라도 DM은 불가능해야 한다. --- ## 8. Technical Constraints - Kotlin, Spring Boot 2.7.14, Java 17, Gradle Wrapper 구조를 유지한다. - 기존 공개 API의 `creatorId` 의미는 이번 범위에서 변경하지 않는다. - 기존 콘텐츠/라이브/커뮤니티/후원/정산 테이블의 소유자 컬럼은 `Member` 기준을 유지한다. - `ChatCharacter`와 `Member` 관계는 초기에는 `ChatCharacter` 단방향 `OneToOne`으로 구현한다. - 운영 DB 반영용 DDL은 MySQL 기준으로 작성한다. - DDL 컬럼에는 가능한 경우 `COMMENT`를 추가한다. - `memberKind` 기반 정책 판단은 중복 분기를 줄이기 위해 정책 함수 또는 명확한 서비스 검증으로 모은다. - 검색 결과 카테고리 개편은 이번 구현에서 제외하되, 향후 `memberKind`를 활용할 수 있도록 데이터 모델만 준비한다. --- ## 9. Data Migration Requirements - Phase 1 DDL - `member.member_kind`를 `NOT NULL DEFAULT 'HUMAN'`으로 추가 - `chat_character.creator_member_id` nullable 추가 - `chat_character.creator_member_id` FK 및 unique index 추가 - Phase 2 backfill - 기존 모든 `ChatCharacter`별로 AI 캐릭터용 `Member(role = CREATOR, memberKind = AI_CHARACTER)`를 생성한다. - 생성된 Member를 `chat_character.creator_member_id`에 연결한다. - Phase 3 검증 및 제약 강화 - `member_kind = 'AI_CHARACTER' and role <> 'CREATOR'` row가 0건인지 확인한다. - `chat_character.creator_member_id is null` row가 0건인지 확인한다. - `chat_character.creator_member_id`를 `NOT NULL`로 변경한다. --- ## 10. Metrics - 기존 `ChatCharacter` 중 `creator_member_id` 누락 0건 - `memberKind = AI_CHARACTER`이면서 `role != CREATOR`인 Member 0건 - `memberKind = AI_CHARACTER` Member 로그인 성공 0건 - `memberKind = AI_CHARACTER` Member 대상 DM 생성 성공 0건 - 기존 사람 크리에이터의 DM, 콘텐츠 등록, 채널 후원 흐름 회귀 실패 0건 --- ## 11. Open Questions - AI 캐릭터용 콘텐츠/라이브/커뮤니티 등록 운영 흐름은 이번 범위에서 구현하지 않으므로, 후속 범위에서 `chatCharacterId` 기반 대리 생성 API 정책을 별도로 정해야 한다.