From 64deadda0bbc4356efe24069122b303a55ab8543 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 13 Aug 2025 05:23:12 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-room):=201.1=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EB=AA=A8=EB=8D=B8=20=EC=83=9D=EC=84=B1=20=EB=B0=8F?= =?UTF-8?q?=20=EC=B1=84=ED=8C=85=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=84=9C=EB=B2=84-=EB=A1=9C=EC=BB=AC=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 왜: 서버 스키마와 클라이언트 전용 필드가 혼재되어 혼란을 야기하던 문제를 해결하고, 유지보수성과 확장성을 높이기 위함. 무엇: - tasks 1.1 수행 (데이터 모델 클래스 생성) - ChatMessage 데이터 클래스 생성 (로컬/UI/도메인용) - MessageStatus enum 생성 (SENDING, SENT, FAILED) - MessageType enum 생성 (USER_MESSAGE, AI_MESSAGE, NOTICE, TYPING_INDICATOR) - CharacterType 기존 enum 재사용 (chat/character/detail/CharacterDetailResponse.kt) - ChatRoomEnterResponse, ChatMessagesResponse 데이터 클래스 생성 - 채팅 메시지 모델 서버-로컬 분리 및 응답 모델 정리 - ServerChatMessage DTO 추가 (서버 응답 전용: messageId, message, profileImageUrl, mine, createdAt) - ChatMessageMappers 추가: ServerChatMessage.toLocal(isGrouped: Boolean = false) - ChatRoomEnterResponse, ChatMessagesResponse에서 messages 타입을 List로 정리 - 문서 - docs/chat-room-data-models.md 갱신 (서버/로컬 분리 사항 반영) - docs/chat-room-message-model-separation.md 신설 (분리 배경/가이드) 추가 참고: - 시간 포맷 유틸은 후속 태스크(8.1)에서 테스트와 함께 구현 예정 --- .../sodalive/chat/room/CharacterInfo.kt | 16 +++++++++++ .../sodalive/chat/room/ChatMessage.kt | 27 +++++++++++++++++++ .../sodalive/chat/room/ChatMessageMappers.kt | 23 ++++++++++++++++ .../chat/room/ChatMessagesResponse.kt | 14 ++++++++++ .../chat/room/ChatRoomEnterResponse.kt | 15 +++++++++++ .../sodalive/chat/room/MessageStatus.kt | 19 +++++++++++++ .../sodalive/chat/room/MessageType.kt | 17 ++++++++++++ .../sodalive/chat/room/ServerChatMessage.kt | 20 ++++++++++++++ 8 files changed, 151 insertions(+) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/CharacterInfo.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessage.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessageMappers.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessagesResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatRoomEnterResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageStatus.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageType.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/room/ServerChatMessage.kt diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/CharacterInfo.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/CharacterInfo.kt new file mode 100644 index 00000000..68101998 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/CharacterInfo.kt @@ -0,0 +1,16 @@ +/* + * 보이스온 - 채팅방 캐릭터 정보 모델 + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName +import kr.co.vividnext.sodalive.chat.character.detail.CharacterType + +@Keep +data class CharacterInfo( + @SerializedName("characterId") val characterId: Long, + @SerializedName("name") val name: String, + @SerializedName("profileImageUrl") val profileImageUrl: String, + @SerializedName("characterType") val characterType: CharacterType +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessage.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessage.kt new file mode 100644 index 00000000..2f6d1416 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessage.kt @@ -0,0 +1,27 @@ +/* + * 보이스온 - 채팅 메시지 모델 (서버 응답 기반) + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +/** + * 서버/로컬 공통으로 사용하는 채팅 메시지 모델 + * - createdAt: UTC timestamp(ms) + * - status: 기본값 SENT (서버에서 내려오는 메시지) + * - localId: 서버 전송 전 임시 식별자 (전송 중 로컬 식별용) + * - isGrouped: UI 그룹화 처리용 클라이언트 플래그 + */ +@Keep +data class ChatMessage( + @SerializedName("messageId") val messageId: Long, + @SerializedName("message") val message: String, + @SerializedName("profileImageUrl") val profileImageUrl: String, + @SerializedName("mine") val mine: Boolean, + @SerializedName("createdAt") val createdAt: Long, + @SerializedName("status") val status: MessageStatus = MessageStatus.SENT, + @SerializedName("localId") val localId: String? = null, + // 서버 필드는 아니지만 UI 로직 편의를 위해 포함 (직렬화 제외 가능) + val isGrouped: Boolean = false +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessageMappers.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessageMappers.kt new file mode 100644 index 00000000..9f4ea163 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessageMappers.kt @@ -0,0 +1,23 @@ +/* + * 보이스온 - 채팅 메시지 매핑 유틸리티 + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep + +/** + * 서버 DTO -> 로컬 표시/도메인 모델 변환 + */ +@Keep +fun ServerChatMessage.toLocal(isGrouped: Boolean = false): ChatMessage { + return ChatMessage( + messageId = messageId, + message = message, + profileImageUrl = profileImageUrl, + mine = mine, + createdAt = createdAt, + status = MessageStatus.SENT, + localId = null, + isGrouped = isGrouped + ) +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessagesResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessagesResponse.kt new file mode 100644 index 00000000..2e7cd6cd --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessagesResponse.kt @@ -0,0 +1,14 @@ +/* + * 보이스온 - 점진적 메시지 로딩 응답 모델 + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class ChatMessagesResponse( + @SerializedName("messages") val messages: List, + @SerializedName("hasMore") val hasMore: Boolean, + @SerializedName("nextCursor") val nextCursor: Long? +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatRoomEnterResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatRoomEnterResponse.kt new file mode 100644 index 00000000..bc17a19e --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatRoomEnterResponse.kt @@ -0,0 +1,15 @@ +/* + * 보이스온 - 통합 채팅방 입장 응답 모델 + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class ChatRoomEnterResponse( + @SerializedName("roomId") val roomId: Long, + @SerializedName("character") val character: CharacterInfo, + @SerializedName("messages") val messages: List, + @SerializedName("hasMoreMessages") val hasMoreMessages: Boolean +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageStatus.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageStatus.kt new file mode 100644 index 00000000..63167f64 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageStatus.kt @@ -0,0 +1,19 @@ +/* + * 보이스온 - 채팅방 메시지 전송 상태 Enum + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep + +/** + * 메시지 전송 상태 + */ +@Keep +enum class MessageStatus { + /** 전송 중 */ + SENDING, + /** 전송 완료 */ + SENT, + /** 전송 실패 */ + FAILED +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageType.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageType.kt new file mode 100644 index 00000000..e82e0ab0 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageType.kt @@ -0,0 +1,17 @@ +/* + * 보이스온 - 채팅방 메시지 타입 Enum + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep + +/** + * 메시지 표시 타입 (RecyclerView ViewType과 연계) + */ +@Keep +enum class MessageType { + USER_MESSAGE, + AI_MESSAGE, + NOTICE, + TYPING_INDICATOR +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ServerChatMessage.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ServerChatMessage.kt new file mode 100644 index 00000000..0520dfe2 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ServerChatMessage.kt @@ -0,0 +1,20 @@ +/* + * 보이스온 - 채팅 메시지 모델 (서버 응답 전용 DTO) + */ +package kr.co.vividnext.sodalive.chat.room + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +/** + * 서버에서 내려오는 순수 메시지 데이터 + * - 클라이언트 전용 필드(status, localId, isGrouped 등)를 포함하지 않음 + */ +@Keep +data class ServerChatMessage( + @SerializedName("messageId") val messageId: Long, + @SerializedName("message") val message: String, + @SerializedName("profileImageUrl") val profileImageUrl: String, + @SerializedName("mine") val mine: Boolean, + @SerializedName("createdAt") val createdAt: Long +)