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

Blog


메시징 시스템(a.k.a messaging-hub) 톺아보기

지난 글, 기술 부채를 갚기 위한 첫 발을 떼기까지에 이어 이번 글에서는 messaging-hub의 아키텍처가 진화해 나간 과정과 세부적인 구성 및 처리 흐름에 대해서 공유하려고 합니다.

아키텍처: v0.1에서 v1.0까지의 변화 과정

messaging-hub의 시스템 아키텍처가 어떤 과정을 거쳐 현재의 모습이 됐는지 단계별로 설명하겠습니다.

v0.1

이 시점에서는 프로젝트를 'connection system'이라고 불렀습니다.

아키텍처

매우 간단한 구조지만, 가장 핵심이 되는 도메인이고, 앞으로 이 구조를 기반으로 뻗어 나가게 됩니다.

아키텍처 구성을 살펴보겠습니다. connection-manager는 클라이언트와 웹소켓으로 연결되는 커넥션을 관리하고, 메시지 소싱과 라우팅, 서버 푸시를 담당합니다. 특이한 점은, connection-manager를 스케일 아웃하면 클라이언트가 어느 connection-manager로 연결됐는지 알 수 없기 때문에 외부 시스템에서 소싱된 메시지의 타깃을 connection-manager 내부에서 라우팅해야 합니다. 연결 정보는 Redis에 싱크를 맞춥니다. 이 정보를 바탕으로 라우팅 지점을 판단한 뒤 메시지를 포워딩합니다. 이를 위해 connection-manager 간에도 연결 상태를 지닌 메시(mesh) 구조가 됩니다. 뒤쪽에 위치한 driver-app과 order-app 등은 데이터 기반의 비즈니스 로직 처리와 데이터 관리를 위한 서버 군으로 외부 연동 시스템의 도메인별로 WAS를 분리했습니다. 도메인 간 통신은 gRPC를 사용했습니다.

요구 사항

시스템 아키텍처의 버전별로 요구 사항이 발생했는데요. 이 요구 사항을 반영한 것이 다음 버전이 됩니다. v0.1에 대한 요구 사항은 아래와 같았습니다.

  • 메시지 소싱을 대용량으로 처리할 수 있어야 한다(메시지 소싱: 외부 시스템에서 메시지를 전달하는 것을 messaging-hub 관점에서 해석한 용어).
  • 커넥션 관리의 허점을 보완해야 한다.
  • 클라이언트가 웹소켓으로 연결되지 않은 상황에서는 앱 푸시(a.k.a FCM)를 보낼 수 있어야 한다.
  • 배포 없이 운영 중에 설정을 동적으로 변경할 수 있으면 좋겠다.

v0.3

이 시점에는 프로젝트 이름이 'connection system'에서 'messaging system'으로 바뀝니다. 

아키텍처

아래 그림에서 'EXTERNAL SYSTEM'(이하 외부 시스템)은 Demaecan 시스템을 가리키며, 메시징 허브와 연동하기 위한 시스템입니다.

  • 외부 시스템에서 소싱되는 메시지 양이 많을 것이라고 예상했기 때문에 일반 Web API가 아닌 gRPC 통신을 선택했습니다.
  • 커넥션 관리에 허점이 발생할 수 있는 점을 보완하기 위해 async-app을 통해 싱크를 체크하고 맞추는 비동기 스케줄러를 등록했습니다. 
  • 클라이언트와 웹소켓 연결이 끊긴 상황에서는 FCM을 통해 앱 푸시로 전환할 수 있도록 push-app을 추가했습니다.
  • 리모트 설정이 가능한 Consul을 도입했습니다.

요구 사항

v0.3에 대한 요구 사항은 아래와 같았습니다.

  • 특정 도메인에 종속되지 않은 독립적이고 범용적인 목적의 시스템이 될 필요가 있다.
  • connection-manager의 역할이 과중하다.
  • 대용량 트래픽 처리에 대한 개선이 필요하다.
  • 데이터 관리를 위한 서버 군의 역할이 데이터 관리 외 별다른 비즈니스 로직을 처리할 만한 게 없다.

