채팅방에서 읽지 않은 인원 수 계산하기-1

navyjeongs·2024년 1월 13일
3

리액트

목록 보기
6/8
post-thumbnail

2023년 부스트캠프에서 방탈출 프로젝트를 진행할 때 단체 채팅방 기능을 만들기로 했다.

프로젝트 : 부스트캠프 8기 Lock Festival

채팅에 대한 상세 스펙을 정할 때 팀원 모두 완성도 있는 채팅을 구현하길 원했다.
즉, 카카오톡처럼 읽지 않은 인원 수 표시, 연속된 채팅에서 첫 번째 채팅에서만 프로필을 보여주기 등등

따라서 우리는 채팅방에서 다음과 같은 기능을 구현하기로 했다.

  • 각 채팅에 대해 읽지 않은 인원 수 계산하기
  • 특정 사용자가 같은 시각에 2개 이상의 채팅을 연속해서 전송하면 첫 번째 채팅에만 프로필과 닉네임 표시하기
  • 특정 사용자가 같은 시각에 2개 이상의 채팅을 연속해서 전송하면 마지막 채팅에만 시각 표시하기
  • 유저가 입장, 퇴장하거나 강퇴를 당할 때 서버 메세지 표시하기
  • 특정 날짜의 첫 번째 채팅이면 날짜 구분선 표시하기

이번 포스팅에서는 첫 번째 상세 스펙인 각 채팅에 대해 읽지 않은 인원 수 계산하기에 대한 아이디어를 소개하고 다음 포스팅에 이어서 아이디어를 구현한 방법을 소개하겠다.

다른 기능들은 추후 다른 포스팅에서 소개하겠다.
나는 프론트엔드를 맡았기 때문에 서버와 DB 내용은 최소한으로 소개하겠다.

미리보기

아래와 같이 실시간으로 사용자가 접속하면 채팅 옆의 읽지 않은 인원 수가 변경되는 것을 확인할 수 있다!

읽지 않은 인원 수를 계산하는 단순한 방법

마땅한 방법이 떠오르지 않았을 때 우리는 구현하기 쉬운 가장 단순한 방법을 생각했다.

처음에 생각한 방법은 채팅 테이블에 대해 읽은 사람을 표시하는 속성을 추가하고 채팅을 읽을 때마다 읽은 채팅에 대해 전부 해당 속성을 업데이트하는 방법이다.

하지만 위의 방식은 엄청난 문제가 있는데 만약 유저 A가 1시간동안 채팅을 읽지 않았으며 1시간 동안 다른 사용자들은 100,000개의 채팅을 주고 받았다고 가정하자.

유저 A가 채팅방에 재접속라면 100,000개의 채팅 데이터에 대해 읽지 않은 인원 수를 계산해서 DB에 업데이트하고 접속하고 있는 클라이언트에게 알려줘야 한다.

logID채팅 내용읽은 사람
6578402a51a926a577b8cfa4안녕하세요.B,C
6578402a51a926a577b8cfb0새로왔어요!B
6578402a51a926a577b8cfcf잘 부탁해요B
... 중략
6578402a51a926a577bfffff100,000번째 채팅!B

즉, 위와 같은 데이터가 있다고 가정했을 때 A가 채팅방에 들어와 100,000개의 채팅을 읽는다면 읽은 사람을 아래와 같이 업데이트해야한다.

logID채팅 내용읽은 사람
6578402a51a926a577b8cfa4안녕하세요.A,B,C
6578402a51a926a577b8cfb0새로왔어요!A,B
6578402a51a926a577b8cfcf잘 부탁해요A,B
... 중략
6578402a51a926a577bfffff100,000번째 채팅!A,B

100,000개라면 문제가 없을 수도 있지만 만약 더 많은 채팅 데이터를 업데이트 해야하면 어떨까? 혹은 100,000개를 업데이트하는 단체 채팅방이 1,000개 라면??

우리는 DB에서 너무 많은 업데이트를 수행해야한다.

따라서 우리는 DB에서의 작업을 최소화하고 클라이언트 혹은 서버에서 계산할 수 있는 다른 방식을 생각해내야했다.

누가 읽었는지는 중요하지 않다.

앞서 우리는 카카오톡 처럼 읽지 않은 인원 수를 표시하기로 했다. 잘 생각해보면 각각의 채팅 입장에서는 누가 읽었는지는 중요하지 않다. 단순히 몇 명이 읽지 않았는지가 중요하다.

로그ID

아이디어를 소개하기 전에 로그ID에 대해 알고 가야한다. 보통 채팅 데이터는 주로 로그ID로 관리한다.

채팅 저장을 위해 데이터베이스는 Mongo DB를 사용했으며 Object ID를 log ID로 사용하였다.

Object ID는 16진수로 이루어져 있고 다음과 같이 3가지로 구성되어있는데 타임 스탬프와 생성 순서를 포함하고 있다.

즉, 17시 05분에 하나의 채팅 데이터를 생성하고 log ID로 Object ID를 부여하고 17시 15분에 하나의 채팅 데이터를 생성하면

17시 15분의 logID가 17시 05분의 logID보다 크다.

logID사이 대소 비교가 가능함을 알 수 있다.

만약 10분 간격으로 채팅을 친다면 아래와 같은 데이터가 생성될 것이다.
17:25의 로그ID가 나머지보다 큰 것을 확인할 수 있다.

