24 KiB
24 KiB
PRD: DM 채팅화면
1. Overview
ChatRoomActivity와 유사한 DM 채팅방 상세 화면을 제공하고, 사용자와 크리에이터 간 DM 메시지를 user-creator-chat API와 SSE 기반 실시간 이벤트로 송수신한다.
2. Problem
- 기존
ChatRoomActivity는 AI 캐릭터 채팅방 기준 화면으로, 캐릭터 타입 배지, CAN 배지, 더보기, 안내 메시지, 쿼터/유료 메시지 흐름이 포함되어 있다. - 채팅 탭의 DM item 클릭 시 이동할 DM 상세 화면이 아직 별도 범위로 구현되어 있지 않다.
- DM 채팅은 AI 채팅과 다르게 크리에이터와 사용자 간 메시지 송수신, SSE 실시간 이벤트 연결/해제, 커서 기반 과거 메시지 조회가 핵심이다.
- 화면 이탈 또는 앱 백그라운드 전환 시 실시간 연결 해제 API를 항상 호출해야 하므로 생명주기 요구사항을 명확히 문서화해야 한다.
- REST pagination과 SSE 실시간 수신 결과가 겹칠 수 있으므로 메시지 병합/중복 제거 기준이 필요하다.
3. Goals
- 기존
ChatRoomActivity의 채팅방 상세 UI 구조를 최대한 재사용하되, DM에 맞지 않는 UI를 제거한 화면을 정의한다. CreateOrGetRoom,OpenRoom,ConnectEvents,GetMessages,SendTextMessage,DisconnectRealtimeAPI 계약을 Android 프로젝트 네이밍에 맞게 정리한다.- DM 채팅방 진입 시 방 생성/조회 후 생성된
roomId로 채팅방을 열고 초기 메시지를 표시한다. - 사용자가 상단으로 스크롤하면 과거 메시지를 커서 기반으로 추가 조회한다.
- 텍스트 메시지 전송 후 서버 응답 메시지를 화면에 반영한다.
- 채팅방 화면 진입/이탈, 앱 foreground/background 전환에 따른 SSE 연결/해제 정책을 정의한다.
- SSE 이벤트 이름/응답 payload, 재연결 가이드, UI thread 비차단, 최신 메시지 동기화 같은 실시간 연결 운영 기준을 정의한다.
4. Non-Goals
- AI 캐릭터 채팅방의 쿼터 구매, 광고 보상, 유료 메시지 구매, 채팅 리셋 기능은 DM 채팅화면에 포함하지 않는다.
character_type_badge,ll_can_badge,iv_more,notice_container는 DM 화면에 표시하지 않는다.- 음성 메시지 전송/재생 UI는 이번 범위에서 구현하지 않는다. 단, 서버 DTO의
voiceMessageUrl필드는 모델에 보존한다. - 메시지 삭제, 신고, 차단, 알림 설정, 읽음 처리, unread count 실시간 갱신은 이번 범위에 포함하지 않는다.
- 백엔드 API 스키마와 필드명을 Android에서 임의 변경하지 않는다. Kotlin class 이름만 프로젝트 가이드에 맞게 조정한다.
- 기존 AI
ChatRoomActivity동작을 변경하거나 리팩터링하지 않는다. - 음성 메시지 UI가 아직 정해지지 않았으므로
messageType=VOICE메시지는 DTO에는 보존하되 이번 구현 대상 UI에서 제외한다.
5. Target Users
- 채팅 탭의 DM 채팅방에서 크리에이터와 1:1 메시지를 주고받으려는 앱 사용자.
kr.co.vividnext.sodalive.v2.main.chat및 신규 DM 채팅 화면을 구현/유지보수하는 Android 개발자.
6. User Stories
- 사용자는 크리에이터 프로필 또는 채팅 탭에서 DM 채팅방을 열고 싶다.
- 사용자는 DM 채팅방에 들어왔을 때 상대 프로필, 상대 이름, 최근 메시지를 바로 보고 싶다.
- 사용자는 텍스트를 입력해 크리에이터에게 메시지를 보낼 수 있어야 한다.
- 사용자는 채팅 목록 상단으로 스크롤해 이전 대화를 이어서 확인하고 싶다.
- 사용자는 화면을 벗어나거나 앱이 백그라운드로 전환될 때 실시간 연결이 안전하게 종료되기를 기대한다.
7. Core Features
DM Chat Room Entry
DM 채팅방을 생성하거나 기존 방을 조회한 뒤 상세 화면을 연다.
Requirements
- 크리에이터와의 DM 시작 진입점에서는
creatorId를 전달받아POST /api/v2/user-creator-chat/rooms/create를 호출한다. - API 응답의
roomId를 기준으로 DM 채팅방 상세 화면을 연다. - 채팅 탭 DM item처럼 이미
roomId를 알고 있는 진입점은 방 생성/조회 API를 생략하고 바로 OpenRoom 흐름으로 진입할 수 있다. roomId <= 0인 상태에서는 채팅방을 열지 않고 기존 Activity 종료/오류 처리 정책을 따른다.
API Contract
- Operation:
CreateOrGetRoom - Method:
POST - Path:
/api/v2/user-creator-chat/rooms/create - Request:
CreateUserCreatorChatRoomRequest - Response:
CreateUserCreatorChatRoomResponse
data class CreateUserCreatorChatRoomRequest(
val creatorId: Long
)
data class CreateUserCreatorChatRoomResponse(
val roomId: Long
)
Naming Requirements
- Android DTO 이름은 기능 도메인과 현재 프로젝트 가이드를 고려해
CreateDmChatRoomRequest,CreateDmChatRoomResponse또는 동등하게 명확한 이름으로 변경한다. - 파일/패키지는 신규 v2 화면 원칙에 따라
kr.co.vividnext.sodalive.v2하위에 둔다.
DM Chat Room UI
기존 activity_chat_room.xml과 유사한 전체 구조를 유지하되 DM에 맞지 않는 요소를 제거한다.
Requirements
- 전체 화면은 기존 채팅방처럼 배경 이미지, dim, header, message RecyclerView, input 영역으로 구성한다.
- header에는 뒤로가기, 상대 프로필 이미지, 상대 닉네임을 표시한다.
- 아래 UI는 표시하지 않는다.
character_type_badgell_can_badge- 더보기 버튼
iv_more - 안내 메시지 영역
notice_container
rv_messages의 top constraint는notice_container가 아닌header_container하단을 기준으로 조정한다.- 입력 영역은 기존
et_message,iv_send와 유사하게 텍스트 입력 및 전송 버튼을 제공한다. - 전송 버튼은 입력값이 blank이면 비활성화하고, 입력값이 있으면 활성화한다.
Edge Cases
- 상대 프로필 이미지 URL이 비어 있거나 로딩 실패하면 기존 placeholder 정책을 따른다.
- 상대 닉네임이 비어 있으면 빈 문자열 그대로 표시하고 임의 대체 문구를 추가하지 않는다.
- 키보드 표시, IME send, 화면 하단 padding/inset은 기존
ChatRoomActivity의 사용자 경험을 우선 참고한다.
Open Room And Initial Messages
생성되었거나 기존에 존재하는 DM 채팅방을 열고 최신 메시지를 표시한다.
Requirements
- 화면 진입 시
GET /api/v2/user-creator-chat/rooms/{roomId}/open을 호출한다. limitquery parameter 기본값은20으로 설정한다.- 응답의
messages를 오래된 메시지부터 최신 메시지 순으로 정렬해 표시한다. hasMore,nextCursor를 저장해 과거 메시지 조회 상태로 사용한다.- 응답의
opponentNickname,opponentProfileImageUrl을 header 상대 정보로 사용한다. mine=true인 메시지는 사용자 발신 말풍선,mine=false인 메시지는 상대 발신 말풍선으로 표시한다.senderNickname,senderProfileImageUrl은 상대 메시지 item 표시와 메시지별 발신자 정보가 필요한 경우에 활용한다.
API Contract
- Operation:
OpenRoom - Method:
GET - Path:
/api/v2/user-creator-chat/rooms/{roomId}/open - Query:
limit=20 - Response:
UserCreatorChatRoomOpenResponse
data class UserCreatorChatRoomOpenResponse(
val roomId: Long,
val opponentNickname: String,
val opponentProfileImageUrl: String,
val messages: List<UserCreatorChatMessageItemDto>,
val hasMore: Boolean,
val nextCursor: Long?
)
SSE Realtime Events
채팅방이 열려 있는 동안 서버 이벤트를 연결해 새 메시지를 실시간으로 반영한다.
Requirements
- OpenRoom 성공 후
GET /api/v2/user-creator-chat/rooms/{roomId}/events를 호출해 SSE 연결을 시작한다. - SSE 연결은 화면이 foreground에 있고 채팅방이 활성 상태일 때만 유지한다.
- 화면을 벗어나거나 앱이 background로 전환되면
DisconnectRealtimeAPI를 항상 호출한다. - 재진입 또는 foreground 복귀 시 현재
roomId로 다시 SSE 연결을 시도한다. - 재연결 성공 후 필요한 경우 GetMessages API로 누락 가능성이 있는 메시지를 동기화한 뒤 SSE 연결 상태를 갱신한다.
- SSE로 수신한 메시지는 기존 목록에 중복 추가하지 않는다. 중복 판단 기준은
messageId다. - SSE 연결 객체는 더 이상 사용하지 않을 때 cancel/close 처리하고 listener 참조를 해제한다.
- 서버는 SSE
reconnectTime을3000ms로 내려주므로 클라이언트 재연결 기본 간격은 3초를 따른다. - 서버가 각 이벤트에
id를 부여하더라도 현재 백엔드는 재연결 시Last-Event-ID를 해석해 유실 이벤트를 replay하지 않는다. - 네트워크 오류로 인한 재연결은 서버의 3초 가이드를 따르되, 구현 라이브러리가 추가 backoff/jitter를 제공하는 경우 즉시 무한 재시도가 발생하지 않도록 적용한다.
- disconnect/cancel 처리는 UI thread를 블로킹하지 않아야 한다.
- 현재 저장소에는 SSE/EventSource 구현 패턴이 없으므로 구현 계획에서 OkHttp 기반 EventSource, Retrofit streaming, 별도 라이브러리 사용 여부를 확정한다.
Event Payloads
connected이벤트- 이벤트 이름:
connected - 데이터 형식:
"connected"단순 문자열 - 용도: SSE 연결 성공 시 최초 1회 발송되는 handshake 이벤트다.
- 이벤트 이름:
message이벤트- 이벤트 이름:
message - 데이터 형식:
UserCreatorChatMessageItemDtoJSON 객체 - 용도: 채팅방에 새로운 메시지가 수신되었을 때 실시간으로 전송된다.
- 주요 필드:
messageId,messageType,mine,createdAt,textMessage,voiceMessageUrl,senderId,senderNickname,senderProfileImageUrl
- 이벤트 이름:
API Contract
- Operation:
ConnectEvents - Method:
GET - Path:
/api/v2/user-creator-chat/rooms/{roomId}/events - Response:
text/event-stream - Events:
connected,message - Event id: 서버가 이벤트
id를 내려주지만 현재 서버는 재연결 시Last-Event-ID를 replay 기준으로 처리하지 않는다.
Edge Cases
- 연결 실패 시 앱이 crash 되지 않아야 하며, 기존 네트워크 오류 처리 정책에 맞춰 사용자에게 최소한으로 안내한다.
- SSE 종료/실패 콜백 수신 시 연결 상태를 disconnected로 갱신한다.
- 이미 연결 중이면 중복 연결을 만들지 않는다.
- 연결 해제 API 호출 중 화면 종료가 진행되어도 종료 흐름을 막지 않는다.
connected이벤트는 UI 메시지 목록에 추가하지 않고 연결 상태 갱신에만 사용한다.message이벤트 수신 시UserCreatorChatMessageItemDto로 파싱하고,messageType=TEXT인 메시지만 이번 UI 대상으로 표시한다.- 재연결 후
Last-Event-ID기반 자동 replay를 기대하지 않고, 필요한 경우 GetMessages API로 누락 메시지를 보정한다.
Load Older Messages
사용자가 메시지 목록 상단으로 스크롤하면 과거 메시지를 추가 조회한다.
Requirements
hasMore=true이고isLoading=false인 상태에서 상단에 도달하면 과거 메시지 API를 호출한다.cursor는 OpenRoom 또는 직전 GetMessages 응답의nextCursor를 사용한다.cursor는 현재 화면에 로드된 가장 오래된 메시지보다 이전 페이지를 요청하기 위한 keyset cursor로 취급한다.limitquery parameter 기본값은20으로 설정한다.- 응답 메시지는 기존 목록 상단에 prepend하고, 스크롤 위치를 유지한다.
- 중복 메시지는
messageId기준으로 제거한다.
API Contract
- Operation:
GetMessages - Method:
GET - Path:
/api/v2/user-creator-chat/rooms/{roomId}/messages - Query:
cursor: Long?,limit=20 - Response:
UserCreatorChatMessagesPageResponse
data class UserCreatorChatMessagesPageResponse(
val messages: List<UserCreatorChatMessageItemDto>,
val hasMore: Boolean,
val nextCursor: Long?
)
Send Text Message
사용자가 입력한 텍스트 메시지를 서버로 전송하고 결과를 UI에 반영한다.
Requirements
- 전송 버튼 또는 IME send 액션 시 trim된 입력값이 blank이면 전송하지 않는다.
- 전송 요청이 진행 중인 동안 같은 입력에 대한 연타/중복 전송을 방지한다.
- 텍스트 메시지는
POST /api/v2/user-creator-chat/rooms/{roomId}/messages/text로 전송한다. - 요청 body는
textMessage를 사용한다. - 성공 응답의
message를 화면에 반영한다. deliveredRealtime,pushSent값은 응답 모델에 보존하되, 이번 PRD에서는 별도 UI를 추가하지 않는다.- 전송 시 낙관적 UI를 적용해 사용자 메시지를 즉시 목록에 추가하고 전송 중 상태로 표시한다.
- 전송 실패 시 해당 메시지를 실패 상태로 전환하고 재시도 버튼을 표시한다.
- 재시도 버튼을 누르면 같은 텍스트 메시지를 다시 전송하고, 성공 시 실패 상태를 정상 메시지 상태로 갱신한다.
- 사용자가 과거 메시지를 보고 있는 상태에서 메시지를 전송하면 최신 메시지 위치로 이동하거나, 최신 페이지 동기화 후 전송 메시지를 반영하는 방식 중 하나를 구현 계획에서 확정한다.
API Contract
- Operation:
SendTextMessage - Method:
POST - Path:
/api/v2/user-creator-chat/rooms/{roomId}/messages/text - Request:
SendUserCreatorTextMessageRequest - Response:
SendUserCreatorChatMessageResponse
data class SendUserCreatorTextMessageRequest(
val textMessage: String
)
data class SendUserCreatorChatMessageResponse(
val message: UserCreatorChatMessageItemDto,
val deliveredRealtime: Boolean,
val pushSent: Boolean
)
Disconnect Realtime
채팅방 화면 종료 또는 앱 background 전환 시 실시간 연결을 서버에 명시적으로 해제한다.
Requirements
- 화면을 벗어날 때 항상
POST /api/v2/user-creator-chat/rooms/{roomId}/events/disconnect를 호출한다. - 앱이 background로 전환될 때도 같은 API를 호출한다.
- 이미 disconnect 호출 중이면 중복 요청을 만들지 않는다.
- disconnect 성공 여부와 관계없이 화면 종료 자체는 막지 않는다.
- 화면 이탈 시 disconnect는 실시간 연결 해제 범위로 한정하고, 로그아웃처럼 로컬 캐시 삭제가 필요한 흐름과 구분한다.
API Contract
- Operation:
DisconnectRealtime - Method:
POST - Path:
/api/v2/user-creator-chat/rooms/{roomId}/events/disconnect - Response:
ApiResponse<Boolean>
Message DTO
서버 메시지 DTO는 텍스트/음성 필드를 모두 보존하되, 이번 화면의 구현 대상은 텍스트 메시지 UI와 텍스트 메시지 전송만으로 한정한다.
Response Model
data class UserCreatorChatMessageItemDto(
val messageId: Long,
val messageType: String,
val mine: Boolean,
val createdAt: Long,
val textMessage: String?,
val voiceMessageUrl: String?,
val senderId: Long,
val senderNickname: String,
val senderProfileImageUrl: String
)
Naming Requirements
- Android DTO 이름은
DmChatMessageResponse,DmChatRoomOpenResponse,DmChatMessagesPageResponse,SendDmTextMessageRequest,SendDmChatMessageResponse등 기능 중심 이름으로 조정한다. messageType은 서버 문자열을 그대로 보존한다.- 가능한
messageType값은TEXT,VOICE다. TEXT는 텍스트 메시지이며textMessage를 사용해 말풍선 UI로 표시한다.VOICE는 음성 메시지이며voiceMessageUrl을 보존한다. 음성 메시지 UI가 아직 정해지지 않았으므로 이번 구현에서는 별도 재생/전송 UI를 만들지 않는다.createdAt은 epoch millis로 보고 기존 시간 표시 유틸 또는 신규 formatter에서 변환한다.
8. UX / UI Expectations
- 기존 AI 채팅방과 동일한 채팅 화면 감각을 유지하되 DM에서 불필요한 캐릭터/쿼터 요소는 제거한다.
- header는 뒤로가기, 상대 프로필, 상대 닉네임만으로 간결하게 구성한다.
- 메시지 영역은 header 바로 아래부터 시작한다.
- 내 메시지와 상대 메시지의 시각적 구분은 기존
ChatMessageAdapter/item 스타일을 우선 재사용한다. - 긴 텍스트 메시지는 기존 채팅 말풍선의 줄바꿈/최대 폭 정책을 따른다.
- 키보드가 올라와도 입력 영역과 최신 메시지가 가려지지 않아야 한다.
9. Technical Constraints
- Android XML Views, ViewBinding, RecyclerView, RxJava3, Retrofit, Gson, Koin 구조를 따른다.
- 신규
Activity,Fragment,ViewModel및 연결 하위 코드는 저장소 규칙에 따라kr.co.vividnext.sodalive.v2패키지 하위에 작성한다. - DM 화면은 신규 Activity로 생성한다.
- 기존
ChatRoomActivity는 직접 변형하지 않는다. 필요한 경우 메시지 item/adapter/model 등 공용화 가능한 최소 컴포넌트만 구현 계획에서 검토한다. - REST API 응답은 기존 패턴처럼
Single<ApiResponse<...>>를 우선 사용한다. SSE 연결은text/event-stream수신으로 처리하며,connected/messageevent 파싱 방식을 구현 계획에서 확정한다. - token 전달은 기존 v2 API 패턴과
SharedPreferenceManager.token사용 방식을 따른다. Repository 구현 시Authorization헤더 문자열은 단일 bearer helper로 생성해 오입력을 방지한다. - 앱 foreground/background 감지는 Activity lifecycle과 앱 전체
ProcessLifecycleOwner중 어떤 기준을 사용할지 구현 계획에서 확정한다. - 구현 전
docs/20260610_DM_채팅화면/plan-task.md를 작성하고, 그 문서에 따라 최소 구현한다. - 테스트는 DTO mapper, 메시지 정렬/중복 제거, pagination state, disconnect lifecycle을 우선 검증한다.
10. API Summary
| 기능 | Method | Path | Request | Response |
|---|---|---|---|---|
| 방 생성 또는 조회 | POST |
/api/v2/user-creator-chat/rooms/create |
CreateUserCreatorChatRoomRequest |
CreateUserCreatorChatRoomResponse |
| 생성된 채팅방 열기 | GET |
/api/v2/user-creator-chat/rooms/{roomId}/open?limit=20 |
없음 | UserCreatorChatRoomOpenResponse |
| SSE 연결 | GET |
/api/v2/user-creator-chat/rooms/{roomId}/events |
없음 | text/event-stream: connected(String), message(UserCreatorChatMessageItemDto JSON) |
| 과거 메시지 조회 | GET |
/api/v2/user-creator-chat/rooms/{roomId}/messages?cursor={cursor}&limit=20 |
없음 | UserCreatorChatMessagesPageResponse |
| 텍스트 메시지 보내기 | POST |
/api/v2/user-creator-chat/rooms/{roomId}/messages/text |
SendUserCreatorTextMessageRequest |
SendUserCreatorChatMessageResponse |
| SSE 연결 끊기 | POST |
/api/v2/user-creator-chat/rooms/{roomId}/events/disconnect |
없음 | ApiResponse<Boolean> |
11. Metrics
creatorId기반 진입 시 CreateOrGetRoom 성공 후 반환된roomId로 DM 채팅방이 열린다.roomId기반 진입 시 OpenRoom이limit=20으로 호출된다.- OpenRoom 응답 메시지가 시간순으로
RecyclerView에 표시된다. - OpenRoom 응답의
opponentNickname,opponentProfileImageUrl이 header 상대 정보로 표시된다. - 상단 스크롤 시
hasMore=true와nextCursor조건에 따라 GetMessages가 호출된다. - 텍스트 메시지 전송 성공 시 응답의
message가 화면에 반영된다. - 텍스트 메시지 전송 실패 시 낙관적으로 추가된 메시지가 실패 상태와 재시도 버튼을 표시한다.
- 화면 이탈 또는 앱 background 전환 시 DisconnectRealtime 호출이 발생한다.
- SSE 재연결 기본 간격은 서버
reconnectTime=3000ms를 따른다. - SSE 재연결 후 필요 시 GetMessages API로 누락 메시지를 동기화한다.
- disconnect 처리는 UI thread를 블로킹하지 않는다.
- 제거 대상 UI(
character_type_badge,ll_can_badge,iv_more,notice_container)가 DM 화면에 나타나지 않는다.
12. Open Questions
- DM 채팅방 진입점별 intent extra 이름은 구현 계획에서 확정한다.
- 음성 메시지 UI/UX와
VOICE메시지 표시 방식은 후속 범위에서 확정한다.
13. References
- 기존 AI 채팅방 Activity:
app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt - 기존 AI 채팅방 layout:
app/src/main/res/layout/activity_chat_room.xml - 기존 AI 채팅 API:
app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt - 기존 AI 채팅 Repository:
app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt - v2 채팅 탭 API:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/data/ChatRoomApi.kt - v2 채팅 탭 모델:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/data/ChatRoomModels.kt - 관련 PRD:
docs/20260609_채팅_탭_페이지/prd.md - PRD 템플릿:
docs/prd/sample-prd.md - 작업 문서 규칙:
docs/agent-guides/work-plan-docs.md
14. Verification Log
- 2026-06-10:
docs/prd/sample-prd.md,docs/agent-guides/work-plan-docs.md,docs/20260609_채팅_탭_페이지/prd.md를 확인해 PRD 구조, 신규 문서 경로 규칙, 검증 기록 누적 방식을 확인했다. - 2026-06-10:
activity_chat_room.xml을 확인해 제거 대상 UI인tv_character_type_badge,ll_can_badge,iv_more,notice_container의 실제 위치와 메시지 영역 constraint를 확인했다. - 2026-06-10:
ChatRoomActivity.kt,TalkApi.kt,ChatRepository.kt를 확인해 기존 채팅방의 초기 로드, 상단 pagination, 텍스트 전송, 입력 UI, header/notice/쿼터 구조를 분석했다. - 2026-06-10:
ChatRoomApi.kt,ChatRoomModels.kt및docs/20260609_채팅_탭_페이지/prd.md를 확인해 v2 채팅 탭의 패키지/DTO/Retrofit 패턴과 DM 상세 화면 연결 배경을 확인했다. - 2026-06-10:
rg로EventSource,SSE,text/event-stream,ResponseBody,events/disconnect등을 검색했으며 현재 저장소에는 재사용 가능한 SSE 구현 패턴이 없는 것으로 확인했다. 따라서 SSE 구현 방식은 후속plan-task.md에서 별도 검증/결정할 항목으로 남겼다. - 2026-06-10: 이번 단계는 PRD 문서 생성만 수행했으며 Android 구현, 빌드, 테스트는 실행하지 않았다.
- 2026-06-10: 백그라운드 조사 결과를 반영해 REST pagination과 realtime 수신의
messageId기준 중복 제거, keyset cursor 의미, 전송 중 연타 방지, 과거 페이지를 보고 있을 때 전송 처리 기준, SSE cancel/close, failure 상태 전환, backoff/jitter 재연결, UI thread 비차단, disconnect와 로그아웃 캐시 삭제 범위 구분을 보강했다. - 2026-06-10: 사용자 확정 사항을 반영해 텍스트 전송 실패 UX를 낙관적 UI와 재시도 버튼으로 확정하고, DM 화면은 신규 Activity로 생성하도록 명시했다. OpenRoom 응답에
opponentNickname,opponentProfileImageUrl을 추가했으며,messageType가능한 값은TEXT,VOICE로 확정하고 이번 구현은 텍스트 메시지만 대상으로 제한했다. - 2026-06-10: 사용자 제공 백엔드 계약을 반영해 SSE 이벤트 이름과 payload를 확정했다.
connected이벤트는"connected"문자열 handshake로,message이벤트는UserCreatorChatMessageItemDtoJSON으로 문서화했다. 서버reconnectTime=3000ms,Last-Event-ID기반 replay 미지원, 재연결 후 GetMessages API를 통한 누락 메시지 보정 요구사항도 반영했다. - 2026-06-10: 백그라운드 조사 결과와 대조해
ConnectEvents응답 표기를ApiResponse<Unit>에서text/event-stream으로 보정하고, API Summary와 기술 제약도 SSE stream 수신/파싱 기준으로 갱신했다. - 2026-06-10: 후속 Repository 구현 시
Authorization헤더 오입력 방지를 위해 단일 bearer helper로 헤더 문자열을 생성하도록 기술 제약에 기록했다.