• 이번 우리 스쿼드의 3분기 목표는 유저끼리 실시간 대화가 가능하게 만들자!였기 때문에 채팅 서비스를 준비하게 되었다
    채팅의 특성상 실시간성 보장이 필요하므로 실시간 통신에 대해 먼저 파악해보았다


Http로 가능한 실시간 통신 방법

기본적으로 Http protocol은 단방향 프로토콜이면서 stateless의 특징이 있어 실시간 통신에 적합하지 않지만, 현재 우리 서비스는 Http만 사용중이기 때문에 Http로 가능한 실시간 통신 방법을 먼저 파악해보았다


Http Polling

스크린샷 2023-10-25 오후 11 27 46

polling은 web browser가 주기적으로 server에게 이벤트가 발생했는지 확인하는 방식이다
이벤트가 발생한 경우에만 response에 이벤트 정보가 포함되며, 발생하지 않았다면 response에는 이벤트 정보가 포함되지 않는다 이벤트 발생여부 확인을 위해 주기적인 요청, 응답이 오가기 때문에 time interval을 어떻게 잡느냐에 따라 서버의 부하가 증가하거나 실시간성이 떨어지는 trade off를 가진다


Long Polling

스크린샷 2023-10-25 오후 11 28 10

long polling은 기존 polling의 실시간성을 보완한 방식이다
web browser 요청 시, 이벤트가 발생하기 전까지는 response를 보내지 않고, 이벤트가 발생할 때만 response에 이벤트 정보를 포함해 보낸다 server의 이벤트가 자주 발생하는 경우라면 오히려 비효율적인 트래픽이 발생할 수 있다



Socket

web socket을 사용하면 web browser에서도 socket 통신처럼 실시간으로 데이터를 주고 받을 수 있다
HTTP 통신과의 차이점으로는 HTTP는 client가 server에 요청하는 것이지만, Web Socket은 양방향 요청이 가능하다 HTTP와 동일하게 80, 443 port를 사용할 수 있다

동작 과정

  1. web socket 연결을 위해 http 통신을 한다
  2. handshake 과정을 성공하면 http -> web socket protocol로 변경하는 protocol switching이 이루어진다
  3. web socket을 위한 socket이 생성된다



실시간성에서 HTTP vs Web Socket

  • HTTP는 client가 응답을 원하는 경우, 항상 서버에 먼저 요청을 보내야한다 = 단방향 통신
  • web socket은 연결이 계속 유지되고 있는 상태이다 = 양방향 통신

    => 채팅에서 상대방의 메시지가 도착했는지 계속해서 server에게 확인해보지 않아도 된다


  • Http는 요청 시마다 header 정보를 보내야한다
  • web socket은 최초 접속 시에만 http protocol 위에서 handshaking을 하기때문에 header 정보를 보낸다

    => 자주 통신을 해야하는 실시간 서비스이기 때문에 네트워크 비용에서 이득이다


  • web socket은 client와 server가 한번 연결되면 같은 라인으로 통신한다 한번 연결이 수립된 후로는 간단한 메시지만 오가는 방식이다

    => Http 사용 시 발생되는 불필요한 Http와 TCP 연결 트래픽을 피할 수 있다


처음에 얕은 지식으로 예상했었는데, 역시 web socket이 실시간 통신에 있어서 더 유리해보인다
polling은 실시간 통신이라 부르기엔 좀 애매한듯한 느낌
그럼 이제는 web socket과 관련된 내용들을 더 자세히 알아봐야지



Web Socket의 한계와 관련사항

Socket.io

최근에는 대부분의 web browser가 websocket 프로토콜을 지원하지만, 일부 환경(사파리, 익스플로어, 파이어폭스와 같은 구버전)에서는 지원하지 않는다
Socket.io는 nodejs 기반의 실시간 이벤트 서버를 개발할 수 있는 오픈소스 라이브러리로, 요청을 보낸 web browser가 web socket을 지원하는지 확인해보고, 지원하지 않는다면 실시간성을 제공해주는 라이브러리이다 스프링의 경우 SocketJS를 지원한다


STOMP

web socket은 기본적으로 text와 binary 타입의 메시지만을 양방향으로 주고받을 수 있는 protocol이다
그 메시지를 어떤식으로 주고받을지는 따로 정해진 것이 없으며, 주고받을 수 있게 해줄 뿐 그 이상의 일을 하지 않는다 주고 받은 문자열의 해독은 application에 맡긴다 Http는 형식을 정해두어서 모두가 약속을 따르기만 하면 해석할 수 있지만, web socket은 형식이 정해져 있지않아 application에서 해석이 쉽지 않다

