LINE Corporation이 2023년 10월 1일부로 LY Corporation이 되었습니다. LY Corporation의 새로운 기술 블로그를 소개합니다. LY Corporation Tech Blog

Blog


독립적이고 범용적인 올인원 메시징 플랫폼

Tech-Verse 2022에서 송재욱 님이 발표한 독립적이고 범용적인 올인원(all-in-one) 메시징 플랫폼 세션 내용을 옮긴 글입니다.

안녕하세요. ABC Studio에서 MessagingHub를 만들고 있는 송재욱입니다. 이번 글에서는 어떤 과정을 거쳐서 독립적이고 범용적인 올인원(all-in-one) 메시징 플랫폼을 만들어 갔는지 소개하겠습니다. 

먼저 MessagingHub가 무엇인지 소개하고, 'Messaging as a Service'에 대해 플랫폼 관점에서 말씀드린 뒤 MessagingHub의 주요 아키텍처와 흐름을 살펴보겠습니다.

MessagingHub 소개

MessagingHub란 무엇이고, 왜 만들었고, 어디에 사용하는 것일까요? 이 글의 제목인 '독립적이고 범용적인 올인원 메시징 플랫폼'은 바로 MessagingHub를 가리키는 말입니다. MessagingHub는 이름에서 알 수 있듯 메시징에 초점을 둔 플랫폼으로, 독립적이고 범용적이라는 핵심 키워드를 만족하면서, 필요한 구성을 전부 MessagingHub 프로젝트에서 올인원으로 관리하고 있는 플랫폼입니다.

MessagingHub는 크게 두 가지 일을 합니다. 하나는 서버에서 클라이언트로 메시지를 전달하는 것이고, 다른 하나는 클라이언트 간에 메시지를 전달하는 것입니다.

MessagingHub는 총 5가지 방식으로 클라이언트로 메시지를 전달합니다. 서버 푸시와 앱 푸시, 이메일, SMS, 채팅입니다. 

그런데 MessagingHub와 연동하는 서비스는 이 기능을 항상, 모두, 다 써야 하는 것일까요? 그렇지 않습니다. 대부분은 각 도메인 상황에 맞게 특정 몇 개 기능만 사용합니다. MessagingHub와 연동하는 서비스 입장에서 생각해 본다면 메시징과 관련된 여러 기능이 툴 박스 형태로 제공되는 것입니다. 툴 박스에 있는 기능 단위 아이템 중 프로덕트를 만들기 위해 필요한 아이템들만 각 도메인의 요구 사항에 맞게 선택해서 활용할 수 있습니다.

MessagingHub 탄생 배경, 폴링에서 푸시로

MessagingHub가 탄생한 배경을 말씀드리기 위해 전형적인 폴링(polling) 구조를 생각해 보겠습니다. 아래는 클라이언트가 서버에서 갱신된 정보를 얻기 위해 주기적으로 폴링을 하는 상황을 나타낸 그림입니다.

만약 대부분의 폴링 응답이 'No Change'인 의미 없는 호출이 반복되는 상황에서 클라이언트 숫자가 가파르게 늘어난다면 어떻게 될까요? 수백, 수천, 수만 개의 클라이언트가 혹시 있을지 모르는 변경 데이터를 직접 확인하기 위해서 폴링하는 것은 서비스가 성장할수록 점점 더 무거운 기술 부채가 됩니다. 불필요한 트래픽은 네트워크 패킷을 낭비하고, 시스템 과부하를 초래하며, 일종의 셀프 DDoS 상황을 초래하기 때문입니다. 서버를 증설하고 DB를 분산시키는 등의 작업으로 대응할 수도 있겠지만, 이는 근본적인 해결책은 아닙니다. 불필요한 트래픽은 여전히 발생하고 있을 것이기 때문입니다.

이 문제는 구조적으로 접근해야 하는 문제로 서버에서 트리거된 이벤트를 푸시로 보내는 방식으로 변경하면 개선할 수 있습니다. 서버에서 적재적소에 필요한 정보를 푸시로 보내면 불필요한 트래픽을 제거할 수 있고, 이를 통해 연쇄적으로 발생했던 문제 상황도 회피할 수 있습니다.