logID채팅 내용시각
6578402a51a926a577b8cfa4안녕하세요.17:05
6578402a51a926a577b8cfb0새로왔어요!17:15
6578402a51a926a577b8cfcf잘 부탁해요17:25

채팅

유저 A가 17시 25분의 채팅을 읽었다면 그 이전의 채팅은 반드시 읽었을 것이다.
17시 05분의 채팅을 읽지 않고 17시 25분의 채팅을 읽을 수는 없다.

채팅 내용시각비고
잘 부탁해요17:25유저 A가 얘를 읽었다면
새로왔어요!17:15반드시 얘도 읽음
안녕하세요.17:05반드시 얘도 읽음

정리

즉, logID가 6578402a51a926a577b8cfaf인 채팅을 유저 A가 읽었다면

해당 채팅의 로그ID보다 작은 로그ID를 가지는 채팅(6578402a51a926a577b8cfaf보다 과거의 채팅)은 유저 A가 반드시 읽었을 것이다.

logID채팅 내용비고
6578402a51a926a577b8cfcf잘 부탁해요얘를 읽었다면
6578402a51a926a577b8cfb0새로왔어요!반드시 얘도 읽음
6578402a51a926a577b8cfa4안녕하세요.반드시 얘도 읽음

확장하면 유저 A가 logID가 6578402a51a926a577b8cfaf인 채팅을 읽었다면 해당 logID보다 작은 값을 가지는 채팅은 반드시 읽었다.

새로운 아이디어

앞서 말했듯이, 읽지 않은 인원 수를 표시할 때 각 채팅의 입장에서는 누가 읽었는지를 몰라도 된다. 단순히 몇 명이 읽지 않았는지가 필요하다.
몇 명이 읽지 않았는지 표시하기위해 각 유저가 마지막으로 읽은 채팅의 logID가 필요하다. 이 때, 현재 접속하고 있는 유저는 고려하지 않는다.

왜 각 유저가 마지막으로 읽은 채팅의 logID가 필요한지는 아래 예시를 통해 설명하겠다.

예시

아래와 같은 채팅을 주고받았다고 가정하자.
채팅옆 주황색 숫자는 읽지 않은 인원 수를 표시한 것이다.
채팅방 참여 인원은 총 3명으로 탈출장인(나), 어그로꾼, 부산탈출러다.

우선 위의 채팅을 분석하면 21시 01분의 그때봐요 채팅까지는 모두가 읽었으므로 읽지 않은 숫자가 0이 된다. 따라서 표시하지 않는다.

그 이후 부산탈출러가 채팅방을 껐다. 따라서 그때봐요 채팅 이후로는 어그로군과 탈출장인(나)이 읽었으므로 읽지 않은 숫자가 1이 된다.

그 이후 어그로꾼이 채팅을 3개 보내고 채팅방을 껐다. 저도 14일밖에 안되는데.. 채팅부터는 탈출장인(나)만 읽었으므로 읽지 않은 숫자가 2가 된다.

이를 정리하면 아래와 같은 표가 된다.

채팅 logID(앞 6자리만 표시)채팅 내용채팅 시각읽은 사람비고
...중략
657802알겠습니다.21:01:02탈출장인(나), 어그로꾼, 부산탈출러
65781a그때봐요21:01:07탈출장인(나), 어그로꾼, 부산탈출러부산탈출러 채팅방 off
65781c저 그날 안될 것 같아요21:01:16탈출장인(나), 어그로꾼
657921일이 있어서...21:01:20탈출장인(나), 어그로꾼
657925부산님 어디가셨징21:01:25탈출장인(나), 어그로꾼어그로꾼 채팅방 off
65807c저도 14일밖에 안되는데..21:01:30탈출장인(나)
65819021:01:42탈출장인(나)
6582aa다들 어디가셨징21:01:58탈출장인(나)

그럼 각 유저가 읽은 마지막 채팅의 logID는 다음과 같다.

유저 이름마지막에 읽은 채팅의 logID비고
부산탈출러65781a
어그로꾼657925
탈출장인(나)아직 접속중이므로 마지막에 읽은 채팅의 logID는 없다.

위의 자료를 활용해서 모든 채팅에 대한 읽지 않은 인원 수를 계산할 수 있다.

logID <= 65781a : 모두가 읽었으므로 읽지 않은 인원 수가 0이다.
65781a < logID <= 657925 : 탈출장인, 어그로꾼이 읽었으므로 읽지 않은 인원 수는 1(3-2)다.
657925 < logID : 탈출장인(나)만 읽었으므로 읽지 않은 인원 수는 2(3-1)다.

예외 상황

하지만 아직 고려하지 않은 것이 있는데 바로 유저들이 순차적으로 나가는 것이 아니라 동시에 나가는 경우다.

즉, 아래와 같이 부산탈출러의 그때봐요 채팅이후 부산탈출러와 어그로꾼이 동시에 채팅방을 나간 경우다.

이러면 한 명만 읽지 않은 채팅은 존재하지 않고 바로 2명이 읽지 않은 채팅이 된다.

다음 포스팅에서는 예외 상황 해결과 각 유저의 logID로 전체 채팅의 읽지 않은 인원 수를 구한 방법, 코드에 대해 포스팅하겠다.

profile
Front-End Developer

0개의 댓글