feat(chat-talk-room): Room Database 설정 및 Entity 생성

refactor(chat-talk-room): 패키지 chat.room → chat.talk.room 마이그레이션 및 DI 모듈 분리

왜: 기능 영역 명확화(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 내용 클래스명/경로 갱신
This commit is contained in:
2025-08-13 17:10:06 +09:00
parent 64deadda0b
commit 725c4335e1
14 changed files with 131 additions and 8 deletions

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 채팅방 캐릭터 정보 모델 * 보이스온 - 채팅방 캐릭터 정보 모델
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 채팅 메시지 모델 (서버 응답 기반) * 보이스온 - 채팅 메시지 모델 (서버 응답 기반)
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 채팅 메시지 매핑 유틸리티 * 보이스온 - 채팅 메시지 매핑 유틸리티
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 점진적 메시지 로딩 응답 모델 * 보이스온 - 점진적 메시지 로딩 응답 모델
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 통합 채팅방 입장 응답 모델 * 보이스온 - 통합 채팅방 입장 응답 모델
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@@ -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<ChatMessageDatabase>().chatMessageDao() }
}

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 채팅방 메시지 전송 상태 Enum * 보이스온 - 채팅방 메시지 전송 상태 Enum
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 채팅방 메시지 타입 Enum * 보이스온 - 채팅방 메시지 타입 Enum
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep

View File

@@ -1,7 +1,7 @@
/* /*
* 보이스온 - 채팅 메시지 모델 (서버 응답 전용 DTO) * 보이스온 - 채팅 메시지 모델 (서버 응답 전용 DTO)
*/ */
package kr.co.vividnext.sodalive.chat.room package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@@ -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) }
}

View File

@@ -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<ChatMessageEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMessage(message: ChatMessageEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMessages(messages: List<ChatMessageEntity>)
@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<Long>
}

View File

@@ -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
}
}
}
}

View File

@@ -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
)

View File

@@ -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.MemberTagRepository
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel
import kr.co.vividnext.sodalive.mypage.recent.recentContentModule 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.FaqApi
import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel
@@ -410,6 +411,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModelModule, viewModelModule,
repositoryModule, repositoryModule,
recentContentModule, recentContentModule,
chatTalkRoomModule,
otherModule otherModule
) )