MessagingHub는 이와 같이 폴링을 푸시로 전환하기 위해 시작한 프로젝트입니다. 여기에 다양한 메시징 관련 요구 사항을 수용하며 기능을 확장해 나가고 있습니다.

MessagingHub 구축 방법론

이와 같은 구조적인 기술 부채를 갚기 위한 방법론은 여러 가지가 있습니다. 유지 보수를 하면서 조금씩 개선하는 방법도 있고, 처음부터 완전히 새로 만드는 방법도 있으며, 공통분모를 별도 도메인으로 분리해서 이를 기반 기술 시스템으로 전환하는 방법도 있습니다.

MessagingHub는 세 번째 방법인 기반 기술로 전환하는 방법을 선택했습니다. 이때 여러 시스템이 데이터를 중심으로 결합도가 매우 높다면 그 커플링을 느슨하게 만들어야 하고, 공통 기능을 별도 도메인으로 분리하고 격리해서 데이터 의존도와 복잡도를 해소하고 독립된 도메인에 집중할 수 있는 환경을 마련해야 합니다.

메시지를 공급지에서 도착지로 보낸다는 공통분모를 별도 도메인으로 분리한 MessagingHub는, 독립적으로 작동할 수 있도록 연동 서비스에 대한 도메인 지식을 넣지 않았고, 여러 도메인에서 범용적으로 사용할 수 있도록 메시징 처리 역할을 쉽게 위임할 수 있게 만들었습니다.

MessagingHub 현재 지표

이렇게 탄생한 MessagingHub는 현재 일본의 No.1 음식 배달 서비스인 Demaecan의 기반 기술로 사용하고 있습니다. Demaecan 프로덕트에는 아래와 같이 여러 도메인이 있는데요. 현재 Delivery와 Merchant 도메인에서 사용하고 있으며, 향후 User와 CS, Retail 같은 여러 도메인으로 접점을 넓혀갈 계획입니다.

Demaecan에서 연동해 사용하고 있는 MessagingHub의 지표를 간략히 살펴보겠습니다.

하루 기준으로 피크 타임 동시 접속자 수는 약 5만 정도이고, 메시지 발송량은 150만에서 200만 건 정도이며, 서버 푸시 처리 속도는 약 150ms 내외를 유지하고 있습니다. 앞서 말씀드렸듯 도메인 접점을 넓혀갈 예정이므로 추후 더 많은 트래픽을 처리할 것으로 기대하고 있습니다.

MessagingHub를 플랫폼으로 만든 이유와 플랫폼의 조건

MessagingHub는 'Messaging as a Service 플랫폼'입니다. MessagingHub가 왜 플랫폼이어야 하는지 그 이유와 조건을 살펴보겠습니다.

MessagingHub가 플랫폼이어야 하는 이유

하나의 프로덕트는 각 도메인을 맡고 있는 여러 프로젝트로 얽혀 있습니다.

프로덕트 규모가 커질수록 각 프로젝트에서 같은 요구 사항으로 같은 개발을 각각 진행하는 사례가 발생합니다.

이는 각 도메인의 핵심 비즈니스와 섞이면서 향후 생산성을 저해하는 요소가 되기 쉽습니다. 시간이 지나면서 유지 보수나 커뮤니케이션은 물론 서비스 안정성을 유지하는 것도 어렵게 만들며, 자연스럽게 개발 생산성이나 서비스 신뢰도에도 영향을 미칩니다.

결국 잦은 실수를 유발하며 시스템 운영 난도를 높이고, 급기야 에러나 시스템 장애로 문제가 번지기도 합니다. 

이럴 때 관련 전문 처리를 위임해서 중복 작업을 줄이기 위한 공통 플랫폼이 필요한데요. 만약 그 대상이 1:1 관계로 단순히 기능을 분리하거나 확장하는 것이라면 굳이 플랫폼일 필요는 없습니다. 오버 엔지니어링이 될 수 있기 때문입니다.

