Foundry
실시간 채팅 시스템 설계
심화
핵심

접속 상태와 타이핑 표시

가장 잦은 쓰기이면서 가장 덜 중요하다

접속 상태는 요구사항 목록의 맨 끝에 붙는 기능인데 쓰기는 가장 많습니다. 메시지는 말할 때만 생기지만 접속 상태는 사람이 앱을 열고 닫을 때마다, 그리고 살아 있다고 알리는 신호마다 생깁니다.

그래서 이 기능은 정확도를 낮춰 비용을 낮추는 것이 정석입니다. 상대가 3초 전에 나갔는지 30초 전에 나갔는지는 아무도 신경 쓰지 않습니다.

살아 있다고 알리고, 조용하면 사라진다

접속 상태를 어딘가에 기록하면 그것을 지울 책임이 생깁니다. 앞 단계에서 본 대로 비정상 종료는 스스로 정리할 기회가 없습니다. 그래서 지우는 대신 만료를 씁니다.

30초마다 갱신하고 60초에 만료시키면 갱신을 한 번 놓쳐도 오프라인으로 뒤집히지 않는다 30초마다 갱신한다. 만료는 60초 여기서 죽는다 갱신이 없다 만료 0초 30 60 90 120 만료를 갱신 주기의 2배로 둔다 한 번 놓쳐도 오프라인으로 뒤집히지 않는다 비정상 종료도 시간이 대신 치운다

여기서 중요한 것은 만료를 갱신 주기의 2배로 두는 것입니다. 같게 두면 갱신이 몇 초 늦게 도착하는 순간마다 접속자가 오프라인으로 깜빡입니다. 여유를 두면 한 번 놓쳐도 상태가 뒤집히지 않습니다.

정하는 기준
갱신 주기짧으면 쓰기가 늘고 길면 사라진 것을 늦게 안다
만료 시간갱신 주기의 2배 이상. 한 번 놓치는 것을 견딘다

화면에 보이는 사람만 구독한다

접속 상태의 진짜 비용은 저장이 아니라 전파입니다. 상태 하나가 바뀔 때 그 사람을 보고 있는 모든 사람에게 알려야 합니다.

앱을 열 때 내가 속한 모든 채널의 참여자를 구독하면, 큰 채널에 든 사용자 한 명이 수만 건을 구독합니다. 그래서 지금 화면에 보이는 사람만 구독하고 화면을 벗어나면 해지합니다.

대화방 목록을 보고 있다: 목록에 보이는 상대만
대화방 안에 있다: 그 채널의 참여자 중 화면에 보이는 사람만
큰 채널: 접속 상태를 아예 보여주지 않는다

이 판단은 읽음 표시에서와 같습니다. 채널이 커지면 기능을 줄입니다.

한 사람이 여러 기기를 쓴다

기기마다 따로 신호를 보내므로 상태는 기기 단위로 저장하고, 사람의 상태는 하나라도 살아 있으면 접속 중으로 계산합니다.

기기 하나가 꺼졌다고 오프라인으로 바꾸면 컴퓨터를 닫는 순간 휴대폰을 쓰고 있는데도 오프라인이 됩니다.

타이핑 표시는 저장하지 않는다

타이핑 표시는 접속 상태보다 더 잦고 더 짧게 삽니다. 이것을 저장소에 쓰면 시스템에서 가장 많은 쓰기가 되는데, 3초 뒤에는 아무 의미가 없는 데이터입니다.

성격처리
몇 초만 유효하다저장하지 않고 전달만 한다
잃어도 문제가 없다재시도하지 않는다
매 글자마다 생긴다클라이언트가 몇 초에 한 번으로 줄여 보낸다

잃어도 되는 데이터는 잃어도 되게 다룹니다. 모든 데이터를 같은 무게로 다루면 정작 중요한 메시지 저장이 느려집니다.

마지막 접속 시각은 조심해서 다룬다

"3분 전 접속" 같은 표시는 상대의 생활 패턴을 노출합니다. 끄는 설정을 주고, 끈 사람은 남의 것도 볼 수 없게 합니다. 읽음 표시와 같은 규칙입니다.