messaging-hub를 만들면서 가장 큰 터닝 포인트가 됐던 피드백이자 요구 사항은 첫 번째 항목입니다. Demaecan의 문제를 해결하기 위해 시작한 프로젝트였지만, 그 자체로 독립적인 시스템이 될 수 있다는 가능성을 열어준 피드백이었기 때문입니다. v0.3까지는 외부 시스템에서 소싱되는 메시지의 도메인을 messaging-hub가 알아야 한다고 규정했습니다. 그러나 이 피드백을 기점으로 messaging-hub는 전달하는 메시지의 내용을 알 필요가 없어지게 바뀌었고, 서버의 메시지를 클라이언트에게 전달한다는 당초의 목표에 더욱 집중할 수 있게 됩니다.

v0.7

아키텍처

  • 그림에서는 드러나지 않지만, 외부 시스템에서 전달된 메시지의 도메인 정보를 더는 messaging-hub에서 알지 않습니다. gRPC 통신을 위한 protocol-buffers에 정의된 메시지 포맷만 맞춰서 전달합니다. 이에 엔드포인트를 단일화할 수 있었습니다. 그 외에 코드 내 Demaecan 도메인에 종속적인 네이밍도 최대한 정리했습니다.
  • connection-manager의 메시지 소싱과 라우팅 역할을 connection-router로 분리했습니다. 이전의 connection-manager 간 메시 구조는 사라지고, connection-router와 connection-manager 간 소켓 연결로 바뀌었습니다.
  • connection-router로 들어온 메시지는 대용량 트래픽 처리를 위해 Kafka를 이용해서 큐잉 처리합니다.
  • 기존 order-app과 driver-app처럼 Demaecan 도메인에 종속적이었던 메시지 데이터 관리 서버 군을 message-app으로 통합했습니다.

요구 사항

v0.7에 대한 요구사항은 아래와 같습니다.

  • 프로젝트 이름이 지나치게 일반적이다. 또한 각 모듈의 네이밍을 정리해 모듈의 목적을 명확히 한다.
  • FCM 외에도 다양한 알림(notification) 채널을 추가할 수 있으면 좋겠다.
  • 서비스 오픈 준비를 위한 제반 여건을 구축해야 한다.

v1.0

프로젝트 이름을 messaging system에서 messaging-hub로 변경했습니다. 메시징은 워낙 일반적인 네이밍이어서 다른 프로젝트명과 충돌할 수도 있었기에 좀 더 목적을 잘 드러낼 수 있는 이름이 없을지 고민했는데요. 알림 채널 추가를 디자인하는 중에 리더가 툭 던진 messaging-hub라는 이름이 '이거다' 싶어서 냉큼 가져왔습니다.

아키텍처

  • 기존 connection-router를 message-router로 변경했습니다. connection-manager뿐 아니라 notification-app을 위해서도 라우팅할 수 있게 되었기 때문입니다. 라우팅 역할을 그대로 유지한 채로 영역을 확장한 것이라고 보면 됩니다. 비동기 뉘앙스가 큰 이름이었던 async-app도 운영 관리를 한다는 의미의 operation-app으로 변경했습니다. 이로써 더 다양한 운영 관리 처리를 맡기기에 자연스러워졌습니다.
  • FCM뿐 아니라 다양한 채널(SMS, 이메일 등)로도 푸시를 보낼 수 있도록 기존 push-app을 리팩토링했습니다. Web API로 앱 푸시 메시지를 받던 것도 Kafka를 중심으로 통신 방식을 변경했습니다. 이 과정에서 notification-app으로 명칭도 변경됐습니다. 
  • 시스템 오픈 준비를 위해서 로그와 모니터링, 경고 알림, 스트레스 테스트와 같은 제반 사항을 정리했습니다.