그런데 이 말은 만약 여러 도메인을 위한 것이라면 반드시 플랫폼의 특성이 필요하다는 말과 같습니다. MessagingHub는 여러 도메인에서 활용해야 하는 N:1 관계였고, 이에 따라 플랫폼의 특성이 필요했습니다.

제가 Demaecan 서비스에 합류한 뒤 이 프로덕트에서 꼭 개선해야 한다고 판단한 것은 각 도메인에서 공통 관심사를 분리해야 한다는 것이었으며, 그중 하나가 메시징 처리였습니다. 각 도메인의 메시징 처리를 MessagingHub로 위임해서 각자 자신의 비즈니스에 더 집중할 수 있는 구조로 만들고, 이를 통해 비즈니스 몰입감을 높여 더 나은 서비스를 만들어 내는 원동력이 되기를 바랐습니다.

플랫폼의 조건

플랫폼을 만들기 위해서는 할 수 있는 것과 할 수 없는 것을 명확히 구분해야 합니다. 이를 통해 플랫폼을 사용하기 위한 기준을 정하고, 플랫폼의 의도를 명확히 할 수 있습니다. 특히 MessagingHub에서는 할 수 없는 것을 정의하는 것이 매우 중요했습니다. 독립적이고 범용적인 시스템으로 만들기 위해서 반드시 지켜야 하는 요소였는데요. 특정 도메인에 종속되지 않아야 할 뿐 아니라, 세부 비즈니스 로직을 알아서도 안 됩니다. 한 마디로 연동하는 서비스의 도메인 지식에 관여하지 않는 것이라고 정리할 수 있습니다.

예를 들어 보겠습니다. 아래 그림은 클라이언트가 MessagingHub와 연결하는 과정을 나타낸 것입니다. MessagingHub는 클라이언트가 어떤 도메인인지, 혹은 어떤 서비스인지 관심이 없습니다. 다른 말로 클라이언트가 누구든 상관없습니다. MessagingHub와 연결하는 클라이언트는 애초에 MessagingHub를 위한 클라이언트가 아니기 때문입니다.

따라서 클라이언트는 본래 서비스를 위해 연동하고 있던 서버 시스템을 경유해서 MessagingHub와 연결하기 위한 인증 토큰을 발급받습니다. 이 토큰을 통해서 MessagingHub와 연결하는 것입니다. 클라이언트가 누구인지는 연동 서버가 잘 알고 있고 또한 검증할 수 있기에 MessagingHub는 연동하는 서버 시스템과 통신하는 것으로 클라이언트의 인증 유효성을 처리합니다.

서버도 마찬가지입니다. 누구든 상관없습니다. 앞서와 마찬가지로 MessagingHub는 연동되는 도메인 지식에 관심이 없기 때문입니다. MessagingHub는 연동되는 외부 시스템을 위해서 라이브러리를 제공하는데요. 이때 서버에서 전달하는 메시지 포맷은 다음과 같습니다.

주요 필드 두 가지만 살펴보겠습니다. 먼저 targets은 메시지를 수신할 클라이언트 대상입니다. 이 대상은 호출 측에서 지정합니다. 즉 MessagingHub가 메시지 수신 대상을 결정하지 않습니다. 다음으로 payload는 클라이언트에게 전달하는 메시지의 내용입니다. 이 값은 Stringify JSON 형태로 들어오는데요. MessagingHub는 일절 이 내용에 관여하지 않고 클라이언트에게 언래핑(unwrapping)해서 전달합니다. 즉 특정 클라이언트에게 어떤 메시지를 보낼지는 연동 서비스에서 결정하는 것입니다.

이와 같이 MessagingHub는 서버와 클라이언트의 도메인 지식에 관계없이 사용할 수 있습니다. MessagingHub가 연동 서비스의 사용자 정보나 도메인 정보를 모른다는 것은 독립성을 보여주는 지표이자, MessagingHub가 제공하는 흐름과 규약을 이용하면 어떤 서비스든 상관없이 연동할 수 있다는 범용성을 나타내는 지표라고 할 수 있습니다.

