이번에 진행했던 분양 플랫폼 프로젝트에서 팀원들과 온라인 킥오프를 하며 기능을 도출하고, 각 기능별 담당자를 배정하는 도중 우리 프로젝트의 핵심이자 큰 챌린지가 될 수 있는 채팅 기능을 누가 맡을지 서로 눈치게임을 하고 있었습니다.
팀 프로젝트를 하다보면 호기심과 열정으로 특정 기능을 가져가서 완성하지 못하면 민폐가 될까봐 망설인적이 많았었는데, 예전 프로젝트를 되돌아봤을때 할만하겠다 쉽겠다 생각하고 마음편히 만들었던 기능은 없었다는 것이 기억나서 용감하게 채팅 기능을 제가 담당하게 되었습니다.
채팅 기능을 구현하기 위해서 관련 레퍼런스들을 찾아보고 어떻게 시스템을 구성할지 생각하다 보니 많은 키워드들이 스쳐지나갔습니다. Kafka, Stomp, MongoDB, Web Socket 중요한 건 이중에서 써본것은 하나도 없었다는 것입니다.
일단 어떤 기술들을 채택할지 선택해야 하는데, 관련 지식이 없으니 어떤 기술의 조합을 가지고 가야할지 고민조차 할 수 없었습니다. 따라서 비슷한 기술은 서로 비교하고, 그 중에서 우리 프로젝트와 어울리는 기술을 채택하기로 결정하고 학습을 시작했습니다.
웹 소켓은 HTML5 표준 기술로, HTTP 환경에서 클라이언트와 서버 사이에 하나의 TCP 연결을 통해 실시간으로 전이중 통신을 가능하게 하는 프로토콜입니다.
웹 소켓이 개발되기 이전에는 Polling이나 Long Polling 방식으로 실시간은 아니지만 그에 준하도록 구현해서 문제를 해결해왔습니다.
웹 소켓이 등장한 이후부터는 클라이언트와 서버간의 실시간 통신이 가능하게 되었습니다.
HTTP 통신에서는 연결을 지속하지 않고, 클라이언트가 서버로 단방향 요청만 가능하게 구성되어 있었습니다.
웹 소켓이 등장한 이후부터는 클라이언트와 서버간 연결을 지속적으로 유지하고 서버도 클라이언트로, 클라이언트도 서버로 데이터를 보낼 수 있게 됨으로서 양방향 통신을 할 수 있는 시대가 열렸습니다.
웹 소켓은 요청과 응답 개념이 아닌 서로 데이터를 주고 받는 형식을 취하고 있습니다.
그렇다면 웹 소켓만으로는 채팅기능을 만들 수 없을까요?
웹 소켓만으로 채팅을 만드는 것은 가능합니다. 그렇다면 왜 Stomp와 비교해서 기술을 선택하려 했을까요?
웹 소켓 프로토콜은 두 가지(텍스트, 바이너리) 유형의 메시지를 정의하고 있지만 메시지의 내용까지는 정의하고 있지 않습니다.
Stomp는 메세지 전송을 효율적으로 하기 위해 등장한 프로토콜입니다. 기본적으로 pub/sub 구조를 따르고 있기 때문에 메시지를 전송하고 메시지를 받아 처리하는 부분이 명확하다는 장점이 있습니다.
Stomp를 이용해 채팅 시스템을 개발한다고 한다면 세가지의 크게 두가지 행동이 있다고 볼 수 있습니다.
Stomp를 이용하면 어떤 점이 좋을까요?
Stomp를 사용해 채팅 기능을 개발한다면 아래와 같은 장점을 가집니다.
Stomp를 사용하면 기본적으로 In-Memory Message Broker를 사용하게 되는데, 이로인해 생기는 여러가지 문제가 있습니다. 다만 이 부분은 RabbitMQ, Kafka, Active MQ와 같은 외부 브로커를 사용함으로서 문제를 해결할 수 있기 때문에 큰 제약이라고 보긴 어렵습니다.
지금 제가 구현해야 하는 기능은 채팅 기능입니다.
채팅을 초점에 두고 생각해보면, WebSocket으로 채팅을 구현한다면 어떤 채팅방에 현재 누가 접속중인지 알아야하고, 채팅방에 포함된 사용자에게 모두 메시지를 보내야 하기 때문에 채팅방마다 세션을 관리해야 하는 번거로움이 있습니다.
Stomp로 채팅을 구현한다면, 사용자가 채팅방에 접속했을때 해당 채팅방을 구독하기만 한다면 누가 접속중인지 따로 관리하지 않아도 메시지를 보낸 사용자가 포함된 채팅방의 모든 사람에게 메시지가 갈 수 있다는 강점을 가지고 있습니다.
이런 부분에서 채팅 기능에 조금 더 부합하는 기술은 Stomp라고 판단하여 Stomp를 채택하게 되었습니다.
메시지 큐를 떠올리면 대표적으로 Kafka와 RabbitMQ를 생각할 수 있습니다.
그렇다면 둘은 어떤 차이가 있는지 지금부터 살펴 보겠습니다.
RabbitMQ는 대표적인 메시지 브로커 입니다.
메시지 브로커란 publisher가 생산한 메시지를 메시지 큐에 저장하고, 저장된 데이터를 consumer가 소비할 수 있도록 중간 다리 역할을 해줍니다.
consumer가 큐에서 데이터를 가져가게 되면, 메시지 처리 후 짧은 시간내에 데이터가 삭제된다는 점을 주의해서 사용해야 합니다. 전통적인 메시지 브로커의 형태를 따르고 있으며, 이러한 형태는 앱의 트래픽이 증가하여도 수평적으로 확장하는 데에 어려움을 주게 됩니다.
이벤트 메시지가 성공적으로 전달되었다고 판단되면, 메시지가 큐에서 삭제되기때문에 다시 이벤트를 재생하기 어렵다는 단점도 존재합니다.
Kafka는 대표적인 이벤트 스트리밍 플랫폼입니다.
메시지 브로커와 이벤트 스트리밍 플랫폼 모두 이벤트를 수신하고, 이것을 consumer에게 전달하는 데에 목적이 있다는 것은 같습니다. 하지만 이 둘의 작동 방식은 큰 차이가 있습니다.
이벤트 스트리밍 플랫폼은 이벤트가 생성되면, 레코드 로그를 streamer에 기록하게 됩니다.
consumer가 topic을 가져간 후에도 이벤트 스트림에서 로그를 계속 유지하기 때문에 에러나 기타 문제가 생겼을 경우 이벤트를 재생할 수 있다는 강력한 장점이 있습니다.
이벤트 스트리밍 플랫폼은 전통적인 메시지 브로커에 비해 좀 더 유연하고 느슨한 결합을 가져갈 수 있게 되었습니다. 따라서 격리와 확장이 비교적 쉽다는 장점도 가지고 있습니다.
Kafka는 Zookeeper가 실행중이여야 사용할 수 있습니다. Zookeeper는 분산 코디네이션 시스템을 제공합니다. Kafaka Cluster의 리더를 발탁하는 방식도 Zookeeper가 제공하고 있습니다.
메시지 브로커는 이벤트 브로커가 될 수 없지만, 이벤트 브로커는 메시지 브로커 역할을 할 수 있습니다.
사이드 프로젝트로 만든 플랫폼 서비스이기 때문에, 사용자가 백만명이상 또는 엄청난 트래픽이 몰려서 그 트래픽을 받아내야 하는 상황은 아니라고 판단이 들었습니다.
그래도 이번 프로젝트의 목적인 고가용성의 설계를 가진 애플리케이션을 만들어보자는 목표에 부합하기 위해서는 트래픽이 많고, 장애가 생겼을때 대응할 수 있는 포인트가 조금 더 명확한 Kafka를 사용하기로 결정하였습니다.
RabbitMQ도 exchange를 통한 pub/sub 구조를 구현할 수 있고, 충분히 채팅 시스템을 구현하는 것에는 지장이 없지만 추후 트래픽이 몰릴거라는 가정 하에서는 Kafka가 조금 더 유연할 것으로 생각했습니다.
이 프로젝트를 추후 MSA 아키텍처로 변경하기 위해서도 느슨한 결합을 가져가는 것이 좋다고 판단이 들었습니다.
채팅방의 채팅 내용을 쓰고, 읽는 작업을 하기 위해서 기존에 사용하는 MySQL을 계속 사용할지 MongoDB를 채팅전용 DB로 사용해야 하는지에 대한 고민이 생겼습니다.
결론부터 말씀드리면, 순수한 채팅방에 대한 데이터는 MySQL을 사용하고, 채팅 기록은 MongoDB를 사용하기로 결정하였습니다. 지금부터 이유를 말씀드리겠습니다.
MongoDB는 NoSQL의 한 종류이기 때문에 RDB보다 더 빠른 읽기 쓰기 성능을 가지고 있습니다.
채팅 내용은 언제든지 바뀔 수 있고, 많은 양의 데이터가 수시로 읽고 써진다는 특징이 있습니다. 이런 부분에서 MongoDB를 이용해서 채팅 데이터를 관리해주는 것이 성능적으로 이점을 가질 것이라고 생각했습니다.
채팅방 부분만 MySQL을 이용해서 따로 관리하는 이유는 다음과 같습니다.
분양 플랫폼 서비스에서 분양글의 PK(고유키)와 회원의 PK는 어떤 회원의 채팅방 리스트를 조회할때 꼭 필요한 정보입니다. 결론적으로 회원이 채팅방 목록을 보려고 할때 JOIN 연산이 불가피하므로, MySQL을 이용해 채팅방 정보를 유지하기로 결정하였습니다.
회원이 채팅방 리스트를 조회할때 한번만 조회 쿼리가 나가기때문에 수시로 읽고 쓰는 채팅 내용보다는 훨씬 가벼운 작업이기 때문에 관계형 데이터베이스의 강점은 살리고, NoSQL의 강점은 따로 가져가는 결정을 하였습니다.
처음 만들게 된 채팅 기능이라, 기술 선정부터 테이블 설계 기능 구현까지 많은 이슈와 에러를 겪으면서 만들었습니다. 이렇게 힘들게 만든 기능이라 조금 더 애착이 가기도 합니다.
오늘은 2가지 또는 3가지 정도의 선택지에서 어떤 기술을 선택하고, 왜 그 기술을 선정했는지에 대한 이유를 다뤄 보았습니다. 다음 포스팅부터는 환경을 설정하고 하나하나 기능을 구현해보겠습니다.
오늘도 읽어주셔서 감사합니다.
다음 시리즈 게시물로 이동
Stomp + Kafka를 이용한 채팅 기능 개발하기 - (with Spring Boot) #2 (Kafka 설치 + MongoDB & Stomp 설정)
게시물로 이동 ->
평소 관심있던 부분이었는데 너무 정리가 깔끔하네요-! 좋은 글 작성해 주셔서 감사합니다 ㅎㅎㅎ