[Android | STOMP] 안드로이드 채팅 구현하기(2) 채팅 기능 구현 (feat. Kotlin & Krossbow)

한시삼십사분·2024년 7월 5일
0

Android

목록 보기
10/10
post-thumbnail

🙋‍♂️ 서론

저번 글을 작성하고 SSFAY 수료, 면접 등 여러 일이 겹치면서 2편을 작성하지 못했다. 지금이나마 2편을 작성해야겠다는 생각이 들어 포스팅을 시작한다.

Subscribe

STMOP 연결에 성공했다면 특정 topic에 구독 요청을 해야한다.

val newChatMessage: Flow<StompFrame.Message> = stompSession.subscribe(
                StompSubscribeHeaders(
                    destination = "${subscribe을 위한 url}",
                    customHeaders = mapOf(
                        "Authorization" to "${token}"
                    )
                )
            )

지난 글에서 연결에 성공한 StompSessionsubscribe 함수를 사용한다.
파라미터로는 StompSubscribeHeaders를 사용했다.
StompSubscribeHeaders에 구독을 위한 custom stomp header를 포함시킬 수 있다.

채팅 수신

위의 newChatMessageFlow이기 때문에 collect를 통해서 데이터를 수집해야한다.

newChatMessage.collect {
                val chatMessage = moshi.adapter(Chat::class.java).fromJson(it.bodyAsText)
                intent { copy(chatList = chatList + chatMessage!!) }
            }

수신하는 데이터의 타입은 StompFrame이다.
클래스 내부를 보면

val bodyAsText: String by lazy { body?.asText(headers.contentType) ?: "" }

과 같이 해당 Frame의 bodyString 타입으로 변환한 bodyAsText 라는 멤버가 존재한다.

또한 내가 정의한 Chat data class는 다음과 같다

data class Chat(
    val memberId: Long,
    val memberName: String,
    val memberImageUrl: String,
    val content: String,
    val sendDateTime: Long,
    val readCount: Int = 1
)

이는 서버 개발자와의 규약으로 StompFrame의 body는 Chat의 정보가 담겨져 넘어온다.

자 그럼 다시

newChatMessage.collect {
                val chatMessage = moshi.adapter(Chat::class.java).fromJson(it.bodyAsText)
                intent { copy(chatList = chatList + chatMessage!!) }
            }

이 코드를 보면 새로운 채팅을 수신하면 이를 moshi를 사용해 자체 데이터 클래스인 Chat으로 변환하고 List<Chat> 타입인 chatList에 수신한 채팅을 추가하고 intent를 사용해 UI 상태를 갱신시킨다.

본인은 Compose + MVI 패턴을 사용했기 때문에 위와 같이 개발했지만 MVVM등 다른 패턴을 사용하는 경우에는 상태에 맞게 View에서 감지만 하면 될 것이다.

채팅 전송

viewModelScope.launch {
            stompSession.withMoshi(moshi).convertAndSend(
                StompSendHeaders(
                    destination = SEND_URL,
                    customHeaders = mapOf(
                        HEADER_AUTHORIZATION to tkn
                    )
                ),
                ChatMessage(roomId, userInfo.photo, message)
            )
        }
    }

우선 suspend fun 이니 Coroutine Scope 내에서 함수를 호출하면 된다.

StompSessionconvertAndSend 함수를 살펴보면

suspend inline fun <reified T> TypedStompSession.convertAndSend(headers: StompSendHeaders, body: T): StompReceipt? =
    convertAndSend(headers, body, typeRefOf())

StompSendHeaderbody를 파라미터로 받는다.
본인은 채팅을 보낼 때 token을 헤더에 담아 보내야 했기 때문에 위와 같이 사용했지만, 상황에 따라 목적지 urlbody만 받을 수도 있고, url만 받을 수도 있다.

body는 규약된 데이터 클래스를 잘 사용하면 될 것이다.

마무리

채팅을 화면에 그리는 것은 나보다 더 잘 정리한 사람들이 많기 때문에 UI관련 로직은 다루지 않고, STOMP를 사용하는 방법에 초점을 맞춰 글을 작성했다.

혹시 전체 코드를 참고하려면 github에서 feature -> student -> home -> chatting 안에 있는 코드를 확인하면 될 것이다.

역시 사람들이 많이 사용하지 않는 것에는 이유가 있다는 것을 느꼈고, 하지만 라이브러리를 샅샅이 뜯어보고 내 상황에 맞게 사용하는 능력을 향상시키는데 많은 도움이 되었던 경험이었다.

profile
인간은 망각의 동물이라지만 이건 너무한 거 아니냐고

1개의 댓글

comment-user-thumbnail
2024년 7월 16일

참고가 많이 되었습니다! 감사합니다:)

답글 달기