Loki's Blog

웹소켓 클라이언트 Socket.IO와 STOMP

  • CS
  • Socket
  • WebSocket
2025-12-27

들어가며

브라우저에서 new WebSocket()으로 연결을 열고 send()로 메시지를 보내는 것까지는 몇 줄이면 끝난다. 문제는 그 몇 줄만으로는 풀리지 않는 두 가지가 있다는 점이다. 오래된 브라우저에서는 애초에 연결이 열리지 않고, 연결이 열려도 주고받는 메시지가 무슨 의미인지는 WebSocket 스펙 어디에도 정의돼 있지 않다.

WebSocket 표준 API

브라우저는 WebSocket 표준 API를 기본으로 제공한다. 서버와 통신하려면 WebSocket 클래스로 객체를 생성하며, ws:// 또는 wss:// 프로토콜을 쓰는 서버 URL을 생성자에 전달한다. 객체가 생성되면 핸드셰이크가 끝나고 서버와 메시지를 주고받을 수 있다.

const socket = new WebSocket('ws://www.test.com/chat')

웹소켓 객체는 open, message, error, close 네 가지 이벤트를 발생시키며, 그중 서버 메시지를 받는 message 이벤트를 가장 자주 다룬다. 이벤트 핸들러는 addEventListener 메서드로 설정한다.

socket.addEventListener('message', (event) => {
  console.log('서버에서 받은 메시지:', event.data)
})

서버로 메시지를 보낼 때는 send() 메서드를 사용한다.

socket.send('하이 웹소켓!')

브라우저 호환성 문제

WebSocket은 오래전에 실시간 양방향 통신의 표준 기술로 자리 잡았고, 지금은 대부분의 모던 브라우저가 WebSocket API를 기본으로 지원한다. 문제는 WebSocket을 지원하지 않는 구형 브라우저까지 함께 대응해야 할 때다. 이럴 때는 Socket.IO 같은 호환성 라이브러리를 쓰는 게 일반적이다.

Socket.IO

Socket.IO는 Node.js 기반의 실시간 통신 라이브러리로, 브라우저가 WebSocket을 지원하면 WebSocket 방식으로 통신하고 지원하지 않으면 Long Polling 같은 HTTP 기반 통신으로 유사한 실시간 동작을 구현한다. 서버는 Node.js 환경을 기본으로 한다(대체 방안이 있지만 완전하지는 않다). 반면 클라이언트는 JavaScript뿐 아니라 Java, C++ 등 다양한 언어를 지원한다. 연결이 끊어져도 자동 재연결 기능이 내장돼 있고, 여러 서버로 확장(스케일링)해 모든 클라이언트에 이벤트를 브로드캐스트할 수 있다.

SockJS

SockJS도 Socket.IO와 마찬가지로 브라우저 종류와 관계없이 실시간 통신을 구현하도록 설계된 라이브러리다. 브라우저가 WebSocket을 지원하면 WebSocket 방식으로 먼저 동작하고, 지원하지 않으면 브라우저별로 Long Polling, Streaming 같은 대체 전송 프로토콜을 사용해 WebSocket과 유사하게 동작한다.

Spring Framework는 WebSocket 사용 시 구형 브라우저를 대응하기 위해 SockJS 프로토콜을 채택하고 있다. SockJS 프로토콜은 브라우저의 SockJS-client와 서버의 SockJS-server 간 통신 규약으로, 브라우저 호환성을 최대한 확보하도록 설계됐다. 따라서 백엔드에서 Spring Framework를 쓴다면 프론트엔드도 SockJS-client를 쓰는 편이 자연스럽고 호환성도 높다.

STOMP 프로토콜

STOMP는 이름 그대로 텍스트 기반의 간단한 메시징 프로토콜이다. WebSocket은 양방향 데이터 전송을 위한 통신 채널만 제공할 뿐, 메시지의 구조나 의미는 규정하지 않는다. 그래서 WebSocket만으로 채팅 서비스를 구현하려면 메시지가 어떤 요청을 의미하는지, 어떤 형식(포맷)으로 전달되는지, 특정 메시지를 어떻게 처리할지 직접 정의해야 한다.

STOMP는 이 메시지 송수신 과정을 표준화해 위 항목을 직접 정의해야 하는 불편함을 없앤다. WebSocket 위에서 동작하며 클라이언트와 서버 간 메시지의 형식, 유형, 처리 절차를 정의해서 복잡한 메시지 교환 과정을 단순화한다. 장점은 다음과 같다.

  • WebSocket 메시지의 형식을 명확히 정의해 구조화된 통신이 가능하다.
  • 세션 관리 기능으로 연결 상태를 체계적으로 유지할 수 있다.
  • 발행/구독 패턴을 도입해 메시지 전송 과정을 유연하게 설계할 수 있다.
  • 메시지에 헤더 정보를 추가할 수 있어 인증과 메타데이터 관리가 쉽다.
  • 규칙을 지키면 언어와 플랫폼 간 호환 메시징이 가능하다.
  • v5부터는 바이너리 데이터 전송을 지원하며, 이전 버전(v4)은 텍스트 기반만 지원한다.

StompJS는 클라이언트 측에서 STOMP 프로토콜을 쉽게 쓸 수 있도록 구현한 JavaScript 라이브러리다. 이를 이용하면 브라우저나 프론트엔드 환경에서도 STOMP 기반 WebSocket 통신을 간단히 구현할 수 있다.

정리하며

raw WebSocket API는 연결을 열고 메시지를 주고받는 기능만 제공한다. 구형 브라우저까지 지원해야 하면 Socket.IO나 SockJS로 호환성 계층을 얹고, 메시지 형식과 처리 절차를 팀 안에서 통일해야 하면 STOMP로 프로토콜 계층을 얹는다. 두 계층 모두 필요 없다면, WebSocket 표준 API만으로 충분하다.

관련 글