여기까지 초기 아키텍처 디자인이 현재에 이르기까지 어떤 피드백과 요구 사항으로 변화돼 왔는지 과정을 설명드렸습니다. 아시다시피 시스템 설계 디자인에서 컴포넌트가 하나 추가되거나 선이 하나 그려지고 지워지는 것은, 코드 수준에서는 그 이상의 리팩토링과 정리가 수반되는 작업인데요. 이 글에서 코드 레벨에서의 변경을 하나하나 설명하면 그 양이 너무 많고 지루해질 수 있어서 아키텍처 중심으로 설명을 드렸습니다. 코드를 기다리시는 분들은 이 글을 끝까지 읽으면 그 답을 찾을 수 있으니 조금만 더 참고 읽어주세요. :)

messaging-hub의 구성 및 처리 흐름

messaging-hub가 어떤 모듈과 라이브러리로 구성되어 있고, 메시지를 처리하기 위한 주요 흐름이 어떻게 전개되는지 살펴보겠습니다.

모듈

배포 모듈

messaging-hub에서 배포되는 모듈은 총 5가지입니다.

  • message-router: 메시지 소싱과 라우팅을 담당합니다.
  • connection-manager: 커넥션 관리와 서버 푸시를 담당합니다.
  • notification-app: 서버 푸시 외 다양한 알림 채널로의 메시지 전달을 담당합니다.
  • message-app: 메시지 데이터 관리를 담당합니다.
  • operation-app: 커넥션 싱크 관리나 스케줄러 등의 운영 처리를 담당합니다.

라이브러리 모듈

messaging-hub에서 만들어 사용하고 있는 라이브러리는 총 4가지입니다.

  • core: 유용한 기능적인 컴포넌트와 공통 모델을 가집니다.
  • messaging-shared: 메시징에 밀접한 유용한 기능적인 컴포넌트와 공통 모델을 가집니다.
  • common: 내부에서 사용하는 avro, protocol-buffers IDL을 정의하고 있고, 빌드를 통해 code generating을 제공합니다.
  • external-lib: messaging-hub와 연동하는 외부 시스템에 공유할 목적으로 protocol-buffers IDL을 정의합니다.

메시지 소싱

외부 시스템에서 클라이언트로 메시지를 보내기 위해서 messaging-hub로 메시지를 전달하는데요. messaging-hub 관점에서 이것을 메시지 소싱(sourcing)이라고 부릅니다.

external-lib

외부 시스템은 메시징 허브가 제공하는 external-lib를 통해 gRPC로 메시지를 전달할 수 있습니다.

messaging-hub는 external-lib를 CI 툴(TeamCity)을 이용해 AWS와 Verda(LINE의 IaaS)로 배포하고 있습니다. 보통 재난 복구(disaster recovery)를 위해 인프라를 이중화하는 경우가 많은데요. 애석하게도 그런 이유로 이중 배포를 하는 것은 아니고, Demaecan 내 시스템들이 인프라 환경을 별도로 운영하고 있기 때문에 이를 지원하기 위한 이중 배포라고 보시면 됩니다. 인프라 환경 다원화 때문에 발생한 ACL(access control list) 네트워크 설정 이슈도 있었는데요. 지면 관계상 여기서는 자세한 내용은 생략하겠습니다.

external-lib에는 gRPC 통신을 위한 protocol-buffers IDL(interface description language)이 정의돼 있습니다. 따라서 연동을 희망하는 외부 시스템의 개발 환경에 맞게 라이브러리를 빌드함으로써 코드 제너레이팅을 통해 연동할 수 있습니다.

Command

external-lib에는 messaging-hub로 메시지를 전달하기 위한 포맷이 정의돼 있습니다. 이 중 command 필드가 있는데요. messaging-hub에서 메시지의 기능을 특정하기 위한 개념으로 사용하는 필드입니다. 메시지는 내용과 상황에 따라 action, event, request, message 등 여러 목적을 가질 수 있습니다. 이를 아우르는 개념이 필요했고, 이 개념을 'command'라고 명명했습니다.

