크리에이터 DM 리스너 선택 PRD
문서 정보
| 항목 |
내용 |
| 문서 상태 |
검토 중 |
| 작성일 |
2026-09-14 |
| 최종 수정일 |
2026-09-14 |
| 대상 제품 |
크리에이터가 리스너에게 먼저 DM 보내기 |
| 작성자·결정권자 |
제품 결정: 요청자 / 문서 작성: Sisyphus |
| 관련 API Contract |
이 문서 §11 |
| 관련 구현 계획 |
docs/20260914_크리에이터_DM_리스너_선택/plan-task.md |
| 관련 review |
없음 |
요구사항 상태
| 상태 |
의미 |
구현 처리 |
| 확정 |
제품·기술 결정이 완료되어 구현 기준으로 사용 |
plan-task.md의 Task와 완료 증거로 추적 |
| 미결 |
제품·UX·운영 결정이 더 필요함 |
구현 전 질문 또는 API 확인 필요 |
| 제외 |
현재 릴리스에서 구현하지 않기로 결정 |
Decision Log에 기록 |
1. Overview
현재 리스너는 크리에이터 채널에서 크리에이터에게 바로 DM을 시작할 수 있지만, 크리에이터가 리스너에게 먼저 DM을 시작하는 진입점은 없다. 이 기능은 메인 대화 탭에서 크리에이터가 팔로우 리스트 또는 닉네임 검색으로 리스너를 선택하고, 확인 팝업 후 기존 유저-크리에이터 DM 방 생성 API로 DM 방에 진입하게 한다.
2. Problem Statement
현재 사용자는 다음 문제를 겪는다.
- 크리에이터가 리스너에게 선제적으로 1:1 DM을 시작할 수 없다.
- 기존 DM 생성 흐름은 크리에이터 채널에서 리스너가 크리에이터에게 보내는 방향을 기준으로 구성되어 있다.
- 크리에이터가 리스너를 찾기 위해 별도 화면이나 외부 수단을 사용해야 한다.
문제를 해결했다는 판단은 크리에이터 계정이 메인 대화 탭에서 리스너를 선택해 기존 DM 방 생성 API를 호출하고, 생성된 DM 방으로 진입하는 것으로 한다.
3. Goals
3.1 제품 목표
- 크리에이터가 메인 대화 탭에서 리스너에게 먼저 DM을 시작할 수 있다.
- 초기 화면은 기존 팔로우 전체 리스트 페이지와 동일한 API를 사용한다.
- 검색은 2글자 이상일 때만
GET /member/search를 500ms debounce 후 호출한다.
- 선택한 유저에게 메시지를 보낼지 확인하는 다이얼로그를 보여준다.
- 보내기 시 기존 유저-크리에이터 DM 방 생성 API를 재사용한다.
3.2 UX 목표
- 메인 대화 탭 우측 하단에 홈의 플로팅
+ 버튼과 같은 진입점을 제공한다.
- 새 화면은 Figma
2372:23496 기준의 검색 바, 팔로우 섹션, 유저 리스트 구조를 따른다.
- 확인 팝업은 Figma
2372:23521 기준 문구와 V2 공통 SodaV2ActionModal을 재사용한다.
- 검색 결과가 없을 때 화면 중앙에
사용자가 없어요.\n다른 이름으로 다시 검색해 주세요.를 다국어 문구로 표시한다.
4. Non-Goals
- 새 채팅 프로토콜, WebSocket 전송 방식, 메시지 본문 전송 기능은 만들지 않는다.
- 기존 채팅방 UI(
UserCreatorChatRoomView)의 메시지 표시·전송 UX를 변경하지 않는다.
- 팔로우 리스트 API의 서버 계약을 새로 정의하지 않는다. 기존 API를 재사용한다.
- 검색 API의 서버 응답 필드, 필터링 정책, 차단 유저 제외 정책을 클라이언트에서 추정하지 않는다.
- 새 공통 모달 컴포넌트를 만들지 않는다.
5. Target Users and Permissions
| 사용자 |
목표 |
주요 작업 |
사용 환경 |
| 크리에이터 |
리스너에게 먼저 DM 시작 |
대화 탭 진입, 팔로우 리스트 조회, 검색, 유저 선택, 확인 후 DM 방 진입 |
iOS 앱 |
| 리스너 |
크리에이터가 시작한 DM 수신 |
기존 DM 방에서 메시지 확인 |
iOS 앱 |
5.2 권한
- 인증 주체: 로그인된 사용자.
- 허용 역할:
MemberRole.CREATOR인 크리에이터.
- 거부 조건: 비로그인 또는 크리에이터가 아닌 사용자는 메인 대화 탭의 신규
+ 진입점을 보지 않는다.
- 리소스 소유권: 방 생성 권한과 recipient 검증은 서버의
/api/v2/user-creator-chat/rooms/create 정책을 따른다.
6. 핵심 사용자 흐름
- 크리에이터가 메인 대화 탭에 진입한다.
- 우측 하단
+ 버튼을 터치한다.
- 새 유저 선택 화면이 열린다.
- 초기 상태에서 기존 팔로우 전체 리스트 API의 결과를 보여준다.
- 검색창에 2글자 이상 입력하면 500ms 동안 추가 입력이 멈춘 뒤
GET /member/search?nickname={query}를 호출한다.
- 검색 결과가 없으면 중앙에 empty 문구를 표시한다.
- 유저를 터치하면
메시지 보내기 확인 팝업을 표시한다.
- 보내기를 터치하면 기존 DM 방 생성 API를 호출한다.
- 성공 시 반환된
roomId로 AppStep.userCreatorChatRoom(roomId:)에 진입한다.
7. 정보 구조와 라우팅
- 새 라우트 이름은 구현 시 코드 스타일에 맞춰 조정할 수 있으나, 역할은 “크리에이터가 DM을 시작할 리스너 선택 화면” 하나로 제한한다.
- 기존
AppStep.userCreatorChatCreator(creatorId:)는 리스너가 크리에이터 채널에서 DM을 시작하는 흐름으로 유지한다.
- 새 흐름은 유저 선택 후
recipientId 기반으로 방을 생성한다.
8. 기능 요구사항
8.1 진입점
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
CDM-ENTRY-001 |
확정 |
메인 대화 탭 우측 하단에 홈처럼 + 플로팅 버튼을 추가한다. |
크리에이터 계정에서만 보이고, 탭 시 새 리스너 선택 화면으로 이동한다. |
P1-T1 |
CDM-ENTRY-002 |
확정 |
비크리에이터와 비로그인 사용자는 신규 + 버튼을 보지 않는다. |
MemberRole.CREATOR가 아니면 버튼이 렌더링되지 않는다. |
P1-T1 |
8.2 리스너 선택 화면
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
CDM-LIST-001 |
확정 |
새 화면은 Figma 2372:23496 구조를 따른다. |
뒤로가기, 검색 바, 섹션 타이틀, 리스트가 검은 배경 위에 배치된다. |
P2-T1 |
CDM-LIST-002 |
확정 |
초기 진입 시 기존 팔로우 전체 리스트 페이지와 동일한 API를 호출한다. |
FollowCreatorRepository.getFollowedCreatorAllList(page:size:)와 같은 endpoint·pagination 정책을 사용한다. |
P2-T2 |
CDM-LIST-003 |
확정 |
초기 리스트 아이템 터치 시 확인 팝업을 보여준다. |
터치만으로 방 생성 API가 바로 호출되지 않는다. |
P2-T4 |
8.3 검색
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
CDM-SEARCH-001 |
확정 |
검색어가 2글자 미만이면 검색 API를 호출하지 않는다. |
0~1글자 입력 중 network 요청 0회, 화면은 초기 팔로우 리스트 기준을 유지한다. |
P2-T3 |
CDM-SEARCH-002 |
확정 |
검색어가 2글자 이상이면 500ms debounce 후 GET /member/search를 호출한다. |
연속 입력 중에는 마지막 입력 후 500ms가 지나기 전 요청하지 않는다. |
P2-T3 |
CDM-SEARCH-003 |
확정 |
검색 결과가 없을 때 중앙 empty 문구를 표시한다. |
사용자가 없어요.\n다른 이름으로 다시 검색해 주세요.가 다국어 키로 표시된다. |
P2-T3 |
8.4 확인 팝업과 방 생성
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
CDM-MODAL-001 |
확정 |
유저 터치 시 V2 내부 공통 팝업을 재사용한다. |
SodaV2ActionModal을 사용하고 새 모달 컴포넌트를 만들지 않는다. |
P2-T4 |
CDM-MODAL-002 |
확정 |
팝업 문구는 메시지 보내기, {{사용자이름}}에게 메시지를 보낼까요?, 취소, 보내기를 다국어 처리한다. |
모든 문구가 I18n 키를 통해 참조된다. |
P2-T4 |
CDM-CREATE-001 |
확정 |
보내기 시 기존 유저-크리에이터 DM 방 생성 API를 호출한다. |
POST /api/v2/user-creator-chat/rooms/create 성공 시 roomId를 받아 DM 방으로 진입한다. |
P2-T5 |
CDM-CREATE-002 |
확정 |
크리에이터가 리스너에게 보내는 흐름은 선택한 유저 ID를 recipientId로 보낸다. |
request body가 recipientId와 creatorId를 동시에 서로 다른 값으로 보내지 않는다. |
P2-T5 |
9. UI/UX Expectations
9.1 디자인 기준
- 신규 화면 Figma:
2372:23496 (chat_src_001, 402×874)
- 확인 팝업 Figma:
2372:23521 (Frame 1707482932, 340×207)
- 검색 안내 문구: Figma 기준
팬 이름을 입력하세요
- 초기 섹션 타이틀: Figma 기준
팔로워
- 리스트 아이템: 원형 프로필 이미지와 닉네임 텍스트 구조를 사용한다.
9.2 화면 상태
- loading: 초기 팔로우 리스트와 검색 요청 중 기존 프로젝트 loading 패턴을 따른다.
- empty: 검색 결과 0건일 때 중앙 안내 문구 표시.
- error: 기존 toast 또는 공통 오류 메시지 정책을 따른다.
- success: 방 생성 성공 시 별도 성공 toast 없이 채팅방으로 이동한다.
10. 다국어 문구
| 용도 |
한국어 문구 |
비고 |
| 검색 안내 문구 |
팬 이름을 입력하세요 |
Figma 기준 |
| 초기 섹션 타이틀 |
팔로워 |
Figma 기준 |
| 검색 empty 1행 |
사용자가 없어요. |
줄바꿈 포함 |
| 검색 empty 2행 |
다른 이름으로 다시 검색해 주세요. |
줄바꿈 포함 |
| 팝업 제목 |
메시지 보내기 |
Figma 기준 |
| 팝업 본문 |
{{사용자이름}}에게 메시지를 보낼까요? |
닉네임 치환 |
| 팝업 취소 |
취소 |
기존 I18n.Common.cancel 재사용 가능 |
| 팝업 보내기 |
보내기 |
새 키 또는 기존 동일 의미 키 재사용 |
11. API 계약
11.1 초기 팔로우 전체 리스트
| 항목 |
내용 |
| 기존 호출 지점 |
FollowCreatorViewModel.getFollowedCreatorAllList() |
| Repository |
FollowCreatorRepository.getFollowedCreatorAllList(page:size:) |
| Method |
GET |
| Path |
/live/recommend/following/channel/all/list |
| Query |
page, size, timezone |
| Response decode |
ApiResponse<GetCreatorFollowingAllListResponse> |
| Item model |
GetCreatorFollowingAllListItem(creatorId, nickname, profileImageUrl, isFollow, isNotify) |
11.2 유저 검색
| 항목 |
내용 |
| 기존 API |
UserApi.searchUser(nickname:) |
| Repository |
UserRepository.searchUser(nickname:) |
| Method |
GET |
| Path |
/member/search |
| Query |
nickname |
| 호출 조건 |
검색어 trim 후 2글자 이상, 500ms debounce 완료 |
11.3 DM 방 생성
| 항목 |
내용 |
| 기존 API |
UserCreatorChatApi.createRoom |
| Repository |
UserCreatorChatRepository.createRoom(...) |
| Method |
POST |
| Path |
/api/v2/user-creator-chat/rooms/create |
| 기존 iOS request |
UserCreatorCreateRoomRequest(creatorId: Int) |
| 신규 요구 request |
recipientId: Int?, creatorId: Int? 중 하나만 유효하게 전송 |
| 성공 response |
UserCreatorCreateRoomResponse(roomId: Int) |
서버 request 기준:
- 리스너가 크리에이터 채널에서 DM 시작: 기존처럼
creatorId 전송.
- 크리에이터가 리스너에게 DM 시작: 선택한 유저의 ID를
recipientId로 전송.
recipientId와 creatorId가 서로 다른 값으로 동시에 전송되는 상태는 만들지 않는다.
12. 보안과 데이터 취급
- 인증 header는 기존 Moya target의
Authorization: Bearer {token} 정책을 따른다.
- 검색어, 닉네임, member ID 외 민감정보를 로그에 남기지 않는다.
- 방 생성 실패 시 서버 message 또는
I18n.Common.commonError를 사용한다.
13. 성능과 품질 요구사항
- 검색 debounce 시간은 500ms로 고정한다.
- 2글자 미만 검색어는 network 요청을 보내지 않는다.
- 검색어가 바뀌면 이전 pending debounce 작업은 취소한다.
- pagination은 기존 팔로우 전체 리스트의
page = 1, size = 10, isLast 정책을 따른다.
- 신규 dependency를 추가하지 않는다.
14. 성공 기준
14.1 기능 수용 기준
14.2 추적성 완료 기준
15. Open Questions
| ID |
상태 |
결정 필요 사항 |
현재 권고 |
결정 주체 |
결정 기한/시점 |
영향 Goal |
| 없음 |
확정 |
인터뷰로 초기 리스트 API 기준 확인 완료 |
기존 팔로우 전체 리스트 API 사용 |
요청자 |
2026-09-14 |
P2-T2 |
16. 요구사항 추적표
| 요구사항 범위 |
API Contract |
계획 Phase |
Goal |
자동 검증 |
수동 검증 |
CDM-ENTRY-001~002 |
해당 없음 |
1 |
P1-T1 |
빌드 |
크리에이터/비크리에이터 버튼 노출 확인 |
CDM-LIST-001~003 |
§11.1 |
2 |
P2-T1, P2-T2, P2-T4 |
빌드 |
초기 목록과 팝업 확인 |
CDM-SEARCH-001~003 |
§11.2 |
2 |
P2-T3 |
빌드 |
2글자/500ms/empty 확인 |
CDM-MODAL-001~002, CDM-CREATE-001~002 |
§11.3 |
2 |
P2-T4, P2-T5 |
빌드 |
request payload와 방 진입 확인 |
17. Decision Log
| 날짜 |
ID |
상태 |
결정 |
근거 |
영향 요구사항·계약·Goal |
| 2026-09-14 |
DEC-001 |
확정 |
코드 구현 없이 PRD와 plan-task.md만 작성한다. |
사용자 직접 지시 |
전체 |
| 2026-09-14 |
DEC-002 |
확정 |
새 화면 초기 목록은 기존 팔로우 전체 리스트 페이지와 동일한 API를 사용한다. |
사용자 답변 B |
CDM-LIST-002, §11.1, P2-T2 |
| 2026-09-14 |
DEC-003 |
확정 |
크리에이터가 리스너에게 보내는 새 흐름은 recipientId를 사용하고, 기존 크리에이터 채널 흐름은 creatorId를 유지한다. |
서버 request contract |
CDM-CREATE-002, §11.3, P2-T5 |
18. 변경 관리
요구사항 변경 시 다음을 확인한다.