From 725c4335e1dac2fc47913991c169f8e9c45e6e32 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 13 Aug 2025 17:10:06 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-talk-room):=20Room=20Database=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B0=8F=20Entity=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?refactor(chat-talk-room):=20=ED=8C=A8=ED=82=A4=EC=A7=80=20chat.?= =?UTF-8?q?room=20=E2=86=92=20chat.talk.room=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98=20=EB=B0=8F=20DI=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 왜: 기능 영역 명확화(talk) 및 DI 책임 분리로 유지보수성과 확장성을 높이기 위함 무엇: - 모델/응답/enum 파일들을 chat.room → chat.talk.room 으로 이동 - Room DB 패키지를 chat.room.db → chat.talk.room.db 로 이동 - AppDatabase 클래스명을 역할에 맞게 ChatMessageDatabase로 변경 문서: - docs/chat-talk-room-package-migration-and-di-module.md 추가 - docs/chat-room-room-database.md 내용 클래스명/경로 갱신 --- .../chat/{ => talk}/room/CharacterInfo.kt | 2 +- .../chat/{ => talk}/room/ChatMessage.kt | 2 +- .../{ => talk}/room/ChatMessageMappers.kt | 2 +- .../{ => talk}/room/ChatMessagesResponse.kt | 2 +- .../{ => talk}/room/ChatRoomEnterResponse.kt | 2 +- .../chat/talk/room/ChatTalkRoomModule.kt | 13 +++++++ .../chat/{ => talk}/room/MessageStatus.kt | 2 +- .../chat/{ => talk}/room/MessageType.kt | 2 +- .../chat/{ => talk}/room/ServerChatMessage.kt | 2 +- .../talk/room/db/ChatMessageConverters.kt | 17 +++++++++ .../chat/talk/room/db/ChatMessageDao.kt | 32 ++++++++++++++++ .../chat/talk/room/db/ChatMessageDatabase.kt | 37 +++++++++++++++++++ .../chat/talk/room/db/ChatMessageEntity.kt | 22 +++++++++++ .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 + 14 files changed, 131 insertions(+), 8 deletions(-) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/CharacterInfo.kt (91%) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/ChatMessage.kt (95%) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/ChatMessageMappers.kt (91%) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/ChatMessagesResponse.kt (88%) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/ChatRoomEnterResponse.kt (90%) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatTalkRoomModule.kt rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/MessageStatus.kt (85%) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/MessageType.kt (84%) rename app/src/main/java/kr/co/vividnext/sodalive/chat/{ => talk}/room/ServerChatMessage.kt (92%) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageConverters.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDao.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDatabase.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageEntity.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/talk/room/CharacterInfo.kt similarity index 91% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/CharacterInfo.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/CharacterInfo.kt index 68101998..7f08bb8a 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/CharacterInfo.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/CharacterInfo.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 채팅방 캐릭터 정보 모델 */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep import com.google.gson.annotations.SerializedName 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/talk/room/ChatMessage.kt similarity index 95% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessage.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessage.kt index 2f6d1416..23cca8d1 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessage.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessage.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 채팅 메시지 모델 (서버 응답 기반) */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep import com.google.gson.annotations.SerializedName 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/talk/room/ChatMessageMappers.kt similarity index 91% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessageMappers.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageMappers.kt index 9f4ea163..75938a98 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessageMappers.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageMappers.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 채팅 메시지 매핑 유틸리티 */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep 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/talk/room/ChatMessagesResponse.kt similarity index 88% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessagesResponse.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessagesResponse.kt index 2e7cd6cd..556e6adc 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatMessagesResponse.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessagesResponse.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 점진적 메시지 로딩 응답 모델 */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep import com.google.gson.annotations.SerializedName 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/talk/room/ChatRoomEnterResponse.kt similarity index 90% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatRoomEnterResponse.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomEnterResponse.kt index bc17a19e..b378e244 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ChatRoomEnterResponse.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomEnterResponse.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 통합 채팅방 입장 응답 모델 */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep import com.google.gson.annotations.SerializedName diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatTalkRoomModule.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatTalkRoomModule.kt new file mode 100644 index 00000000..3c6e9bb4 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatTalkRoomModule.kt @@ -0,0 +1,13 @@ +package kr.co.vividnext.sodalive.chat.talk.room + +import kr.co.vividnext.sodalive.chat.talk.room.db.ChatMessageDatabase +import org.koin.android.ext.koin.androidContext +import org.koin.dsl.module + +val chatTalkRoomModule = module { + // Database + single { ChatMessageDatabase.getDatabase(androidContext()) } + + // DAO + single { get().chatMessageDao() } +} 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/talk/room/MessageStatus.kt similarity index 85% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageStatus.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/MessageStatus.kt index 63167f64..a2134426 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageStatus.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/MessageStatus.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 채팅방 메시지 전송 상태 Enum */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep 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/talk/room/MessageType.kt similarity index 84% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageType.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/MessageType.kt index e82e0ab0..042b1cc4 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/MessageType.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/MessageType.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 채팅방 메시지 타입 Enum */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep 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/talk/room/ServerChatMessage.kt similarity index 92% rename from app/src/main/java/kr/co/vividnext/sodalive/chat/room/ServerChatMessage.kt rename to app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ServerChatMessage.kt index 0520dfe2..04cac086 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/room/ServerChatMessage.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ServerChatMessage.kt @@ -1,7 +1,7 @@ /* * 보이스온 - 채팅 메시지 모델 (서버 응답 전용 DTO) */ -package kr.co.vividnext.sodalive.chat.room +package kr.co.vividnext.sodalive.chat.talk.room import androidx.annotation.Keep import com.google.gson.annotations.SerializedName diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageConverters.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageConverters.kt new file mode 100644 index 00000000..73b7c875 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageConverters.kt @@ -0,0 +1,17 @@ +/* + * 보이스온 - 채팅 메시지 Room TypeConverters + */ +package kr.co.vividnext.sodalive.chat.talk.room.db + +import androidx.room.TypeConverter +import kr.co.vividnext.sodalive.chat.talk.room.MessageStatus + +object ChatMessageConverters { + @TypeConverter + @JvmStatic + fun fromStatus(value: MessageStatus?): String? = value?.name + + @TypeConverter + @JvmStatic + fun toStatus(value: String?): MessageStatus? = value?.let { MessageStatus.valueOf(it) } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDao.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDao.kt new file mode 100644 index 00000000..54267951 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDao.kt @@ -0,0 +1,32 @@ +/* + * 보이스온 - 채팅 메시지 DAO + */ +package kr.co.vividnext.sodalive.chat.talk.room.db + +import androidx.annotation.Keep +import androidx.room.* + +@Keep +@Dao +interface ChatMessageDao { + @Query("SELECT * FROM chat_messages WHERE roomId = :roomId ORDER BY createdAt DESC LIMIT 20") + suspend fun getRecentMessages(roomId: Long): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertMessage(message: ChatMessageEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertMessages(messages: List) + + @Update + suspend fun updateMessage(message: ChatMessageEntity) + + @Query("DELETE FROM chat_messages WHERE roomId = :roomId AND createdAt < :cutoffTime") + suspend fun deleteOldMessages(roomId: Long, cutoffTime: Long) + + @Query("DELETE FROM chat_messages") + suspend fun deleteAllMessages() + + @Query("SELECT DISTINCT roomId FROM chat_messages") + suspend fun getAllRoomIds(): List +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDatabase.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDatabase.kt new file mode 100644 index 00000000..032ba301 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageDatabase.kt @@ -0,0 +1,37 @@ +/* + * 보이스온 - 채팅방 Room Database (ChatMessage 테이블 포함) + */ +package kr.co.vividnext.sodalive.chat.talk.room.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters + +@Database( + entities = [ChatMessageEntity::class], + version = 1, + exportSchema = false +) +@TypeConverters(ChatMessageConverters::class) +abstract class ChatMessageDatabase : RoomDatabase() { + abstract fun chatMessageDao(): ChatMessageDao + + companion object { + @Volatile + private var INSTANCE: ChatMessageDatabase? = null + + fun getDatabase(context: Context): ChatMessageDatabase { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + ChatMessageDatabase::class.java, + "chat_database" + ).build() + INSTANCE = instance + instance + } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageEntity.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageEntity.kt new file mode 100644 index 00000000..705bed8a --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/db/ChatMessageEntity.kt @@ -0,0 +1,22 @@ +/* + * 보이스온 - 채팅 메시지 Room Entity + */ +package kr.co.vividnext.sodalive.chat.talk.room.db + +import androidx.annotation.Keep +import androidx.room.Entity +import androidx.room.PrimaryKey +import kr.co.vividnext.sodalive.chat.talk.room.MessageStatus + +@Keep +@Entity(tableName = "chat_messages") +data class ChatMessageEntity( + @PrimaryKey val messageId: Long, + val roomId: Long, + val message: String, + val profileImageUrl: String, + val mine: Boolean, + val createdAt: Long, + val status: MessageStatus = MessageStatus.SENT, + val localId: String? = null +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 632af85c..0e95a4e3 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -146,6 +146,7 @@ import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagApi import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagRepository import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel import kr.co.vividnext.sodalive.mypage.recent.recentContentModule +import kr.co.vividnext.sodalive.chat.talk.room.chatTalkRoomModule import kr.co.vividnext.sodalive.mypage.service_center.FaqApi import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel @@ -410,6 +411,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModelModule, repositoryModule, recentContentModule, + chatTalkRoomModule, otherModule )