이번 프로젝트를 진행하면서 Firebase로 실시간 채팅을 구현해보았습니다.
생각보다 신경쓸 부분이 많아서 오래걸렸지만 차근차근 작성해 보겠습니다.
data class ChatRoomDataEntity(
val chatRoomId: String,
val lastMessageData: LastMessageDataEntity,
val unReadMessage: Int,
val participant: List<String>,
val chatRoomSession: List<Map<String, Boolean>>
)
data class LastMessageDataModel(
val lastMessage: String,
val lastSendAt: String,
val lastMessageSender: String
)
채팅방에 대한 데이터는 채팅방 고유 ID, 마지막으로 받은 메시지(시간, 내용), 안읽은 메시지 갯수, 참여자, 채팅방에 지금 들어와 있는지 이렇게 구성되어 있습니다.
마지막으로 받은 메시지에 대한 내용을 가지고 오는 것보다 더 간단할 것 같아서 메시지를 보낼 때 Firebase에서 update 할 수 있도록 구성했고, 채팅방에 현재 상대방이 들어와 있는지 없는지 Boolean 타입으로 나타내어 false일 경우에는 안읽은 메시지로 나타내고 true일 경우에는 읽은 메시지로 나타낼 수 있도록 하였습니다.
채팅방 List에서 안읽은 메시지를 나타낼 수 있도록 Firebase의 snapshotListener를 이용하여 실시간으로 갱신되어 update 할 수 있도록 unReadMessage를 구성했습니다.
chatRoomDB.collection(COLLECTION_CHAT_MESSAGE)
.whereArrayContains("read", mapOf(recipientUid to false))
.addSnapshotListener { unReadMessage, e ->
chatRoomDB.update("unReadMessage", unReadMessage?.size())
}
// * unReadMessage 갯수 갱신
data class MessageDataModel(
val uid: String,
val chatRoomId: String,
val messageId: String,
val message: String?,
val imageList: List<ImageDataEntity>?,
val sendAt: String,
val read: List<Map<String, Boolean>>,
val type: MessageViewType
)
enum class MessageViewType(val messageType: Int) {
TEXT_MESSAGE(0),
IMAGE_MESSAGE(1)
}
메시지는 보낸 사람의 uid(고유 id를 통해서 그 사람의 프로필 정보를 가져옴) 메시지 고유 id, 보낸 시간, 읽음 여부, 메시지 타입(imageType or textType)을 담았습니다.
이미지 타입과 텍스트 타입 두가지 타입의 메시지를 보낼 수 있는데 메시지 타입일 때는 enum class로 타입을 나누어 multiView로 recyclerView를 나타낼 수 있도록 하였고 imageType일 때는 message 내용을 null로, textType일 때는 imageList를 null로 Firebase 채팅방에 보내줍니다.
채팅방과 채팅방을 구성하는 메시지에 대한 데이터 모델은 이렇게 설정하였습니다. 생각보다 처음 만들어보는 채팅이라서 그런지 생각해야할 부분이 많아서 데이터 모델만 해도 계속 추가하고 수정했었던 것 같습니다.
.
.
.

기존 채팅방이 없는 경우 서로 친구일 경우에 다음과 같이 상단에 친구 목록이 나타나게 되는데 채팅을 시작할 친구를 눌러서 채팅을 시작할 수 있습니다.
채팅방을 들어가면 먼저 채팅방이 있는지 없는지 Boolean으로 확인을 하고 채팅방이 없다면 false를 반환, 있다면 true를 반환하게 합니다.
채팅방이 있는지 없는지 체크하는 메서드는 LiveData로 observe하여 첫 메시지를 보내고 난 후 채팅방이 감지되면 바로 true를 반환합니다.
체크 메서드가 false로 결과 값을 반환하는 경우에 첫 메시지를 보내면 다음과 같이 Firebase에 채팅방에 대한 데이터가 set 됩니다.


상대방이 채팅을 읽는 경우를 생각하지 못해서 데이터 모델도 다시 고치고 시간이 좀 걸렸던 것 같습니다.
원래는 시간을 현재 사용하는 기기 시간인 LocalDateTime을 사용하여 작업을 진행하였는데 sortedDescending 이용하여 메시지 리스트를 adapter에 submitList하였을 때 순서대로 나오지 않는 경우가 발생하였습니다. 이 부분을 고치고자 한국 표준시를 이용하였고 이 부분에서 조금 아쉬웠던 점은 UTC를 활용하여 하지 못한 것 같아 다음에는 UTC+09와 같이 적용을 해볼까 생각하고 있습니다.
fun chatDateFormat() : String {
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.KOREAN)
formatter.timeZone = TimeZone.getTimeZone("Asia/Seoul")
return formatter.format(Date().time)
}
// * 한국 표준 시를 가져오는 코드