앞서 말씀드렸듯 messaging-hub는 독립적이고 범용적인 시스템을 지향하고 있기 때문에 payload 또한 stringify JSON(JSON을 문자열화한 것)을 사용하고 있습니다. 이를 통해 클라이언트에 전달하는 payload를 모델링해야 하는 수고를 덜 수 있고, messaging-hub와 연동 시스템 간 모델링으로 인한 강결합을 없애는 데에도 도움이 되는데요. 반면 messaging-hub 운영 관점에서는 흘러간 메시지가 무엇인지 도통 알 수 없어서 로그 조회나 통계 추출 등에 어려움이 발생합니다. 이때 이를 해소하는 방법 중의 하나가 command입니다. 그뿐만 아니라, 클라이언트에서도 전달된 메시지가 무엇인지를 단번에 알고 모델로 매핑해 핸들링하는 데에 사용할 수 있습니다.

command의 value 표기법은 UPPER_SNAKE_CASE를 권장하고 있습니다. 외부 시스템에서 전달되는 메시지의 경우, 그 command 값에 대한 네이밍을 messaging-hub가 정하지 않습니다. 온전히 외부 시스템과 클라이언트 개발자들 간에 메시지의 종류나 기능에 대해 협의된 결과에 따라 명명하면 됩니다. 그렇기 때문에 command를 정의를 위해 3자 간(외부시스템<->messaging-hub<->클라이언트) 협의할 필요 없이, 외부 시스템과 클라이언트 간에 협의해서 결정하면 됩니다. 이로써 messaging-hub 측면에서는 커뮤니케이션 비용과 작업 공수가 대폭 줄어드는 효과가 생깁니다.

이와는 다르게 messaging-hub에서 정의한 command도 있습니다. 클라이언트와 messaging-hub 간 통신에 필요한 command들입니다. 예를 들어 클라이언트가 소켓 연결을 하기 위해 CONNECT command를 사용하는 것과 같은 경우입니다. 향후 채팅이 도입되면 클라이언트의 통신 주체가 외부 시스템이 아닌 messaging-hub가 되기 때문에 이때에도 messaging-hub가 command를 정의하게 될 것입니다. 

이렇듯 command는 메시지의 기능과 목적을 특정하기 위한 정보를 아우르는 표현으로 사용되고 있습니다.

메시지 유형

외부 시스템이 메시지를 전달할 때의 기본 포맷은 아래와 같습니다.

여기서 payload에 주목해야 합니다. 앞서 말씀드린 것처럼 stringify JSON 형태로 전달해야 합니다. messaging-hub는 외부 시스템이 클라이언트에게 전달하려는 메시지의 내용에 관심이 없습니다. 오직 메시지를 목적지로 보내는 것에만 집중하고 거기에 충실합니다. 이것은 앞서 독립적이고 범용적인 시스템을 지향한다는 것과 같은 맥락입니다. 혹여라도 메시지 내용에 관여하는 순간부터 messaging-hub는 흘러가는 모든 메시지의 포맷을 모델링해야 하기 때문입니다. 그렇게 되면 사실상 강력한 도메인 의존성이 발생하므로 독립적인 시스템이라고 부를 수 없습니다.

소켓

messaging-hub를 다른 API 서버와 비교했을 때 가장 큰 차이점은 커넥션을 사용한다는 것입니다. messaging-hub는 Netty를 사용해서 기본적인 소켓 설정과 채널 관리를 합니다. 클라이언트와의 통신은 웹소켓을 쓰고, connection-manager와 message-router 간의 통신에선 소켓을 사용합니다. 연결 정보를 공유하기 위해서 Redis를 이용하고, 이 정보가 불일치하는 상황을 보완하기 위해서 connection-manager와 message-router, operation-app이 자체적으로 혹은 협력해서 동기화 처리를 진행합니다.

소켓 처리를 위한 대략적인 구성은 아래 그림과 같습니다.

