코드노트

Stomp.js, SockJS / SockJS-client 정리 본문

Code note/codenote

Stomp.js, SockJS / SockJS-client 정리

코드노트 2024. 6. 3. 13:37

실시간 이벤트처리 및 웹소켓 연결을 하게 되면서 웹소켓을 바로 사용하는게 아니라 StompJS, SockJS를 사용하는 것을 알게 되었다.

기존 서비스에서 사용되고 있는 코드들을 해석하면서 꼭 알고 넘어가야하는 것들을 정리 해보려고 한다.

 


WebSocket

- http의 단방향 통신(클라이언트가 서버로 요청을 보내고, 서버에서 응답을 받는)과 다르게 서버와 양방향으로 연결하는 것을 말한다.

클라이언트가 서버로 요청을 보내게되면 서버의 응답을 받고 끝나는 것이 아닌 연결이 계속해서 유지가 된다.

Rest API와 WebSocket 관련한 내용들은 따로 정리를 해둔 링크를 참고하면 좋을 것 같다.

 

REST API vs WebSocket 차이점 정리 feat.Polling, LongPolling, Streaming, SSE

프로젝트에 채팅기능을 만들려고한다. WebSocket을 사용하여 실시간 통신을 하지만 WebSocket 이전의 양방향 통신 Rest 방법도 함께 정리해보려고 한다. Rest는 일반적으로 요청을 하고 서버에서 응답

codeno-te.tistory.com


Stomp.js (Simple/Stream Text Oriented Message Protocol)

- 텍스트 기반의 메시징 프로토콜로, 클라이언트와 메시징 브로커 간의 통신을 위한 프로토콜이다.

- Stomp는 메시지 큐 시스템과 함께 사용되어 실시간 메시징 기능을 제공한다.


왜 사용할까?

- 단순화된 메시징 : STOMP는 텍스트 기반 프로토콜로 메시지를 주고 받는 형식이 간단하고 이해하기 쉽다.

- 브로커 지원 : STOMP는 RabbitMQ, ActiveMQ 등 여러 메시지 브로커와 호환된다. 따라서 이러한 브로커와 통신하기 위해 STOMP.js 를 사용할 수 있다.

- WebSocket 통합: WebSocket API는 실시간 양방향 통신을 가능하게 하지만, 이를 직접 다루는 것은 복잡하다. STOMP.js는 WebSocket 위에서 동작하며, 메시지 송수신을 보다 간편하게 처리할 수 있게 해준다.

- 구독/발행 : STOMP.js는 Topic과 큐에 메시지를 발행하거나 구독할 수 있는 기능을 제공하여 메시지 기반 애플리케이션을 쉽게 구현할 수 있다.

- 헤더 : 헤더를 통해 인증 처리 로직 또한 구현할 수 있다.

* Topic, pub/sub, 메시지 브로커, 큐 시스템 🔸 알고 넘어가자!

Topic

- 메시지가 발행(Publish)되고 구독(Subscribe)될 수 있는 대상이다. 메시징 시스템에서 토픽은 특정 주제나 채널을 말하며, 발행된 메시지는 해당 토픽을 구동하는 모든 클라이언트에게 전달된다. 쉽게 말하면 채팅방에 있는 사람들을 채팅방(Topic)에 입장(Subscribe 구독)하게 되면 다른사람들이 보낸 메시지들(Publish 발행)을 볼 수 있게 되는걸 생각하면 된다.

 

Sub(Subscribe)

- 구독(Subscribe)은 클라이언트가 특정 토픽의 메시지를 수신할 수 있도록 하는 작업이다. 클라이언트는 stomp.js의 subscribe 메서드를 사용하여 특정 토픽을 구독한다. 구독한 후에는 해당 토픽으로 발행된 모든 메시지를 실시간으로 수신할 수 있다.

 

Pub(Publish)

- 발행(Publish)은 클라이언트가 특정 Topic으로 메시지를 보내는 작업이다. stomp.js의 send 메서드를 사용해서 메시지를 발행할 수 있다. 발행된 메시지는 해당 토픽을 구독한 모든 클라이언트에게 전달된다.

 

메시지브로커

- 메시지 브로커는 다양한 시스템, 서비스, 애플리케이션 간에 메시지를 전송하는 중간 매개체 역할을 하는 소프트웨어이다. 발신자(생성자)로 부터 메시지를 받아 이를 적절한 수신자(소비자)에게 전달한다.

- 메시지 라우팅 : 메시지를 특정 조건에 따라 적절한 수진자에게 라우팅 한다.

- 메시지 변환: 서로 다른 형식의 메시지를 변환하여 호환성을 유지한다.