독립성과 범용성을 지키기 위한 일반화

MessagingHub와 같은 플랫폼 아키텍처를 설계하고 개발할 때에는 일반화하는 것이 매우 중요합니다. 일반화는 독립성을 훼손하지 않으면서 범용성을 넓혀가는 방법이기 때문입니다. 따라서 특정 도메인의 요구 사항이 들어왔을 때 독립성과 범용성의 가치를 지키기 위해서 요구 사항을 일반화하기 위해 힘쓴 뒤 그 결과를 MessagingHub에 반영해 나가고 있습니다.

예를 들어 음식 배달 도메인에서 채팅을 구현할 때에는 특수한 요구 사항이 있습니다. 드라이버와 사용자 간의 채팅을 생각해 보겠습니다. 우리가 일반적으로 알고 있는 채팅에서는 친구 관계라는 게 있어서 상대를 알고 있는 사람이 채팅방을 만드는데요. 드라이버와 사용자 간에 채팅을 해야 한다면 채팅방 생성 주체는 누가 돼야 할까요?

만약 드라이버가 채팅방을 만들어야 한다면 드라이버가 사용자 정보를 알 수 있어야 하며, 드라이버가 채팅방을 직접 만들어야 하기 때문에 배달 외에 해야 할 일이 늘어납니다. 반대로 사용자는 배달하는 드라이버가 누구인지 당연히 모르기 때문에 사용자가 먼저 드라이버에게 문의하고 싶은 상황이 발생했을 때 이를 처리하는 것이 곤란해집니다.

또한 생성한 채팅방은 계속 유지하는 것이 아니라 배달이 종료되면 자동으로 사라지도록 만들어야 하며, 메시지를 직접 입력하는 것을 막고 미리 설정된 메시지만 선택해서 보낼 수 있어야 한다는 요구 사항이 들어올 수도 있습니다. 이처럼 일반적인 패턴이 아닌 요구 사항에는 어떻게 대응해야 할까요?

이와 같은 요구 사항을 해결하기 위해서 MessagingHub에 채팅방의 생명 주기나 선택형 메시지를 관리할 수 있는 옵션을 추가했습니다. 다행히 MessagingHub는 외부 서버의 트리거를 받을 수 있는 구조로 이미 도메인이 분리돼 있었고, 아래와 같은 흐름이 생겼습니다.

여기서 한 가지 의문이 생긴 분들이 계실 겁니다. 앞서 MessagingHub는 연동하는 서비스의 도메인 지식에 관여하지 않는다고 말씀드렸는데요. 지금 살펴본 것은 음식 배달 도메인에 종속되는 요구 사항이기에 특정 도메인 지식에 관여한 것이 아니냐고 생각하실 수 있습니다.

하지만 채팅 구조 또한 도메인 지식에 관계없이 작동할 수 있도록 일반화해서 설계했습니다. 아래와 같이 일반적으로 우리에게 익숙한 채팅의 유스 케이스를 먼저 정리해서 개발한 뒤, 위와 같은 특이 케이스를 개별적으로 분리해서 개발했습니다.

MessgingHub 아키텍처 소개

다음으로 MessagingHub 아키텍처의 모듈 구성과 메시지 전달 흐름, 인프라 구성 기술을 살펴보겠습니다.

모듈 구성

MessagingHub 아키텍처는 프로젝트가 진행되는 과정에서 꽤 많은 변화를 거쳐왔습니다. 최초에는 하나의 모듈이었지만 도메인의 역할과 목적에 따라서 쪼개지고 합쳐지는 과정을 거쳐 현재는 앞서 보신 것처럼 여러 모듈로 구성된 도메인이 존재하는데요. MessagingHub의 모듈이 어떻게 구성돼 있는지 살펴보겠습니다.

Message-router는 외부 연동 시스템으로부터 메시지를 공급받아 비즈니스 요건에 따라서 적절히 라우팅합니다.

Connection-manager는 클라이언트와 연결되는 웹소켓 연결을 관리하고 서버 푸시를 처리합니다.