면접에서 이렇게 나옵니다

Q.접속 상태를 어디에 어떻게 저장하시겠습니까

만료가 있는 캐시에 두고, 살아 있는 동안 주기적으로 갱신합니다.

30초마다 자기 항목을 갱신한다
갱신이 멈추면 60초에 만료된다

핵심은 만료를 갱신 주기의 2배로 두는 것입니다. 같게 두면 갱신이 조금 늦는 순간마다 접속자가 오프라인으로 깜빡입니다.

접속 상태는 자주 바뀌고 짧게 사는 데이터입니다. 영구 저장소에 두면 쓰기 부하만 늘고, 비정상 종료로 남은 값을 치울 방법이 사라집니다.

흔한 실수: 종료할 때 자기 항목을 지우는 처리만 두는 것. 정상 종료만 덮습니다. 프로세스가 갑자기 죽거나 네트워크가 끊기는 경우가 훨씬 흔하고, 그때는 시간이 대신 치우게 만들어야 합니다.

Q.접속 상태 전파 비용을 어떻게 줄이나요

지금 화면에 보이는 사람만 구독하고, 화면을 벗어나면 해지합니다.

진짜 비용은 저장이 아니라 전파입니다. 상태 하나가 바뀔 때 그 사람을 보고 있는 모든 사람에게 알려야 합니다.

대화방 목록: 목록에 보이는 상대만
대화방 안: 화면에 보이는 참여자만
큰 채널: 접속 상태를 보여주지 않는다

내가 속한 모든 채널의 참여자를 구독하면 큰 채널에 든 사용자 한 명이 수만 건을 구독합니다. 화면 단위로 좁히면 수십 건입니다.

흔한 실수: 구독 등록을 큐로 나눠 천천히 처리하겠다고 답하는 것. 등록이 몰리는 것은 완화되지만 유지해야 하는 구독 수와 전파 총량은 그대로입니다. 줄여야 할 것은 속도가 아니라 개수입니다.

Q.한 사람이 여러 기기를 쓰면 접속 상태를 어떻게 계산하나요

기기 단위로 저장하고, 하나라도 살아 있으면 접속 중으로 봅니다.

기기마다 따로 갱신 신호를 보내므로 항목도 기기마다 둡니다. 사람의 상태는 그 항목들을 모아 판단합니다.

기기 3대 중 1대라도 갱신 중이면 접속 중
모두 만료되면 오프라인

기기 하나가 꺼질 때 사람을 오프라인으로 바꾸면, 컴퓨터를 닫는 순간 휴대폰을 쓰고 있는데도 오프라인이 됩니다.

흔한 실수: 새 기기가 접속할 때 이전 기기의 항목을 지우는 것. 한 사람에 하나만 두려는 생각인데, 그러면 두 기기를 함께 쓸 때 서로를 밀어내며 상태가 계속 뒤바뀝니다. 알림도 마지막 기기에만 가서 놓치게 됩니다.

Q.타이핑 표시는 어떻게 구현하나요

저장하지 않고 전달만 합니다. 재시도도 하지 않습니다.

타이핑 표시는 몇 초만 유효하고 잃어도 아무 문제가 없습니다. 저장소에 쓰면 시스템에서 가장 많은 쓰기가 되는데, 3초 뒤에는 의미가 없는 데이터입니다.

성격처리
몇 초만 유효하다저장하지 않고 전달만
잃어도 된다재시도하지 않는다
매 글자마다 생긴다몇 초에 한 번으로 줄여 보낸다

잃어도 되는 데이터는 잃어도 되게 다룹니다. 모든 데이터를 같은 무게로 다루면 정작 중요한 메시지 저장이 느려집니다.

흔한 실수: 타이핑 표시를 메시지와 같은 경로로 보내는 것. 순서 보장과 저장이 붙은 경로에 태우면 잃어도 되는 데이터가 파티션의 처리량을 잡아먹습니다. 보장이 필요 없는 것은 보장이 없는 경로로 보냅니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

실시간 채팅 시스템 설계 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.