확인하지 않은 메시지를 빠르게 확인할 수 있는 비결은 무엇일까요? 바로 메시지 읽음 처리 기능에 있습니다.
TmaxCoreAI의 GAIA Works는 ‘그룹채팅방의 메시지 읽음 여부를 판단하기 위한 방법 및 장치’ 특허 기술을 기반으로 메시지 읽음 처리
로직을 대폭 개선했습니다. 메시지 읽음 처리 로직은 사용자가 마지막으로 확인한 메시지를 추적하고, 메시지 별 Unread Count를 계산할 수 있게 해주죠. 과부하와 성능 저하 문제까지 해결한, 더 똑똑해진 GAIA Works의 메시지 읽음 처리 로직을 소개합니다.
기존 메시지 읽음 처리 로직, 개선이 필요한 이유
메시지 읽음 처리 기능은 사용자가 채팅에서 마지막으로 읽은 메시지를 추적하여, 읽지 않은 메시지 개수를 계산하고 메시지 별로 Unread Count를
계산하여 보여줍니다.
기존의 메시지 읽음 처리 구조는 각 메시지마다 해당 메시지를 읽은 유저 ID 배열을 기록하고, 읽은 유저 ID 배열의 길이를 활용해 Unread Count를
계산했습니다. 이때 사용자가 많은 채팅방의 경우는 사용자가 적은 채팅방보다 더 많은 메시지가 오가기 때문에, 서비스 호출 횟수도 기하급수적으로
늘어나게 됩니다. 이에 따라 시스템이나 서버에 과도한 작업이나 요청이 집중되면 과부하가 발생해, 성능 저하로 이어지게 되었습니다. 이를 해결하기
위해 메시지 읽음 처리 로직을 개선했습니다.
로직 개선을 통해 개별 메시지마다 유저 ID 배열을 기록하는 대신, 룸 별 Last Read Message ID 테이블을 통해 메시지의 읽음 여부를 확인합니다.
새로운 로직을 통해 Unread Count를 더욱 정확하고 빠르게 계산하며 비효율까지 줄일 수 있었습니다. 로직 개선 과정을 살펴보겠습니다.
기존 메시지 읽음 처리 로직은?
가장 먼저 기존의 채팅 시스템 메시지 읽음 처리 로직을 살펴보겠습니다.
앞서 이야기한 것처럼 기존에는 메시지마다 읽은 사용자들의 정보를 개별적으로 저장해, 메시지의 Unread Count를 계산했습니다.
기존 메시지 읽음 처리 로직과 Unread count 도출방법
‘Tech’라는 룸에 10명의 사용자가 있고, 누군가 ‘A’라는 메시지를 보냈다고 가정해봅시다. ‘A’ 메시지를 읽은 사용자와 안 읽은 사용자로 나눠지겠죠.
그럼 메시지 A에는 다음과 같이 메시지를 읽은 사용자 정보가 개별적으로 저장이 됩니다. 이를 바탕으로 Unread Count를 계산합니다.
그림의 예시처럼 각 메시지마다 읽은 유저의 정보를 담기 때문에, 메시지가 쌓일수록 데이터의 양은 더욱 커질 수밖에 없습니다.
이로 인해 메모리 사용량은 증가하고 메시지 처리 속도는 느려지게 되죠.
또 하나의 문제는 과도한 서비스 콜(요청)이 발생한다는 점입니다. 사용자가 룸을 보지 않는 동안 새로운 메시지 30개가 새로 왔다고 가정해봅시다.
이때 읽지 않은 메시지는 30개가 되고, 메시지 읽음 처리를 위해 서버에 'PUT 요청*'을 보내죠.
* PUT 요청은 HTTP 요청 메소드 중 하나로, 서버에 데이터를 업데이트할 때 주로 사용됩니다.
메시지 하나하나에 정보를 기록하는 기존의 방식을 따르면, 30개의 메시지 읽음 처리를 위해 30번의 PUT 요청이 발생합니다.
사용자가 한 명인 경우에도 위 예시처럼 과도한 PUT 요청이 발생하는데, 여러 사용자가 동시에 메시지를 읽을 때는 더 많은 요청이 발생하죠.
10명의 사용자가 30개의 메시지 읽음 처리를 동시에 진행한다고 가정하면, 10명이 각각 30번의 PUT 요청을 보내기 때문에 서버는 300번의 PUT
요청을 처리합니다. 서버에 큰 부담이 될 수밖에 없는 무거운 작업이죠.
그렇기 때문에 서버가 감당하지 못해 성능이 떨어지거나 응답이 지연되는 등의 문제가 발생합니다.
개선된 메시지 읽음 처리 로직은?
개선된 메시지 읽음 처리 로직에서는 채팅방마다 별도의 Last Read Message ID 테이블이 추가되었습니다. 이제 개별 메시지에 정보를 기록하는
대신, 각 룸마다 있는 테이블에 사용자별 마지막 읽은 메시지 ID를 저장합니다.
개선된 메시지 읽음 처리 과정은 다음과 같습니다.
해당 룸의 Last Read Message ID 테이블 fetch
fetch 한 테이블을 정렬하여 각 메시지의 Unread Count를 계산
사용자가 룸에서 이탈할 경우(다른 룸으로 이동, 앱 종료 등),
현재 룸에서 가장 마지막에 도착한 메시지 ID로 해당 사용자의 Last Read Message ID 업데이트
이후 사용자가 룸에 재접속하는 경우, Last Read Message ID를 업데이트
사용자가 해당 룸에서 메시지를 확인하고 있는 동안은 해당유저의 Last Read Message ID값을 업데이트하지 않고, Infinity로 유지합니다.
그러면 해당 방의 메시지를 모두 읽었고, 앞으로 새로운 ID의 메시지가 오더라도 그 메시지는 읽은 것으로 간주됩니다. 정렬된 Last Read Message ID 테이블을 이용해 불필요한 DB 업데이트를 감소시키고, 빠르고 정확하게 Unread messages와 Unread Count를 계산할 수 있습니다.
개선된 메시지 읽음 처리 로직 : Last Read Message ID 테이블 활용
예시를 통해 다시 한번 살펴볼까요?
위 그림은 테이블로, 유저별로 마지막으로 읽은 메시지 ID를 나타냅니다.
해당 테이블에서 가로는 유저, 세로는 각 메시지의 ID를 의미합니다. (메세지는 룸 별로 순차적(Sequential)으로 할당된 ID를 가지고 있습니다.)
해당 룸의 사용자는 총 10명, 메시지는 10개이며 이중 하이라이트 된 사용자는 현재 룸을 실시간으로 확인하고 있는 사용자를 의미합니다. 각 유저가
마지막으로 읽은 메시지는 ‘ㅇ’로, ‘∞’는 사용자가 메시지를 실시간으로 확인하고 있음을 나타냅니다. 이를 바탕으로 Unread Count를 계산해봅시다.
10번 메시지의 Unread Count는 10(총인원) - 2 (User 2, User 1)로 8입니다.
9번 메시지의 Unread Count는 10(총인원) -2(User 2, User 1)로 8입니다.
8번 메시지의 Unread Count는 10(총인원) -3(User 2, User 1, User 3)으로 7입니다.
7번 메시지의 Unread Count는 10(총인원) -3(User 2, User 1, User 3)으로 7입니다.
6번 메시지의 Unread Count는 10(총인원) -3(User 2, User 1, User 3)으로 7입니다.
5번 메시지의 Unread Count는 10(총인원) -5(User 2, User 1, User 3, User 0, User 9)로 5입니다.
…
이런 식으로 각 메시지의 Unread Count를 구할 수 있습니다.
이때 새로운 메시지(11번 메시지)가 오면 룸을 보고 있는 인원의 수가 2명이므로, 새로운 메시지의 Unread Count는 10 - 2인 8입니다. 이처럼 개선된 로직은 각 메세지별로 읽음처리 서비스콜을 하지 않고 Unread Count를 계산할 수 있습니다.
유의할 점은 Last Read Message ID를 활용하기 위해서는 계산의 정확성을 위해 값이 적절한 상황에 제때 업데이트되어야 하며, 테이블 정렬이 항상 잘 되어 있어야 한다는 것입니다.
테이블 업데이트와 정렬 시점
테이블 업데이트와 정렬 시점은 두 가지로 나뉩니다.
첫째, 다른 룸에 있던 사용자가 해당 룸으로 이동하거나, '포커스 아웃(Focus Out)*' 상태에서 '포커스 인(Focus in)*' 될 때 Last Read Message ID를 -1로 서비스콜하여 업데이트합니다. 앞서 이야기한 것처럼 사용자가 해당 룸에서 메시지를 확인하고 있는 동안 Last Read Message ID는 DB에 -1로 표기되며, 프론트에서는 Infinity로 치환하여 가지고 있습니다.
* 사용자가 특정 화면이나 요소에 진입하여 포커스가 해당 요소로 이동되는 것을 의미합니다. 본문에서는 사용자가 채팅방으로 진입하여 그 방이 활성화되는 것을 뜻합니다.
* 사용자가 특정 화면이나 요소에서 벗어나 포커스가 다른 곳으로 이동하는 것을 의미합니다. 사용자가 채팅방을 떠나거나 화면에서 벗어나 비활성화되는 것을 뜻합니다.
둘째, 사용자가 해당 룸에서 다른 룸으로 이동하거나, 포커스 아웃(Focus Out) 될 때 Last Read Message ID를 해당 룸의 마지막 메시지 ID로
업데이트합니다.
데이터 업데이트 방식은 각 클라이언트가 처음으로 데이터를 가져오는 초기 1회만 테이블을 fetch한 후, 이후에 오는 메시지는 CMS 메시지를 받고
실시간으로 업데이트합니다. 즉, 한 번의 fetch 이후에는 DB에 업데이트된 데이터만 각 클라이언트에 반영해 최신 상태를 유지하는 것입니다.
채팅 시스템 메시지 읽음 처리 개선 효과
개선 전에는 100개의 메시지 읽음 처리를 위해서 100번의 서비스 콜이 필요하고, 이후 새로운 메시지가 n개 도착하면, 추가된 메시지를 처리하기 위해 n번의 서비스 콜이 필요했습니다.
메시지 읽음 처리 로직 개선 후에는 한 번의 요청으로 100개의 메시지 읽음 처리도 1번의 서비스 콜로 가능합니다. 이후 새로운 메시지 몇개가 도착해도 DB업데이트 없이 메시지 읽음 처리가 가능해졌습니다.
기존에는 서비스콜이 빈번하여 DB가 자주 과부하 되었지만, 지금은 각각의 클라이언트로 부하를 분산시켜 메신저의 효율성과 성능 향상까지 이루어졌습니다.
참고자료
- 자사 특허 자료
임재환, 김창엽, 김해선, 이호용, 그룹채팅방의 메시지 읽음 여부를 판단하기 위한 방법 및 장치, 특허번호 KR102658902B1, 2023, 2024. - 참고자료
천홍석, 천영석, 이상휘, 박상운, 그룹 채팅 메시지 읽지 않은 사람 수 연산 방법, 이를 구현하기 위한 프로그램이 저장된 기록매체 및 이를 구현하기 위해 매체에 저장된 컴퓨터프로그램, 특허번호 KR101987171B1, 2017, 2019.
Writer 이호용 Editor 최윤영 Graphic Design 김선우
해당 콘텐츠는 저작권법에 의해 보호받는 저작물로 TmaxCoreAI에 저작권이 있습니다.
해당 콘텐츠의 사전 동의없는 2차 가공 및 영리적인 이용을 금하고 있습니다.
확인하지 않은 메시지를 빠르게 확인할 수 있는 비결은 무엇일까요? 바로 메시지 읽음 처리 기능에 있습니다.
TmaxCoreAI의 GAIA Works는 ‘그룹채팅방의 메시지 읽음 여부를 판단하기 위한 방법 및 장치’ 특허 기술을 기반으로 메시지 읽음 처리
로직을 대폭 개선했습니다. 메시지 읽음 처리 로직은 사용자가 마지막으로 확인한 메시지를 추적하고, 메시지 별 Unread Count를 계산할 수 있게 해주죠. 과부하와 성능 저하 문제까지 해결한, 더 똑똑해진 GAIA Works의 메시지 읽음 처리 로직을 소개합니다.
기존 메시지 읽음 처리 로직, 개선이 필요한 이유
메시지 읽음 처리 기능은 사용자가 채팅에서 마지막으로 읽은 메시지를 추적하여, 읽지 않은 메시지 개수를 계산하고 메시지 별로 Unread Count를
계산하여 보여줍니다.
기존의 메시지 읽음 처리 구조는 각 메시지마다 해당 메시지를 읽은 유저 ID 배열을 기록하고, 읽은 유저 ID 배열의 길이를 활용해 Unread Count를
계산했습니다. 이때 사용자가 많은 채팅방의 경우는 사용자가 적은 채팅방보다 더 많은 메시지가 오가기 때문에, 서비스 호출 횟수도 기하급수적으로
늘어나게 됩니다. 이에 따라 시스템이나 서버에 과도한 작업이나 요청이 집중되면 과부하가 발생해, 성능 저하로 이어지게 되었습니다. 이를 해결하기
위해 메시지 읽음 처리 로직을 개선했습니다.
로직 개선을 통해 개별 메시지마다 유저 ID 배열을 기록하는 대신, 룸 별 Last Read Message ID 테이블을 통해 메시지의 읽음 여부를 확인합니다.
새로운 로직을 통해 Unread Count를 더욱 정확하고 빠르게 계산하며 비효율까지 줄일 수 있었습니다. 로직 개선 과정을 살펴보겠습니다.
기존 메시지 읽음 처리 로직은?
가장 먼저 기존의 채팅 시스템 메시지 읽음 처리 로직을 살펴보겠습니다.
앞서 이야기한 것처럼 기존에는 메시지마다 읽은 사용자들의 정보를 개별적으로 저장해, 메시지의 Unread Count를 계산했습니다.
기존 메시지 읽음 처리 로직과 Unread count 도출방법
‘Tech’라는 룸에 10명의 사용자가 있고, 누군가 ‘A’라는 메시지를 보냈다고 가정해봅시다. ‘A’ 메시지를 읽은 사용자와 안 읽은 사용자로 나눠지겠죠.
그럼 메시지 A에는 다음과 같이 메시지를 읽은 사용자 정보가 개별적으로 저장이 됩니다. 이를 바탕으로 Unread Count를 계산합니다.
그림의 예시처럼 각 메시지마다 읽은 유저의 정보를 담기 때문에, 메시지가 쌓일수록 데이터의 양은 더욱 커질 수밖에 없습니다.
이로 인해 메모리 사용량은 증가하고 메시지 처리 속도는 느려지게 되죠.
또 하나의 문제는 과도한 서비스 콜(요청)이 발생한다는 점입니다. 사용자가 룸을 보지 않는 동안 새로운 메시지 30개가 새로 왔다고 가정해봅시다.
이때 읽지 않은 메시지는 30개가 되고, 메시지 읽음 처리를 위해 서버에 'PUT 요청*'을 보내죠.
* PUT 요청은 HTTP 요청 메소드 중 하나로, 서버에 데이터를 업데이트할 때 주로 사용됩니다.
메시지 하나하나에 정보를 기록하는 기존의 방식을 따르면, 30개의 메시지 읽음 처리를 위해 30번의 PUT 요청이 발생합니다.
사용자가 한 명인 경우에도 위 예시처럼 과도한 PUT 요청이 발생하는데, 여러 사용자가 동시에 메시지를 읽을 때는 더 많은 요청이 발생하죠.
10명의 사용자가 30개의 메시지 읽음 처리를 동시에 진행한다고 가정하면, 10명이 각각 30번의 PUT 요청을 보내기 때문에 서버는 300번의 PUT
요청을 처리합니다. 서버에 큰 부담이 될 수밖에 없는 무거운 작업이죠.
그렇기 때문에 서버가 감당하지 못해 성능이 떨어지거나 응답이 지연되는 등의 문제가 발생합니다.
개선된 메시지 읽음 처리 로직은?
개선된 메시지 읽음 처리 로직에서는 채팅방마다 별도의 Last Read Message ID 테이블이 추가되었습니다. 이제 개별 메시지에 정보를 기록하는
대신, 각 룸마다 있는 테이블에 사용자별 마지막 읽은 메시지 ID를 저장합니다.
개선된 메시지 읽음 처리 과정은 다음과 같습니다.
해당 룸의 Last Read Message ID 테이블 fetch
fetch 한 테이블을 정렬하여 각 메시지의 Unread Count를 계산
사용자가 룸에서 이탈할 경우(다른 룸으로 이동, 앱 종료 등),
현재 룸에서 가장 마지막에 도착한 메시지 ID로 해당 사용자의 Last Read Message ID 업데이트
이후 사용자가 룸에 재접속하는 경우, Last Read Message ID를 업데이트
사용자가 해당 룸에서 메시지를 확인하고 있는 동안은 해당유저의 Last Read Message ID값을 업데이트하지 않고, Infinity로 유지합니다.
그러면 해당 방의 메시지를 모두 읽었고, 앞으로 새로운 ID의 메시지가 오더라도 그 메시지는 읽은 것으로 간주됩니다. 정렬된 Last Read Message ID 테이블을 이용해 불필요한 DB 업데이트를 감소시키고, 빠르고 정확하게 Unread messages와 Unread Count를 계산할 수 있습니다.
개선된 메시지 읽음 처리 로직 : Last Read Message ID 테이블 활용
예시를 통해 다시 한번 살펴볼까요?
위 그림은 테이블로, 유저별로 마지막으로 읽은 메시지 ID를 나타냅니다.
해당 테이블에서 가로는 유저, 세로는 각 메시지의 ID를 의미합니다. (메세지는 룸 별로 순차적(Sequential)으로 할당된 ID를 가지고 있습니다.)
해당 룸의 사용자는 총 10명, 메시지는 10개이며 이중 하이라이트 된 사용자는 현재 룸을 실시간으로 확인하고 있는 사용자를 의미합니다. 각 유저가
마지막으로 읽은 메시지는 ‘ㅇ’로, ‘∞’는 사용자가 메시지를 실시간으로 확인하고 있음을 나타냅니다. 이를 바탕으로 Unread Count를 계산해봅시다.
10번 메시지의 Unread Count는 10(총인원) - 2 (User 2, User 1)로 8입니다.
9번 메시지의 Unread Count는 10(총인원) -2(User 2, User 1)로 8입니다.
8번 메시지의 Unread Count는 10(총인원) -3(User 2, User 1, User 3)으로 7입니다.
7번 메시지의 Unread Count는 10(총인원) -3(User 2, User 1, User 3)으로 7입니다.
6번 메시지의 Unread Count는 10(총인원) -3(User 2, User 1, User 3)으로 7입니다.
5번 메시지의 Unread Count는 10(총인원) -5(User 2, User 1, User 3, User 0, User 9)로 5입니다.
…
이런 식으로 각 메시지의 Unread Count를 구할 수 있습니다.
이때 새로운 메시지(11번 메시지)가 오면 룸을 보고 있는 인원의 수가 2명이므로, 새로운 메시지의 Unread Count는 10 - 2인 8입니다. 이처럼 개선된 로직은 각 메세지별로 읽음처리 서비스콜을 하지 않고 Unread Count를 계산할 수 있습니다.
유의할 점은 Last Read Message ID를 활용하기 위해서는 계산의 정확성을 위해 값이 적절한 상황에 제때 업데이트되어야 하며, 테이블 정렬이 항상 잘 되어 있어야 한다는 것입니다.
테이블 업데이트와 정렬 시점
테이블 업데이트와 정렬 시점은 두 가지로 나뉩니다.
첫째, 다른 룸에 있던 사용자가 해당 룸으로 이동하거나, '포커스 아웃(Focus Out)*' 상태에서 '포커스 인(Focus in)*' 될 때 Last Read Message ID를 -1로 서비스콜하여 업데이트합니다. 앞서 이야기한 것처럼 사용자가 해당 룸에서 메시지를 확인하고 있는 동안 Last Read Message ID는 DB에 -1로 표기되며, 프론트에서는 Infinity로 치환하여 가지고 있습니다.
* 사용자가 특정 화면이나 요소에 진입하여 포커스가 해당 요소로 이동되는 것을 의미합니다. 본문에서는 사용자가 채팅방으로 진입하여 그 방이 활성화되는 것을 뜻합니다.
* 사용자가 특정 화면이나 요소에서 벗어나 포커스가 다른 곳으로 이동하는 것을 의미합니다. 사용자가 채팅방을 떠나거나 화면에서 벗어나 비활성화되는 것을 뜻합니다.
둘째, 사용자가 해당 룸에서 다른 룸으로 이동하거나, 포커스 아웃(Focus Out) 될 때 Last Read Message ID를 해당 룸의 마지막 메시지 ID로
업데이트합니다.
데이터 업데이트 방식은 각 클라이언트가 처음으로 데이터를 가져오는 초기 1회만 테이블을 fetch한 후, 이후에 오는 메시지는 CMS 메시지를 받고
실시간으로 업데이트합니다. 즉, 한 번의 fetch 이후에는 DB에 업데이트된 데이터만 각 클라이언트에 반영해 최신 상태를 유지하는 것입니다.
채팅 시스템 메시지 읽음 처리 개선 효과
개선 전에는 100개의 메시지 읽음 처리를 위해서 100번의 서비스 콜이 필요하고, 이후 새로운 메시지가 n개 도착하면, 추가된 메시지를 처리하기 위해 n번의 서비스 콜이 필요했습니다.
메시지 읽음 처리 로직 개선 후에는 한 번의 요청으로 100개의 메시지 읽음 처리도 1번의 서비스 콜로 가능합니다. 이후 새로운 메시지 몇개가 도착해도 DB업데이트 없이 메시지 읽음 처리가 가능해졌습니다.
기존에는 서비스콜이 빈번하여 DB가 자주 과부하 되었지만, 지금은 각각의 클라이언트로 부하를 분산시켜 메신저의 효율성과 성능 향상까지 이루어졌습니다.
참고자료
임재환, 김창엽, 김해선, 이호용, 그룹채팅방의 메시지 읽음 여부를 판단하기 위한 방법 및 장치, 특허번호 KR102658902B1, 2023, 2024.
천홍석, 천영석, 이상휘, 박상운, 그룹 채팅 메시지 읽지 않은 사람 수 연산 방법, 이를 구현하기 위한 프로그램이 저장된 기록매체 및 이를 구현하기 위해 매체에 저장된 컴퓨터프로그램, 특허번호 KR101987171B1, 2017, 2019.
Writer 이호용 Editor 최윤영 Graphic Design 김선우
해당 콘텐츠는 저작권법에 의해 보호받는 저작물로 TmaxCoreAI에 저작권이 있습니다.
해당 콘텐츠의 사전 동의없는 2차 가공 및 영리적인 이용을 금하고 있습니다.