- 내구성: 메시지를 안전하게 저장하고 전달 실패 시 재전송을 지원한다.

 

메시지 큐 시스템

- 메시지 큐 시스템은 메시지를 임시로 저장하는 큐를 사용하여 비동기적으로 통신할 수 있게 해주는 시스템이다. 발신자는 메시지를 큐에 넣고, 수신자는 큐에서 메시지를 가져가 처리한다.

- 비동기 통신 : 발신자와 수신자가 동시에 동작하지 않아도 메시지를 주고 받을 수 있다.

- 부하 분산 : 여러 수신자에게 작업을 분산하여 처리할 수 있다.

- 확장성 : 시스템의 부하에 따라 수신자의 수를 유연하게 조절할 수 있다.


Sock.js / SockJS-Client

- 보통 WebSocket을 사용하게 되면 Sockjs를 사용하는 것을 많이 봤을 것이다. 그 이유는 Sock.js와 SockJS-Client를 통해서 WebSocket이 지원되지 않는 환경에서 실시간 통신을 제공할 수 있게 하기 위해서 사용된다.

- 즉 기본적으로는 WebSocket기반으로 실행되지만 만약 웹소켓이 실행되지 않는다면 다른 전송방법으로 자동 폴백을 시킨다. 이를 통해서 통신의 신뢰성을 높일 수 있다.

 

- 다양한 전송방법을 지원하며 브라우저 호환성 문제를 해결한다. 웹 소켓이 지원되지 않는 환경에서도 작동하며, fallback 매커니즘을 통해 여러 전송 방법을 제공한다.


예제 코드

import SockJS from 'sockjs-client';
import { Client } from '@stomp/stompjs';

// JWT 토큰 예시 (실제 토큰은 서버로부터 받아야 함)
const jwtToken = 'your-jwt-token';

let socket;

// STOMP 클라이언트 생성
const client = new Client({
  webSocketFactory: () => {
    // SockJS 엔드포인트 설정
    socket = new SockJS('http://your-server/sockjs');
    return socket;
  },
  reconnectDelay: 5000,
  heartbeatIncoming: 4000,
  heartbeatOutgoing: 4000,
});

// 연결 이벤트 핸들러
client.onConnect = (frame) => {
  console.log('Connected:', frame);

  // 토픽 구독
  const subscription = client.subscribe('/topic/sports', (message) => {
    console.log('Received message:', message.body);
  });

  // 메시지 발행 (헤더에 인증 정보 추가)
  client.send('/topic/sports', { Authorization: `Bearer ${jwtToken}` }, 'Hello, this is a message!');
};

// 연결 실패 이벤트 핸들러
client.onStompError = (frame) => {
  console.error('Broker reported error:', frame.headers['message']);
  console.error('Additional details:', frame.body);

  // 실패 시 SockJS 다시 실행
  restartSockJS();
};

// 연결 해제 이벤트 핸들러
client.onDisconnect = (frame) => {
  console.log('Disconnected:', frame);
  // 연결 해제 시 수행할 추가 작업
};

// 클라이언트 연결
client.activate();

// 임의로 연결 해제를 원할 때 사용할 함수
function disconnect() {
  client.deactivate();
}

// SockJS 다시 실행 함수
function restartSockJS() {
  console.log('Attempting to restart SockJS...');
  socket = new SockJS('http://your-server/sockjs');
  client.webSocketFactory = () => socket;
  client.activate();
}

 

기본적을 사용될 수 있는 메서드들로 예제를 구현했다.

  • onConnect : 이벤트 핸들러를 설정하여 연결 성공 시 
  • subscribe : onCunnect 내부에서 특정 주제(Topic)에 대한 메시지 수신 설정
  • send: onCunnect 내부에서 특정 메시지 발행
  • onStompError: 연결 실패 핸들러 실패시 sockJS 실행하여 대응
  • activate: 클라이언트 연결
  • deactivate: 이벤트 연결 해제를 원할 때 사용

웹소켓도 이러한 라이브러리를 통해서 구현하면 더 쉽게 구현할 수 있는걸 알게 되었고 기존 현 회사 코드에서도 이러한 로직들을 통해서 진행되고 있다.( 실제 화상에 사용되는 이벤트들은 조금은 다르게 이벤트를 등록하고 받아 사용되지만 기본 로직은 비슷하다.)

 

웹소켓 채팅 관련하여 솔루션들도 있었는데 샌드버드, 톡플러스 등이 있다.

이번 사이드로 진행하는 곳에서는 톡플러스를 사용해보기로 했는데 톡플러스를 사용해보고 후기도 작성해봐야겠다.