토큰 발행 흐름

클라이언트가 messaging-hub와 웹소켓으로 연결하기 위해서는 messaging-hub가 발급한 커넥션 토큰(connection token)을 사용해야 합니다. 그러나 클라이언트는 messaging-hub를 위한 클라이언트가 아닙니다. Demaecan 도메인의 클라이언트입니다. 따라서 messaging-hub는 클라이언트의 회원 및 인증 정보를 알지 못합니다. 그래서 Demaecan 도메인의 요구 사항을 담당하는 외부 시스템에서 클라이언트의 인증을 허가한 뒤, 외부 시스템은 messaging-hub와 서버 간 통신을 통해 커넥션 토큰을 발급 받아 클라이언트에 리턴합니다. 마지막으로 클라이언트는 커넥션 토큰을 이용해 messaging-hub와 연결합니다. 

messaging-hub가 특정 도메인에 의존성을 갖게 된다면 앞서 말씀드렸듯이 독립적인 시스템이 될 수 없기 때문에, 이와 같이 messaging-hub가 특정 도메인의 인증 정보를 취하지 않음으로써 독립성을 보존할 수 있습니다. 토큰 발급과 연결 흐름은 아래와 같습니다.

연결

이렇게 발급받은 토큰을 이용해서 웹소켓 연결을 수행합니다. 연결된 이후부터는 외부 시스템에서 전달된 메시지가 서버 푸시됩니다.

메시지 라우팅과 히스토리

전체 아키텍처만으로는 파악하기 어려웠던 세부적인 주요 메시지 흐름에 대해서 살펴보겠습니다.

웹소켓 메시지 흐름

외부 시스템은 external-lib를 통해 gRPC로 message-router에 메시지를 전달합니다. message-router는 이 메시지를 바로 Kafka에 큐잉하고, 응답으로 message-uuid를 발급합니다. 외부 시스템은 message-uuid를 이용해 message-app에 조회해서 메시지의 현재 처리 상태와 결과를 확인할 수 있습니다. message-router는 메시지를 다시 소비(컨슈머 그룹)해서 클라이언트의 연결 상태를 파악한 뒤, connection-manager로 보낼지 notification-app으로 보낼지 판단합니다. 연결 상태인 경우 connection-manager는 메시지를 받아 클라이언트로 서버 푸시합니다. 또한 멀티 디바이스를 지원하기 때문에 동일 계정의 여러 연결 정보가 있다면 멀티 서버 푸시합니다. 혹여라도 message-router에서 connection-manager로 전달되는 와중에 클라이언트 연결이 끊겼다면, connection-manager는 이를 notification-app이 소비해서 처리할 수 있도록 Kafka로 전달합니다. notification-app이 메시지를 수신하면 클라이언트에게 앱 푸시를 보냅니다. 이 과정에서 메시지의 발송 상태 및 최종 상태는 message-uuid로 message-app에서 조회할 수 있습니다. 아래는 이 과정을 대략적으로 표현한 그림입니다.

알림 메시지 흐름

웹소켓을 기본으로 하는 위의 메시지 흐름과는 다르게, 이번에는 처음부터 알림 채널을 통해 발송하고자 하는 흐름입니다. 외부 시스템은 아까와 마찬가지로 message-router로 메시지를 전달합니다. 단, 서드 파티(notification) 채널로 메시지를 보내기 위한 별도의 정보가 필요하다면 notification-app으로도 관련 정보를 미리 등록합니다(예: FCM의 경우 푸시 토큰 관련 정보). message-router는 notification-app이 처리할 수 있도록 Kafka로 전달하고, notification-app은 메시지의 알림 채널 정보를 기반으로 해당하는 서드 파티로 메시지를 보냅니다. 앞서의 경우와 마찬가지로 message-app에서는 메시지 발송 상태를 기록하고 조회할 수 있습니다.

코어 라이브러리