그래서 web socket 방식은 STOMP 등의 sub protocol을 사용해 주고받는 메시지의 형태를 약속하는 경우가 많다 해당 메세지가 어떤 요청인지, 어떻게 처리해야 하는지에 따라 채팅방과 세션을 일일이 구현하고 메세지 발송 부분을 관리하는 추가 코드를 구현할 필요가 줄어든다


STOMP(Simple Text Oriented Message Protocol)란 web socket 위에서 동작하는 간단한 텍스트기반 message protocol로, client와 server가 전송할 메시지의 유형, 형식, 내용들을 정의하는 매커니즘이다
TCP와 web socket과 같은 신뢰할 수 있는 양방향 streaming network protocol에서 사용할 수 있고,
기본적으로 pub/sub 구조로 되어있어 메세지를 전송하고 받아 처리하는 부분이 확실히 정해져있다 pub/sub 구조이기 때문에 broker를 통해 다른 사용자들에게 메세지를 보내거나 서버가 특정 작업을 수행하도록 메세지를 보낼 수 있다
Http와 마찬가지로 frame을 사용해 전송하는 프로토콜이다


Frame

STOMP는 COMMAND, header, Body 라는 형식으로 골격이 정의되어 있다

COMMAND 
header1:value1 
header2:value2 

Body^@

따라서 web socket만 사용했을 때는 서버에서 보는 그냥 날 것의 메시지만 오고가지만, STOMP를 사용하면 command, header, body의 형태로 메시지가 오고간다


통신 흐름

스크린샷 2023-11-02 오후 5 35 05


  1. 구독자들은 /topic 경로를 구독하고 있다
  2. 발신자는 서버를 통한 가공 및 처리를 위해 /app 주소로 메시지를 송신한다
  3. 가공을 마친 메시지를 /topic 이라는 경로에 담아서 전송하면 그 메시지를 message broker가 전달받는다
  4. message broker는 그 메시지를 /topic을 구독중인 구독자들에게 최종적으로 전달한다


장점

  • messaging protocol과 messaging 형식을 개발할 필요가 없다
    • 하위 protocol과 message convention을 정의할 필요가 없다
    • 형식을 고민하고, 파싱하는 코드를 구현할 필요가 없다
  • 연결 주소마다 새로운 handler를 구현하고 설정해줄 필요가 없다
  • message broker를 사용하면 구독을 관리하고 메시지를 broadcast 하는데에 사용할 수 있다
    • 외부 messaging queue 를 사용할 수 있다 ex) RabbitMQ, Kafka 등


Pub/Sub

pub/sub은 메세지를 공급하는 주체소비하는 주체분리해 제공하는 메세징 방법이다

우체통에 빗대어보자면

  • 우체통 = 채팅방 생성 = pub/sub 구현을 위한 topic 생성

  • 신문 구독 신청 = 채팅방 입장 = topic 구독

  • 구독한 신문이 우체통에 오면 봄 = 채팅방에서 메세지를 송수신 = 해당 topic으로 메세지를 송신(pub), 메세지를 수신(sub)

    • 우체통(topic)이 있다면 집배원(publisher)이 신문을 우체통에 배달
    • 구독자(subscriber)는 우체통에 신문이 배달되는 것을 기다렸다가 빼서 봄
      • 구독자는 다수가 될 수 있음


Spring에서 지원하는 STOMP

Spring에서 지원하는 STOMP를 사용하면 spring websocket applicationSTOMP broker로 동작한다

Spring에서 지원하는 STOMP는 Simple In-Memory Broker를 이용해 subscribe 중인 다른 client들에게 메세지를 보내주며, 이 때 Simple In-Memory Broker는 client의 subscribe 정보를 자체적으로 메모리에 유지하고 있다

또한 RabbitMQ, ActiveMQ, Kafka 같은 외부 메세징 시스템을 STOMP broker로 사용할 수 있도록 지원한다 Spring은 메세지를 외부 broker에게 전달하고, broker는 websocket으로 연결된 client에게 메세지를 전달한다 이 구조로 Http 기반의 보안 설정과 공통된 검증 등을 적용할 수 있다


SpringBoot 사용중이니까 STOMP, SocketJS 그리고 broker로 RabbitMQ 적용하기로!
RabbitMQ 정리도 부지런히 해두어야쥐



Reference
Web socket
Socket.io
STOMP
실시간 서비스 경험기(배달운영시스템)
웹소켓과 STOMP, Spring Messaging을 통한 실시간 통신 이해하기
Web Polling, Long Polling, Server-sent Events, WebSocket


업데이트: