Files
sodalive-android/docs/20260914_크리에이터의_리스너_DM_시작/prd.md
T

282 lines
22 KiB
Markdown

# 크리에이터의 리스너 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. 핵심 사용자 흐름
1. 크리에이터가 메인 대화 탭에 진입한다.
2. 우측 하단 `+` 버튼을 터치한다.
3. 신규 수신자 선택 화면이 열리고, 최초 상태에서는 팔로우 리스트를 보여준다.
4. 검색어를 입력하면 2글자 이상이며 debounce가 끝난 뒤 `GET /member/search`를 호출한다.
5. 검색 결과가 없으면 화면 가운데에 `사용자가 없어요.\n다른 이름으로 다시 검색해 주세요.`를 표시한다.
6. 사용자 리스트에서 사용자를 터치하면 확인 팝업을 표시한다.
7. `취소`를 터치하면 팝업만 닫고 API를 호출하지 않는다.
8. `보내기`를 터치하면 기존 크리에이터 채널 DM 생성과 같은 API를 호출하고 DM 방으로 이동한다.
## 7. 정보 구조와 라우팅
```text
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 | 확정 | 메인 대화 탭 우측 하단에 `ic_new_talk_add` 아이콘을 사용하는 `+` 버튼을 추가한다. | 버튼은 크리에이터 역할 사용자에게만 보이고, 비크리에이터에게는 보이지 않는다. | `P1-T1`, `P2-T3`, `P6-T1` |
### 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` |
| DM-022 | 확정 | 수신자 선택 화면의 팔로워 목록이 0명이면 `팔로워` 섹션 문구를 표시하지 않는다. | 팔로워 모드라도 표시할 수신자가 0명이면 `팔로워` 라벨이 숨겨진다. | `P5-T1` |
| DM-023 | 확정 | `검색결과 0`과 `팔로워` 문구 크기를 통일하고, 검색결과 숫자만 `gray/500`으로 한다. | 두 라벨은 같은 typography style을 사용하고, 검색결과 라벨의 숫자 범위에만 `@color/gray_500` span을 적용한다. | `P5-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: 기존 팔로우 목록 페이지 정책을 확인 후 재사용한다.
- 팔로우 목록 count: 팔로워가 0명이면 `팔로워` 섹션 문구를 숨긴다.
- 검색 결과 empty: `DM-010` 문구를 중앙에 표시한다.
- 검색 결과 count: 검색 결과가 0건이어도 Figma `2372:23506` 위치에 `검색결과 0`을 표시한다.
- 검색 결과 count style: `팔로워` 라벨과 같은 문구 크기를 사용하고 숫자만 `gray/500` 색상으로 표시한다.
- 검색 오류: 기존 검색/목록 오류 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/search` API가 반환한 사용자는 앱에서 추가 필터 없이 모두 선택 가능하게 둔다.
- 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 기능 수용 기준
- [x] 크리에이터에게만 메인 대화 탭 `+` 버튼이 보인다. (`DM-001`, `DM-002`)
- [x] 신규 화면 최초 진입 시 기존 팔로우 리스트 API를 호출한다. (`DM-004`)
- [x] 검색은 2글자 이상과 debounce 완료 후에만 `/member/search?nickname=`을 호출한다. (`DM-006`~`DM-009`)
- [x] 검색 결과가 없으면 지정 empty 문구를 중앙에 표시한다. (`DM-010`)
- [x] 사용자 선택 후 팝업에서 `보내기`를 터치하면 `recipientId`로 기존 DM 생성 API를 호출하고, 기존 크리에이터 채널 DM은 `creatorId`로 유지된다. (`DM-013`~`DM-019`)
- [x] DM 방에서 뒤로 돌아오면 대화 탭의 기존 목록이 비워지고 현재 filter의 첫 페이지가 다시 표시된다. (`DM-020`)
- [x] 수신자 선택 화면 상단이 Figma 검색 toolbar 구조와 일치하고, 검색 결과가 0건이어도 `검색결과 0`이 표시된다. (`DM-021`)
- [x] 팔로워가 0명이면 수신자 선택 화면의 `팔로워` 라벨이 표시되지 않는다. (`DM-022`)
- [x] `검색결과 0`과 `팔로워` 라벨의 문구 크기가 같고 검색결과 숫자만 `gray/500` 색상이다. (`DM-023`)
### 13.2 추적성 완료 기준
- [x] 모든 확정 요구사항이 `plan-task.md`의 Phase/Task에 연결된다.
- [x] 서버 제공 계약 `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 대조 |
| `DM-022` | 5 | `P5-T1` | 팔로워 라벨 표시 조건 source test | 없음 |
| `DM-023` | 5 | `P5-T1` | 수신자 layout typography와 숫자 color span source test | 없음 |
## 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` 상태를 갱신하고 사용자에게 확인한다.