코어(core) 라이브러리는 보일러 플레이트(boilerplate) 코드를 모아둔 라이브러리입니다. 개발자들의 의견이 각각의 철학에 따라 충돌할 여지가 높은 영역인데요. 제 경험에 비추어 봐도 이런 종류의 라이브러리는 관리가 엉성해지면 자칫 돌이키기 힘든 괴물 같은 레거시로 변할 수 있습니다. 그만큼 관리가 어렵고, 부지불식간에 결함을 야기할 수도 있는 부분입니다. 코어 라이브러리가 털끝만큼만 수정돼도 이에 의존하고 있는 배포 모듈들은 자동화된 CI/CD를 통해 배포가 진행되는데요. 코어 라이브러리에 의존하고 있지만 수정된 코드는 사용하지 않고 있더라도 그렇습니다(물론 배포 단계를 제어할 수는 있지만요...)! 이걸 회피하려고 수정된 코드의 사용 여부를 마이크로 매니징하는 것은 오버 엔지니어링일 뿐 아니라 사이드 이펙트를 야기할 수 있습니다. 또한 기능이 추가될 때마다 외부 라이브러리의 의존성이 늘어나면 코어 라이브러리의 크기는 그만큼 커집니다. 이와 같이 단점이 명확한 만큼 장점도 명확합니다. 보일러 플레이트 코드를 모아뒀다는 점. 그 자체가 장점입니다. 

이렇게 언급한다는 건, 썼다는 얘기겠죠? 네. 저는 쓰는 쪽을 택했습니다. 현재 모든 배포 모듈은 코어 라이브러리에 의존하고 있습니다. 글의 서두에서 '하나의 문제를 해결하는 방법에는 여러 해법이 있다'고 말씀드렸는데요. 이 사례가 하나의 예가 될 것 같네요. 정답이냐, 아니냐를 명쾌하게 나눌 수 있는 문제가 아니고, 각자의 상황에 맞는 최선의 선택지를 고르는 문제라고 봅니다. 저는 messaging-hub를 개발하면서 언제든 기능과 목적에 맞게 모듈을 분리하고 확장하는 것을 열어둔 상태입니다. 실제로 앞서 소개한 시스템 아키텍처 v0.1에서 v1.0에 이르는 구성을 보면 이해가 되실 겁니다. 이는 앞으로도 유효합니다. 따라서 고민해야 할 점이 있습니다. 보일러 플레이트 코드를 필요할 때마다 copy & paste할지 코어 라이브러리를 사용할지를 놓고 생산성 측면을 고려해 결정해야 했습니다. 저는 쓰는 쪽을 택했습니다. 현재 코어 라이브러리에 담긴 기능을 요약하면 대략 아래와 같습니다.

보시는 것처럼 공통의 관심사가 되는 부분을 코어 라이브러리에 모았습니다. 각 기능의 사용법은 아래와 같습니다.

모든 빈(bean)을 디폴트로 생성하지 않도록 @Conditional* 어노테이션을 사용했습니다. 따라서 사용하는 쪽에서는 위 그림의 왼쪽(enable ex.)과 같이 사용할 기능을 명시화(enable)합니다. 그리고 오른쪽(configuration ex.)처럼 명시화한 각 기능을 설정하도록 해서 반복되는 공통 코드를 줄였습니다.

모든 배포 모듈이 코어 라이브러리에 의존하고 있지만, 각각의 설정은 다릅니다. 현재는 messaging-hub의 모든 모듈과 라이브러리, 인프라 설정 등을 하나의 프로젝트로 관리하고 있기 때문에 관심을 갖고 제어할 수 있는데요. 만약 향후 코어 라이브러리를 배포해서 다른 시스템에서도 사용할 수 있는 상황이 온다면, 그때는 더 나은 방법을 고민하고 논의하며 타협해야 하는 상황이 오지 않을까 생각합니다.

인프라 구성

messaging-hub 프로젝트의 대략적인 인프라 구성은 아래 그림과 같습니다. 

GitHub으로 푸시하면 CI 툴(TeamCity)을 통해 수정된 코드에 대한 타깃 모듈을 Docker 이미지 레지스트리(Harbor)에 올립니다. external-lib가 수정된 경우에는 Nexus(AWS/Verda)로 배포하고요. 정적 분석(SonarQube)은 아직은 로컬에서 진행하고 있습니다. Docker 이미지가 레지스트리에 올라가면 CD(Spinnaker)가 트리거되어 배포를 시작합니다(배포 단계에 대해서는 아래 그 외 - 단계 섹션에서 자세히 설명하겠습니다). 

배포 인프라는 LINE의 IaaS인 Verda를 사용하고 있습니다. 기본적인 구성(LB(Load Balancer), DNS, MySQL, Redis, Elasticsearch, Kibana, Kafka 등)은 미리 설정된 그대로 사용합니다. 그림 속 VKS는 Verda의 구성 요소 중 하나이며, Kubernetes(이후 K8s)를 래핑한 컨테이너로 배포됩니다. K8s로 배포되는 모듈 외에 미리 배포되어 있는 오브젝트로는 Fluentd와 Influxdb, Prometueus, Grafana, K6, Consul 등이 있습니다. messaging-hub의 모듈은 K8s로 배포되며, Kustomize로 K8s 오브젝트를 관리하고 빌드합니다. 

Kustomize에는 'base/overlay'라는 개념이 있는데요. 이름에서 예상할 수 있듯이 공통 설정을 base 디렉터리에 지정해 놓고, overlay에서는 base를 기준으로 달라지는 설정을 분리해서 관리할 수 있습니다. 이를 이용해 각 환경별로 설정을 나눠서 관리할 수 있습니다. 

Kustomize를 사용하면 다음과 같이 K8s 오브젝트를 선언형으로 관리하기 용이합니다.

  • 다른 소스에서 리소스 생성
  • 리소스에 대한 크로스 커팅(cross-cutting) 필드 설정
  • 리소스 집합을 구성하고 사용자 정의

그 외

현재 messaging-hub에서 운영하고 있는 개발 단계와 적용하고 있는 기술 셋을 소개하겠습니다. 

단계

messaging-hub에서 운영하는 단계는 총 6개입니다.

  • local: 서버 개발자의 로컬 환경입니다. 로컬 인프라는 docker-compose를 사용합니다.
  • alpha: 스트레스 테스트 환경입니다. K6로 스케줄링한 자동화 테스트를 진행합니다.
  • dev: 서버 개발자 테스트 환경입니다.
  • beta: QA 및 외부 시스템 연동 환경입니다.
  • rc: 인하우스 환경입니다. 스토리지는 운영 환경을 바라보지만, 접근 네트워크는 사내망으로 제한합니다.
  • release: 운영 환경입니다.

기술 셋

현재 messaging-hub의 주요 기술 셋은 아래와 같은데요. 향후 버전이 바뀌면 변경될 수 있습니다.

마치며

messaging-hub는 Demaecan 프로젝트의 이슈를 개선할 목적으로 시작된 프로젝트였습니다. 하지만 개발 과정을 거듭하며 비단 Demaecan 프로젝트에만 적용할 수 있는 게 아닌, 독립성을 갖춘 시스템으로 발전하고 있습니다. 요구 사항을 받아들이는 과정에서 뚜렷한 목적의식을 갖는다는 것이 프로젝트의 방향을 결정하는 데 얼마나 중요한 기준이 되는지 새삼 깨달을 수 있었습니다. 앞으로도 messaging-hub는 메시지 처리의 중추적인 역할을 담당하는 안정적이면서 독립적이고 범용성을 갖춘 시스템이 되기 위해 꾸준히 노력을 거듭할 것입니다.

현재 진행 중인 사항

'채팅도 가능한가요?' - '네. 가능합니다.'