Notification-app은 서버 푸시를 제외한 서드 파티 알림 채널로 메시지를 전달하는 역할을 담당합니다. 현재 FCM(Firebase Cloud Messaging)와 SMS, 이메일을 전달할 수 있습니다.

Message-app은 연동 시스템에서 클라이언트로 전달되는 메시지 데이터를 관리합니다. 관리 항목에는 발송 결과 정보가 포함돼 있습니다.

Ack-receiver는 메시지가 클라이언트에게 안전하게 전달됐는지 파악하기 위해서 클라이언트로부터 메시지 수신 콜백을 받는 역할을 담당합니다.

Consumer-proxy는 Kafka의 consumer-client를 별도 레이어로 분리하기 위해 추가했습니다. 이렇게 분리하면 두 가지 이점을 얻을 수 있습니다. 첫 번째는 비즈니스 로직 변경으로 배포할 때 컨슈머 리밸런싱 이슈를 해소할 수 있습니다. 두 번째는 Kafka 토픽의 파티션 수로 결정되는 스케일 아웃 한계를 극복할 수 있습니다.

Chat-app은 채팅 비즈니스 로직을 처리하고, 그 데이터를 관리합니다.

Operation-app은 서비스 운영 관련 처리를 담당합니다.

그 밖에 모니터링과 경고 알림, 통합 테스트 등을 위해서 다음과 같은 서드 파티 솔루션을 사용하고 있습니다.

라이브러리

서비스나 프로젝트는 애초에 변화라는 속성을 내포하고 있습니다. 그 변화를 예측하고 영향을 최소화해서 효율을 높이기 위한 방법 중 하나가 라이브러리입니다. MessagingHub는 내부적으로 세 개의 라이브러리를 만들어서 사용하고 있고, 외부 서버와 연동하기 위해서 하나의 라이브러리를 제공하고 있습니다.

MessagingHub의 아키텍처는 그동안 많은 변화를 거쳐왔다고 말씀드렸는데요. 보일러 플레이트 코드를 줄이기 위해 만들어 온 라이브러리를 통해 생산성을 높일 수 있었습니다. 기본적으로 아래와 같이 팩토리와 로그, 보안, 설정 등 여러 모듈에서 횡적인 관심사가 되는 항목을 라이브러리 요소로 만들고 있습니다. 

메시지 전달 흐름 

기본적인 시스템 구성을 살펴봤으니 이제 메시지가 어떻게 흘러가는지 살펴보겠습니다.

외부 연동 시스템에서는 MessagingHub에서 제공하는 외부 라이브러리를 이용해 message-router에 메시지를 전달합니다. Message-router는 메시지를 전달받아 Kafka에 큐잉한 뒤, 메시지 UUID를 호출 측에 즉각 반환합니다. 이는 대용량 트래픽을 처리하면서 메시지 처리 과정에서 message-router의 처리 지연이 호출 측의 처리 지연으로 전파되지 않도록 하기 위함입니다. 다음으로 클라이언트의 연결 상태에 따라서 서버 푸시할 수 있는 connection-manager로 보낼지, 아니면 앱 푸시할 수 있는 notification-app으로 보낼지 판단하고 라우팅합니다.

클라이언트가 connection-manager와 연결돼 있다면 메시지를 서버 푸시합니다. 연결돼 있지 않다면 notification-app에서 앱 푸시로 처리합니다. 이때 message-router가 connection-manager로 메시지를 라우팅하는 그 짧은 시간에 클라이언트 연결이 끊기거나 서버 푸시가 실패할 수 있는데요. 이런 경우 connection-manager에서 폴백(fallback) 처리해서 notification-app이 처리할 수 있도록 메시지를 보내줄 수 있습니다.

Message-app은 흘러가는 메시지의 상태와 결과를 보관합니다. 호출 측에서는 message-router가 발급해 준 UUID를 이용해 메시지 발송 상태를 확인할 수 있으며, 클라이언트가 메시지를 수신하면 그 정보를 ack-receiver로 알려주기 때문에 메시지 도달률도 확인할 수 있습니다.

