21 KiB
21 KiB
크리에이터의 리스너 DM 시작 PRD
문서 정보
| 항목 | 내용 |
|---|---|
| 문서 상태 | 구현 완료 |
| 작성일 | 2026-09-14 |
| 최종 수정일 | 2026-09-14 |
| 대상 제품 | 크리에이터의 리스너 DM 시작 |
| 작성자·결정권자 | 제품 담당자, Android 담당자 |
| 관련 API Contract | 서버 CreateUserCreatorChatRoomRequest(recipientId?, creatorId?) 계약 제공됨 |
| 관련 구현 계획 | docs/20260914_크리에이터의_리스너_DM_시작/plan-task.md |
| 관련 review | docs/20260914_크리에이터의_리스너_DM_시작/reviews/phase4-recipient-picker-review.md |
요구사항 상태
| 상태 | 의미 | 구현 처리 |
|---|---|---|
| 확정 | 제품·기술 결정이 완료되어 구현 기준으로 사용 | plan-task.md의 Task와 완료 증거로 추적 |
| 미결 | 제품·UX·운영 결정이 더 필요함 | 구현 전 확인하고 Decision Log에 누적 |
| 외부 의존 | 서버 계약·권한·배포 조건 확인이 필요함 | 담당 주체·영향·재개 조건 기록 |
| 제외 | 현재 릴리스에서 구현하지 않음 | 제외 이유와 후속 조건 기록 |
1. Overview
현재 리스너는 크리에이터 채널에서 크리에이터에게 바로 DM을 보낼 수 있지만, 크리에이터가 리스너에게 먼저 DM을 시작하는 진입점은 없다. 이 기능은 크리에이터가 메인 대화 탭에서 리스너를 찾고, 확인 팝업을 거쳐 기존 DM 방 생성 흐름으로 대화를 시작할 수 있게 한다.
현재 요구사항은 Phase 4까지 구현과 회귀 검증이 완료됐다. 이후 변경은 이 문서와 plan-task.md의 추가 Task로 추적한다.
2. Problem Statement
- 크리에이터가 리스너에게 먼저 연락해야 하는 상황에서도 현재 앱에는 직접 DM 시작 경로가 없다.
- 기존 DM 생성 API와 대화방 이동 흐름은 있으나, 메인 대화 탭에서 수신자를 선택하는 화면과 검색 흐름이 없다.
- 검색 호출 조건과 빈 결과 문구, 확인 팝업 문구가 다국어 리소스로 정리되어야 한다.
문제를 해결했다는 판단은 크리에이터가 메인 대화 탭의 + 버튼에서 리스너를 선택하고, 보내기로 기존 DM 방 생성 API를 호출한 뒤 DM 방으로 이동하는 흐름이 검증되는 것으로 한다.
3. Goals
3.1 제품 목표
- 크리에이터가 리스너에게 먼저 DM을 시작할 수 있다.
- 기존 크리에이터 채널의 DM 생성 API와 방 이동 흐름을 재사용한다.
- 검색 API 호출은 입력 2글자 이상과 debounce 조건을 만족할 때만 발생한다.
3.2 UX 목표
- 메인 대화 탭 우측 하단에 홈 화면과 유사한
+버튼을 제공한다. - 신규 수신자 선택 화면은 Figma
2372:23496의 검색바, 팔로우 섹션, 사용자 리스트 구조를 따른다. - 사용자 선택 시 Figma
2372:23521의 확인 팝업 구조를 따르고, v2 내부 공통 다이얼로그를 재사용한다. - 사용자에게 보이는 모든 신규 문구는 다국어 리소스로 관리한다.
4. Non-Goals
- 신규 백엔드 endpoint를 추가하지 않는다.
- DM 메시지 송수신 화면 자체의 UI, 소켓, 음성 메시지 전송 정책은 변경하지 않는다.
- 팔로우/검색 결과의 프로필 상세, 차단, 팔로우 토글 정책은 이번 범위에서 새로 정의하지 않는다.
- 레거시 파일 변경이 필요하면 구현 전에 별도 확인한다.
5. Target Users and Permissions
5.1 사용자
| 사용자 | 목표 | 주요 작업 | 사용 환경 |
|---|---|---|---|
| 크리에이터 | 리스너에게 먼저 DM을 시작 | 대화 탭 진입, 수신자 검색/선택, DM 방 생성 | Android 앱 |
| 리스너 | 크리에이터로부터 시작된 DM을 수신 | 기존 DM 방에서 대화 | Android 앱 |
5.2 권한
- 인증 주체: 로그인한 사용자.
- 허용 역할: 크리에이터 역할 사용자.
- UI 노출 조건: 메인 대화 탭의
+버튼과 신규 수신자 선택 화면은 크리에이터에게만 노출한다. - 서버 권한: 기존 DM 생성 API가
recipientId와creatorId를 모두 수용하는 호환 계약으로 제공됐다.
6. 핵심 사용자 흐름
- 크리에이터가 메인 대화 탭에 진입한다.
- 우측 하단
+버튼을 터치한다. - 신규 수신자 선택 화면이 열리고, 최초 상태에서는 팔로우 리스트를 보여준다.
- 검색어를 입력하면 2글자 이상이며 debounce가 끝난 뒤
GET /member/search를 호출한다. - 검색 결과가 없으면 화면 가운데에
사용자가 없어요.\n다른 이름으로 다시 검색해 주세요.를 표시한다. - 사용자 리스트에서 사용자를 터치하면 확인 팝업을 표시한다.
취소를 터치하면 팝업만 닫고 API를 호출하지 않는다.보내기를 터치하면 기존 크리에이터 채널 DM 생성과 같은 API를 호출하고 DM 방으로 이동한다.
7. 정보 구조와 라우팅
MainV2Activity
ChatMainFragment
+ button (creator only)
DmRecipientPickerActivity (v2 package)
Follow list state
Search result state
Empty state
Confirm dialog
DmChatRoomActivity
- 신규 화면은
kr.co.vividnext.sodalive.v2.main.chat.recipient.DmRecipientPickerActivity로 구현한다. - 신규 화면과 연결 하위 코드는
kr.co.vividnext.sodalive.v2패키지 하위에 둔다. - 기존 채팅 방 이동은
DmChatRoomActivity의 기존 진입 방식을 재사용한다.
8. 기능 요구사항
8.1 진입점과 권한
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|---|---|---|---|---|
| DM-001 | 확정 | 크리에이터는 메인 대화 탭에서 리스너에게 먼저 DM을 시작할 수 있다. | 크리에이터 계정에서 신규 수신자 선택 화면에 진입할 수 있다. | P2-T3 |
| DM-002 | 확정 | 메인 대화 탭 우측 하단에 홈과 유사한 + 버튼을 추가한다. |
버튼은 크리에이터 역할 사용자에게만 보이고, 비크리에이터에게는 보이지 않는다. | P1-T1, P2-T3 |
8.2 수신자 선택 화면
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|---|---|---|---|---|
| DM-003 | 확정 | 신규 화면은 Figma 2372:23496을 기준으로 검색바, 팔로우 섹션, 사용자 목록을 제공한다. |
검색바 placeholder, 팔로우 섹션, 리스트 구조가 문서화되고 구현 전 기존 UI 패턴과 연결된다. | P1-T1, P2-T3 |
| DM-004 | 확정 | 화면 최초 진입 시 팔로우 리스트를 보여준다. | 기존 팔로우 리스트 페이지와 동일한 API를 호출한다. | P2-T2 |
| DM-005 | 확정 | 검색어가 0~1자로 줄어든 경우 검색 모드를 해제하고 팔로우 리스트로 복귀한다. | 0~1글자 입력에서는 /member/search 요청이 발생하지 않고 팔로우 리스트 상태가 표시된다. |
P2-T2 |
8.3 검색
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|---|---|---|---|---|
| DM-006 | 확정 | 검색은 2글자 이상 입력했을 때 유저 검색 API를 호출한다. | 0~1글자 입력에서는 /member/search 요청이 발생하지 않는다. |
P2-T2 |
| DM-007 | 확정 | 검색 API는 GET /member/search를 사용하고 파라미터는 nickname이다. |
요청 query가 nickname=입력값으로 전달된다. |
P2-T2 |
| DM-008 | 확정 | debounce를 적용해 글자를 치는 중에는 API를 호출하지 않는다. | 연속 입력 중 요청 0회, 입력 중지 후 최종 검색어 요청 1회가 검증된다. | P2-T2 |
| DM-009 | 확정 | debounce 대기 시간은 500ms로 한다. | 입력 중지 후 500ms가 지난 최종 검색어로만 /member/search를 1회 호출한다. |
P2-T2 |
8.4 Empty와 다국어
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|---|---|---|---|---|
| DM-010 | 확정 | 검색 결과가 없을 때 가운데 안내 문구를 표시한다. | 사용자가 없어요.\n다른 이름으로 다시 검색해 주세요.가 중앙 empty state로 표시된다. |
P2-T2, P2-T3 |
| DM-011 | 확정 | 모든 신규 문구는 다국어 리소스로 처리한다. | 한국어, 일본어 등 기존 지원 언어 리소스에 신규 key가 추가된다. | P2-T3 |
| DM-012 | 확정 | 검색바 placeholder는 팬 이름을 입력하세요, 섹션 제목은 팔로워로 한다. |
Figma 기준 한국어 문구를 기존 지원 언어 리소스에 번역해 추가한다. | P2-T3 |
8.5 확인 팝업과 DM 생성
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|---|---|---|---|---|
| DM-013 | 확정 | 사용자 터치 시 Figma 2372:23521 기준 확인 팝업을 표시한다. |
제목, 본문, 버튼이 지정 문구로 노출된다. | P2-T3 |
| DM-014 | 확정 | 팝업 다이얼로그는 v2 내부에서 사용하는 공통 다이얼로그를 재사용하고 문구만 변경한다. | 신규 다이얼로그 구현 없이 기존 공통 다이얼로그 contract를 소비한다. | P1-T1, P2-T3 |
| DM-015 | 확정 | 팝업 제목은 메시지 보내기다. |
신규 string resource로 관리된다. | P2-T3 |
| DM-016 | 확정 | 팝업 본문은 {{사용자이름}}에게 메시지를 보낼까요?다. |
사용자 이름을 포맷 인자로 전달한다. | P2-T3 |
| DM-017 | 확정 | 팝업 버튼은 취소, 보내기다. |
취소는 요청 없이 닫고, 보내기는 DM 생성 요청을 1회 호출한다. |
P2-T3 |
| DM-018 | 확정 | 보내기는 크리에이터 채널에서 DM 보내기를 터치했을 때 호출되는 API와 동일한 API를 호출한다. |
기존 DM 생성 endpoint와 방 이동 흐름을 재사용한다. | P2-T1, P2-T3 |
| DM-019 | 확정 | DM 생성 요청 DTO는 recipientId와 creatorId를 모두 표현할 수 있어야 한다. |
신규 크리에이터→리스너 DM은 recipientId만 보내고, 기존 크리에이터 채널 DM은 creatorId를 유지한다. |
P2-T1 |
| DM-020 | 확정 | DM 방에서 뒤로 돌아와 대화 탭이 다시 보이면 대화방 목록을 첫 페이지부터 갱신한다. | 기존 표시 목록을 비운 뒤 현재 선택 filter로 첫 페이지를 다시 요청하고, 요청 cursor는 null이다. |
P3-T1 |
| DM-021 | 확정 | 수신자 선택 화면은 Figma 2372:23496, 검색 결과 화면은 Figma 2372:23506과 동일한 상단 검색 toolbar, 섹션/검색결과 라벨, 리스트 행 구조를 사용한다. |
별도 detail_toolbar를 쓰지 않고 뒤로가기와 검색 input이 같은 54dp 검색 toolbar 안에 배치되며, 검색 모드에서는 0건도 검색결과 0을 표시한다. |
P4-T1 |
9. UI/UX Expectations
9.1 Figma 근거
| 항목 | Figma node | 확인 내용 |
|---|---|---|
| 수신자 선택 화면 | 2372:23496 |
상단 status bar, 뒤로가기, 검색바, 팔로워 섹션, 사용자 리스트 |
| 수신자 검색 결과 화면 | 2372:23506 |
뒤로가기와 검색 input이 같은 검색 toolbar, 검색결과 N sort-bar, 검색 결과 리스트 |
| 확인 팝업 | 2372:23521 |
제목 메시지 보내기, 본문 {{사용자이름}}에게 메시지를 보낼까요?, 버튼 취소/보내기 |
9.2 화면 상태
- 최초 로딩: 기존 팔로우 목록 API 로딩 정책을 따른다.
- 팔로우 목록 empty: 기존 팔로우 목록 페이지 정책을 확인 후 재사용한다.
- 검색 결과 empty:
DM-010문구를 중앙에 표시한다. - 검색 결과 count: 검색 결과가 0건이어도 Figma
2372:23506위치에검색결과 0을 표시한다. - 검색 오류: 기존 검색/목록 오류 toast 또는 empty 정책을 확인 후 최소 변경으로 맞춘다.
- 보내기 진행 중: 중복 터치를 막고 기존 로딩/비활성화 패턴을 따른다.
10. API 계약
10.1 알려진 기존 코드 근거
| 목적 | 기존 근거 | 확인된 내용 |
|---|---|---|
| 채팅 탭 | ChatMainFragment |
메인 대화 탭, 필터, 채팅방 목록 진입점 |
| DM 생성 API | DmChatApi.createDmChatRoom |
POST /api/v2/user-creator-chat/rooms/create |
| DM 요청 생성 | DmChatRepository.createOrGetRoom(token, creatorId) |
현재 기존 크리에이터 채널 DM 경로에서 CreateDmChatRoomRequest(creatorId = creatorId) 생성 |
| 기존 크리에이터 DM 이동 | ChatActionCommand.DmCreator, DmChatRoomActivity.newIntentByCreatorId |
크리에이터 채널에서 기존 DM 방 진입에 사용 |
| 유저 검색 | UserApi.searchUser, UserRepository.searchUser |
GET /member/search, query nickname |
| 검색 참고 구현 | SelectMessageRecipientViewModel.searchUser |
nickname.length > 1 조건은 있으나 debounce는 별도 확인 필요 |
| 팔로우 목록 | FollowingCreatorRepository.getFollowedCreatorAllList |
기존 팔로우 목록 API 재사용 후보 |
10.2 서버 제공 계약
| ID | 상태 | 제공 계약 | 담당 주체 | 구현 영향 | 구현 기준 |
|---|---|---|---|---|---|
| EXT-001 | 해결 | CreateUserCreatorChatRoomRequest(recipientId: Long? = null, creatorId: Long? = null) 제공. 서버는 둘 다 있으면 같은 값이어야 하며, recipientId ?: creatorId를 수신자 member ID로 사용한다. |
서버/API 담당자 | 신규 리스너 DM은 recipientId, 기존 크리에이터 채널 DM은 creatorId를 사용할 수 있다. |
클라이언트 DTO는 두 필드를 모두 nullable로 표현하고 호출 경로별로 하나만 채운다. |
11. 보안과 데이터 취급
- 인증 header는 기존 repository 정책을 재사용한다.
- token, Authorization header, 내부 사용자 식별자는 log, Toast, 문서 fixture에 직접 노출하지 않는다.
- 검색어와 사용자 이름은 화면 표시와 API 요청에만 사용하고 별도 저장하지 않는다.
/member/searchAPI가 반환한 사용자는 앱에서 추가 필터 없이 모두 선택 가능하게 둔다.- DM 방 복귀 갱신은 기존 채팅 목록 첫 페이지 API를 재사용하고 token, 내부 식별자를 log·Toast에 노출하지 않는다.
12. 성능과 품질 요구사항
- 검색 API는 2글자 이상과 500ms debounce 완료 조건을 모두 만족할 때만 호출한다.
- 연속 입력 중 중간 검색어 요청을 보내지 않는다.
- 검색어가 0~1자로 줄어들면 검색 모드를 해제하고 팔로우 리스트로 복귀한다.
- 동일 화면 내 팔로우 목록과 검색 결과 상태 전환이 명확해야 한다.
- DM 방 복귀 후 대화 탭 목록 갱신은 기존 pagination 상태를 첫 페이지 기준으로 초기화한다. 이 저장소의 v2 대화 목록은 cursor 기반이므로 page 0은
cursor = null요청을 의미한다. - 수신자 선택 화면의 Figma 정합성은 Android XML 구조/source test와 resource compile로 검증하고, View 크기·constraint 속성 자체를 unit/UI test에서 직접 검증하지 않는다.
- 신규 테스트는
app/src/test의 로직/local unit test만 계획한다. app/src/androidTest,connectedDebugAndroidTest, 기기·에뮬레이터 UI 조작, 스크린샷·시각 QA는 사용자가 별도로 요청하기 전까지 범위에서 제외한다.
13. 성공 기준
13.1 기능 수용 기준
- 크리에이터에게만 메인 대화 탭
+버튼이 보인다. (DM-001,DM-002) - 신규 화면 최초 진입 시 기존 팔로우 리스트 API를 호출한다. (
DM-004) - 검색은 2글자 이상과 debounce 완료 후에만
/member/search?nickname=을 호출한다. (DM-006~DM-009) - 검색 결과가 없으면 지정 empty 문구를 중앙에 표시한다. (
DM-010) - 사용자 선택 후 팝업에서
보내기를 터치하면recipientId로 기존 DM 생성 API를 호출하고, 기존 크리에이터 채널 DM은creatorId로 유지된다. (DM-013~DM-019) - DM 방에서 뒤로 돌아오면 대화 탭의 기존 목록이 비워지고 현재 filter의 첫 페이지가 다시 표시된다. (
DM-020) - 수신자 선택 화면 상단이 Figma 검색 toolbar 구조와 일치하고, 검색 결과가 0건이어도
검색결과 0이 표시된다. (DM-021)
13.2 추적성 완료 기준
- 모든 확정 요구사항이
plan-task.md의 Phase/Task에 연결된다. - 서버 제공 계약
EXT-001이DM-019와plan-task.md의P2-T1구현 기준에 반영돼 있다.
14. Resolved Questions
| ID | 상태 | 결정 사항 | 근거 | 결정 주체 | 결정일 | 영향 Goal |
|---|---|---|---|---|---|---|
| OQ-001 | 확정 | debounce 대기 시간은 500ms로 한다. | 사용자 인터뷰 답변 | 제품/Android 담당자 | 2026-09-14 |
P2-T2 |
| OQ-002 | 확정 | 검색어가 0~1자로 줄어들면 팔로우 리스트로 복귀한다. | 사용자 인터뷰 답변 | 제품 담당자 | 2026-09-14 |
P2-T2 |
| OQ-003 | 확정 | /member/search API 결과는 앱에서 추가 필터 없이 모두 선택 가능하게 둔다. |
사용자 인터뷰 답변 | 제품/서버 담당자 | 2026-09-14 |
P2-T2, P2-T3 |
| OQ-004 | 확정 | Figma 한국어 문구 팬 이름을 입력하세요, 팔로워, 사용자가 없어요.\n다른 이름으로 다시 검색해 주세요.를 최종 문구로 한다. |
사용자 인터뷰 답변 | 제품/번역 담당자 | 2026-09-14 |
P2-T3 |
| OQ-005 | 확정 | 레거시 수정이 필요하면 기능에 필요한 최소 수정은 허용한다. | 사용자 인터뷰 답변 | 사용자/Android 담당자 | 2026-09-14 |
전체 |
15. 요구사항 추적표
| 요구사항 범위 | 계획 Phase | Goal | 자동 검증 | 수동 검증 |
|---|---|---|---|---|
DM-001~002 |
2 | P2-T3 |
역할별 진입 판단 local unit test | 코드 구현 단계에서 앱 흐름 확인 |
DM-003~005 |
1, 2 | P1-T1, P2-T2, P2-T3 |
상태 mapper/ViewModel test | 코드 구현 단계에서 화면 확인 |
DM-006~009 |
2 | P2-T2 |
검색 길이/debounce/request test | 없음 |
DM-010~012 |
2 | P2-T2, P2-T3 |
empty state/resource mapping test | 코드 구현 단계에서 문구 확인 |
DM-013~019 |
2 | P2-T1, P2-T3 |
request serialization, dialog action, navigation test | 코드 구현 단계에서 팝업 확인 |
DM-020 |
3 | P3-T1 |
Fragment source wiring test, ViewModel first-page reset regression test | 사용자가 확인한 뒤로가기 흐름 |
DM-021 |
4 | P4-T1 |
수신자 layout/source test, 검색 결과 count ViewModel test | Figma screenshot 대조 |
16. Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항·계약·Goal |
|---|---|---|---|---|---|
2026-09-14 |
DEC-001 | 확정 | 이번 세션에서는 문서만 생성하고 코드 구현은 하지 않는다. | 사용자 요청 | 전체 |
2026-09-14 |
DEC-002 | 확정 | 메인 대화 탭의 + 버튼과 신규 DM 시작 화면은 크리에이터에게만 노출한다. |
사용자 인터뷰 답변 | DM-001, DM-002, P2-T3 |
2026-09-14 |
DEC-003 | 확정 | 검색 API는 GET /member/search와 nickname 파라미터를 사용한다. |
사용자 요청과 기존 UserApi.searchUser 확인 |
DM-006~DM-008, P2-T2 |
2026-09-14 |
DEC-004 | 정정 | 서버 계약 확인 전에는 CreateDmChatRoomRequest.creatorId를 recipientId로 변경하는 방향으로 계획했다. |
사용자 요청 | DM-019, EXT-001, P2-T1 |
2026-09-14 |
DEC-005 | 확정 | 검색 debounce 대기 시간은 500ms로 한다. | 사용자 인터뷰 답변 | DM-008, DM-009, P2-T2 |
2026-09-14 |
DEC-006 | 확정 | 검색어가 0~1자로 줄어들면 팔로우 리스트로 복귀한다. | 사용자 인터뷰 답변 | DM-005, P2-T2 |
2026-09-14 |
DEC-007 | 확정 | /member/search API 결과는 앱에서 추가 필터 없이 모두 선택 가능하게 둔다. |
사용자 인터뷰 답변 | P2-T2, P2-T3 |
2026-09-14 |
DEC-008 | 확정 | Figma 한국어 문구를 최종 문구로 확정하고 기존 지원 언어 리소스에 번역을 추가한다. | 사용자 인터뷰 답변 | DM-010~DM-012, P2-T3 |
2026-09-14 |
DEC-009 | 확정 | 레거시 수정이 필요하면 기능에 필요한 최소 수정은 허용한다. | 사용자 인터뷰 답변 | 전체 |
2026-09-14 |
DEC-010 | 확정 | 서버가 recipientId와 기존 creatorId를 모두 수용하는 호환 계약을 제공했다. 신규 크리에이터→리스너 DM은 recipientId, 기존 크리에이터 채널 DM은 creatorId를 사용한다. |
서버 변경 공유 | DM-019, EXT-001, P2-T1 |
2026-09-14 |
DEC-011 | 확정 | DM 방에서 뒤로 돌아와 대화 탭이 다시 보이면 현재 filter의 첫 페이지를 재조회한다. page 0은 cursor 기반 API의 cursor = null로 처리한다. |
사용자 확인 및 추가 요청 | DM-020, P3-T1 |
2026-09-14 |
DEC-012 | 확정 | 수신자 선택 화면은 Figma 2372:23496, 검색 결과 화면은 Figma 2372:23506과 동일한 검색 toolbar와 검색결과 N 라벨을 사용한다. |
사용자 지적 및 Figma 재확인 | DM-021, P4-T1 |
17. 변경 관리
- 요구사항 변경 시 Decision Log에 변경 이유와 날짜를 기록한다.
- 관련 요구사항 상태, 수용 기준, 계획 Task를 코드 변경 전에 갱신한다.
- 기존 결정과 검증 기록은 삭제하거나 덮어쓰지 않는다.
- 서버 계약이 다시 변경되면 구현 전에
EXT-001상태를 갱신하고 사용자에게 확인한다.