지금까지의 messaging-hub는 서버에서 메시지를 전달받아 클라이언트로 메시지를 보내는 방식의 솔루션이었는데요. 채팅의 경우에는 통신의 주체가 클라이언트들이며, 이를 위해서는 별도의 채팅 비즈니스를 처리할 수 있는 서버가 필요합니다. messaging-hub에는 connection-manager를 통해 클라이언트의 연결을 관리하고 있고, message-router를 통해 연결된 클라이언트의 connection-manager를 찾아 브로드캐스팅하고 있습니다. notification-app은 서버 푸시할 수 없는 상황에서는 앱 푸시를 보낼 수 있고, message-app은 데이터 관리와 비즈니스 로직을 처리할 수 있는 구성을 갖추고 있습니다. 이와 같은 모듈의 구성에 기인해 messaging-hub에 채팅이 도입되는 것은 아주 자연스러운 흐름이라고 생각합니다. 이 글을 쓰고 있는 시점에 드디어 채팅 관련 요구 사항이 나왔고, 이에 작업하려고 합니다.

'기술 블로그에 왜 코드 한 줄이 없나요?' - '코드는 오픈소스로 공개하려고 합니다.'

이 글은 messaging-hub를 만들게 된 배경과 아키텍처, 메시지의 흐름 및 처리 과정을 소개하는 것에 초점을 두고 작성했습니다. messaging-hub는 Demaecan의 시스템 안정성 확보 차원에서 도입된 해법 중 하나였습니다만, 앞서 설명드린 아키텍처와 메시지 처리 방식에서 알 수 있듯이 그 용도가 특정 서비스 도메인으로 한정되지 않습니다. 즉, 독립적이고 범용성을 갖춘 시스템입니다. 이에 GitHub의 LINE 리포지터리에 오픈소스로 공개할 예정입니다. 물론 서비스 레벨에서 이용하고 있는 토큰 정보 및 인프라 관련 설정들을 제거하는 등의 코드 정리가 더 필요하긴 합니다. 아직 미진한 작업 내용을 정리할 필요도 있고요. 이 부분은 LINE 오픈소스 팀의 도움을 받아 절차대로 소스를 공개하는 것을 목표로 하고 있습니다. 아직 부족한 게 많습니다만, 훌륭한 오픈소스로 내놓을 수 있도록 노력하겠습니다.

Special thanks to :)

messaging-hub를 결코 혼자서 만들었다고 생각하지 않습니다. 시작할 수 있는 기회를 받았고, 꼼꼼히 살피지 못하고 뭉개고 지나간 것들에 대해서는 날카로운 지적도 받았고, 고민의 순간에는 생각을 전환할 수 있는 여러 도움을 받으며 만들고 있으니까요. 반복되는 일상을 살다 보면 정작 고마운 분들께 고맙다고 인사드리는 것이 괜스레 부끄럽고 조금 생뚱맞다고 느껴질 때가 있는데요. 제가 지금까지, 그리고 앞으로도 계속 개발자로 일하고 싶다고 생각하는 이유는 매 순간 제가 믿고, 의지하고, 배울 수 있는 좋은 동료를 만나왔기 때문이라고 생각합니다. 각자의 자리에서 최선의 노력을 기울이며 최고의 역량을 발휘하고 있는 동료분들께 이 자리를 빌려 진심으로 감사드립니다.

채용

ABC Studio는 일본 음식 배달 플랫폼의 시장 No.1을 확고히 다지기 위해 달리고 있습니다. 그 이상도 바라봅니다. 프로덕트 팀이기 때문에 기획과 디자인, 클라이언트(iOS, Android), 프런트엔드, 서버가 하나의 팀으로 움직입니다. 여러모로 매력적인 팀이라고 할 수 있습니다. 마지막으로, 저는 인복이 매우 많은 사람입니다. 따라서 저와 같이 일하시는 분들은 최소 복덩어리라고 할 수 있습니다. :)

혹시 시간이 지나 채용 링크로 연결되지 않는다면, LINE+ 채용 공고 페이지에서 'ABC Studio'를 검색해 보세요!