웹소켓에 연결된 클라이언트가 아니라 FCM이나 SMS, 이메일과 같은 알림 채널로 직접 메시지를 전달하고 싶을 때도 있을 텐데요. MessagingHub와 연동하고 있는 외부 시스템은 외부 라이브러리에 정의돼 있는 메서드를 이용해서 앞서와 마찬가지로 데이터만 채워서 message-router로 메시지를 전달하면 됩니다.

채팅은 최근에 나온 요구 사항입니다. MessagingHub는 클라이언트와 웹소켓으로 연결하는 것이 기본 스펙이라서 채팅 관련 요구 사항도 잘 처리할 수 있습니다. 기본적으로 채팅은 클라이언트가 통신을 지원하는 것이기 때문에 connection-manager 뒷단에 채팅 비즈니스 로직을 담당하는 chat-app을 추가했습니다.

인프라 구성 기술

MessagingHub에서 사용하고 있는 주요 인프라 기술은 다음과 같습니다.

코드가 푸시되면 CI(Continuous Integration) 툴이 정적 분석을 진행하며, 필요하면 라이브러리를 빌드하고 배포합니다. 이미지가 레지스트리에 등록되면 CD(Continuous Delivery) 툴이 각 단계별로 순차 배포합니다. 컨테이너 오케스트레이션은 쿠버네티스를 사용하며, 쿠버네티스의 각 오브젝트는 Kustomize를 이용해서 최대한 코드 베이스로 관리합니다. 또한 로컬 환경에서 MessagingHub 전체를 구동하고 테스트할 수 있도록 Docker Compose로 인프라 구성을 관리하고 있습니다.

안정적인 메시징 시스템으로 만들기 위해

다음으로 MessagingHub를 안정적이고 신뢰할 수 있는 시스템으로 만들기 위해 어떤 부분에 신경을 쓰고 있는지 말씀드리겠습니다. 여기서 소개하는 내용은 비단 MessagingHub에 한정되는 것이 아니라 연결 기반으로 메시지를 처리하는 메시징 시스템에 공통으로 해당되는 내용이라고 생각합니다.

연결 관리

MessagingHub는 클라이언트와 웹소켓으로 연결하기 때문에 연결 관리가 매우 중요합니다. 아래 그림은 연결 관리를 위해서 connection-manager와 message-router, operation-app이 서로 어떻게 협력하며 관계를 맺고 있는지 보여줍니다. 세부 내용보다는 큰 관점에서 말씀드리겠습니다.

기본적으로는 connection-manager가 사용자 연결을 관리하고 담당하지만, 사용자 연결 정보는 message-router에서도 알아야 하는 정보입니다. 메시지를 정확한 위치로 라우팅하기 위해서는 타깃 사용자가 현재 연결된 상태인지 알아야 하고, 클라이언트가 connection-manager의 어떤 서버에 연결됐는지도 알아야 하기 때문입니다. 따라서 연결 정보를 공유하기 위해서 별도 스토리지를 이용해 동기화하고 있습니다.

또한 message-router와 connection-manager 간 서버 통신은 소켓을 사용하고 있는데요. 서버 간 소켓 연결과 갱신 정보도 스토리지에서 관리하며, 변경되는 서버의 이벤트는 Kafka 이벤트로 처리하고 있습니다.

메시지 수신 확인 처리

MessagingHub는 메시지가 클라이언트까지 안전하게 도달했는지 자체적으로 알 수 없습니다. 서버 푸시는 I/O 버퍼로 메시지를 플러시(flush)했다는 것까지만 알 수 있고, 앱 푸시는 서드 파티인 FCM에서 요청을 잘 받았다는 응답까지만 받을 수 있습니다. 따라서 실제로 메시지가 클라이언트까지 도달했는지 확인할 수 있는 자체 흐름이 필요했고, 아래와 같은 흐름을 추가했습니다.

메시지 내용에 클라이언트가 제대로 수신했다고 MessagingHub에 알려줄 수 있는 ACK 정보를 포함시켰습니다. 메시지를 수신한 클라이언트는 해당 ACK 정보를 포함해서 MessagingHub로 알려줍니다. 이와 같은 수신 확인 데이터를 활용해 누락된 수신 메시지를 폴백 처리하는 것이 가능해졌습니다.

클라이언트 연결 확인 처리

클라이언트의 네트워크 환경에 따라 실제로는 연결이 끊겼지만 MessagingHub는 클라이언트의 연결이 끊겼다는 사실을 알지 못하는 상황이 발생할 수 있습니다. 네트워크 환경이 고르지 못한 경우에 발생할 수 있는데요. 쉬운 예로 스마트폰을 비행기 모드로 전환하면 이 상황을 재현할 수 있습니다.

이때에는 클라이언트가 소켓 연결을 정상적으로 닫지(close) 않기 때문에 일종의 half-open 상태가 발생합니다. 이 상황에서 MessagingHub는 클라이언트의 연결이 끊겼다는 사실을 알 수 없기 때문에 I/O 버퍼로 메시지를 플러시한 것으로 메시지를 성공적으로 보냈다고 판단하지만, 실제로 클라이언트는 연결이 끊긴 상태이기 때문에 메시지를 받을 수 없습니다.

이 문제를 해결하는 방법은 MessagingHub와 클라이언트가 주기적으로 핑퐁(PING-PONG)을 주고받으며 연결 상태를 확인하는 것입니다. 아래와 같이 MessagingHub가 주기적으로 PING을 보내면 클라이언트는 PONG을 응답합니다. 만약 특정 시간 내에 클라이언트가 응답하지 않으면 그 연결을 서버에서 직접 종료합니다.

위 그림에서 PONG_ACK는 클라이언트 PONG에 대한 MessagingHub의 ACK입니다. PONG_ACK를 추가한 이유는 클라이언트도 ACK를 받지 못했을 때 자체적으로 재연결을 시도할 수 있도록 하기 위함입니다. 앞서 말씀드린 메시지 수신 확인 처리와 클라이언트 연결 확인 처리를 활용하면 보다 더 안정적이고 신뢰할 수 있는 메시지 처리가 가능합니다.

그런데 만약에 클라이언트가 MessagingHub와 연결돼 있지 않고, 앱 푸시 수신도 꺼놨으며, 심지어 오프라인 상태라면 그 시점에 전달해야 하는 메시지는 어떻게 해야 할까요? 클라이언트에게 전달되지 않은 채로 그대로 남겨둬야 할까요?

웹 표준 개발 기구인 W3C에서는 이런 상황에 대해 '사용자 에이전트가 일시적으로 오프라인일 때도 푸시 메시지를 보낼 수 있어야 한다'고 말하고 있으며, '이를 지원하기 위해서 사용자 에이전트가 온라인 상태가 될 때까지 그 메시지를 보관하고 있어야 한다'고 가이드하고 있습니다. 즉 이런 상황에서도 클라이언트에게 메시지를 전달할 방법이 있어야 한다는 것입니다. 

이를 위해 MessagingHub는 클라이언트에게 전달하는 메시지를 일정 기간 보관하고 있습니다. 클라이언트가 온라인 상태로 돌아와서 다시 MessagingHub와 연결되면 최근 메시지를 가져가면서 오프라인 상태였던 동안에 받지 못했던 메시지를 수신할 수 있으며, 혹은 앞서 말씀드렸던 메시지 수신 확인 처리 결과에 따라 MessagingHub가 클라이언트에게 능동적으로 메시지를 전달하는 것도 가능합니다.

마치며

MessagingHub에서는 사용처를 다변화하고 넓히는 것을 다음 계획으로 잡고 있습니다. 더 많은 개발자와 더 많은 서비스 및 도메인에서 MessagingHub를 사용하길 바라고 있으며, 그 목표를 이루기 위해 더 쉽고 강력한 시스템을 만들기 위해 노력하고 있습니다.

이번 글을 읽으며 MessagingHub에 관심이 생겼다면 제가 쓴 아래 MessagingHub 관련 기술 블로그 세 편도 함께 읽어보시면 좋을 것 같습니다. 긴 글 읽어주셔서 감사합니다.