WebSocket과 Stomp 정리

장숭혁·2024년 4월 16일

웹 소켓(WebSocket):

  • 웹 소켓은 TCP 기반의 실시간 양방향 통신을 지원하는 프로토콜
  • 웹 브라우저와 서버 간에 양방향 통신 채널을 열고, 이 채널을 통해 데이터를 주고받는다.
  • 일반적으로 HTTP 프로토콜은 클라이언트가 서버에 요청을 보내고, 서버는 그에 대한 응답을 보내는 단방향 통신이지만 웹 소켓은 이러한 단점을 보완하여 실시간으로 양방향 통신이 가능하다.
  • 웹 소켓은 웹 브라우저와 서버 간에 지속적인 연결을 유지하고, 이를 통해 서버가 클라이언트에 데이터를 푸시할 수 있도록 한다.

polling 방식

클라이언트가 평범한 http request서버로 계속 날려서 이벤트 내용을 전달받는 방식이다.가장 쉬운방법이지만 클라이언트가 계속적으로 request를 날리기때문에 클라이언트가 많아지면 서버의 부담이 급증하게 된다.

  • http request connection을 맺고 끊는것 자체가 부담이 많은 방식이다. 그리고 클라이언트에서 실시간정도의 빠른 응답을 기대하기도 어렵다.
  • polling은 http 오버헤드가 발생한다는 단점이 있다.하지만 일정하게 갱신되는 서버 데이터의 경우 유용하게 사용할 수 있는 방식이다. (ex. 대시보드 갱신)

Http Overhead 란?

정보의 신뢰성 판단을 위한, 보내지는 헤더 같은 정보 때문에 오히려 데이터량이나 처리시간이 증가하는걸 말한다.
오버헤드(overhead)가 됐다 라는 말은, 처리 시간 및 메모리등이 추가적으로 사용되는 현상을 말한다.

ex) A라는 처리를 실행한다면 3초 걸린다고 했는데, 안전성을 고려하여 추가로 B라는 처리를 가미한 결과 처리시간이 10초가 걸렸다고 하자. 그러면, 이 때 오버헤드는 7초 이게 된다.

출처: https://inpa.tistory.com/608 [Inpa Dev 👨‍💻:티스토리]

long-polling

  • 서버측에서 접속을 열어두는 시간을 길게하는 방식.
  • 클라이언트에서 서버로 http request를 날린다.
  • 서버에 응답에 대한 사용 가능한 데이터가 없으면 계속 기다리다가 서버에서 해당 클라이언트로 전달할 이벤트가 있다면 그순간 response 메시지를 전달하면서 연결이 종료된다.
  • 클라이언트에서는 곧바로 다시 http request를 날려서 서버의 다음 이벤트를 기다리게 되는 방식이다.
  • 일반 polling 방식보다는 서버의 부담이 줄겠지만 클라이언트로 보내는 이벤트들의 시간간격이 좁다면 polling 과 별 차이가 없게 되며, 다수의 클라이언트에게 동시에 이벤트가 발생될 경우에는 곧바로 다수의 클라이언트가 서버로 접속을 시도하면서서버의 부담이 급증하게 된다.

WebSocket

  • 양방향 채널을 이용해 채팅방 처럼양방향 통신이 가능하다.
  • 기존 http요청 응답 방식은 요청한 그 클라이언트에만 응답이 가능했는데, ws 프로토콜을 통해 웹소켓 포트에 접속해 있는 모든 클라이언트에게 이벤트 방식으로 응답한다.
  • 최초 접속이 일반 http request를 통해 handshaking과정을 통해 이루어 지기 떄문에, 기존의 80, 443 포트로 접속을 하므로 추가로 방화벽을 열지 않고도 양방향 통신이 가능하고, http 규격인 CORS적용이나 인증등의 과정을 기존과 동일하게 가져갈 수 있는것이 장점이다. 단, websocket 프로토콜을 처리하기 위해 전이중 연결과 새로운 웹소켓 서버가 필요하다.

SSE

  • Server Sent Events - SSE는 서버의 데이터를 실시간으로, 지속적으로 Streaming 하는 기술

웹소켓은 별도의 서버와 프로토콜로 통신하기 때문에 구현하는 비용이 많이 든다는 단점이 있다.

하지만 SSE는 기존 HTTP 웹 서버에서 HTTP API 만으로 동작되며 구현도 간단하기 때문에 서버와 프론트엔드 양측 모두 매우 쉽게 개발이 가능하다.

Server Sent Event 특징

  • 브라우저는 서버가 생성 한 Stream 을 계속 받음(Server 에서 보내는 Stream 으로 Read Only)
  • Connection 유지를 위해 HTTP protocol 을 사용, HTTP/2를 통한 multiplexing 사용 가능
  • 연결이 끊어지면 EventSource가 오류 이벤트를 발생시키고 자동으로 다시 연결을 시도(error recovery)
  • 표준 기술로 IE 를 제외한 브라우저 대부분을 지원(Pollyfill로 IE 사용 가능)

Server Sent Event 사용 시점

  1. 효율적인 단방향 통신이 필요한 경우
  2. 실시간 데이터 스트림밍에 HTTP 를 사용하려는 경우(RestFul 의 GET method 와 유사)
  3. 사용 되는 예 (클라이언트는 수신만 받으면 된다)
  • 암호 화폐 또는 주가 피드 구독
  • Twitter 피드 구독
  • 라이브 스포츠 점수 받기
  • 뉴스 업데이트 또는 알림

마치 라디오 처럼, 클라이언트는 데이터를 서버로 보낼수는 없고 받기만 할 수 있기 때문에 채팅방 같은 양방향 통신을 구현하기에는 한계가 발생
클라이언트에서 서버로 Request를 보내야 하므로 HTTP 오버헤드가 발생한다.

Stomp

(Simple Test Oriented Messaging Protocol)

  • 메시지 브로커를 활용, pub/sub의 형태로 경로를 설정해 데이터를 주고 받을 수 있게 하는 메시지 프로토콜
  • 웹소켓과 함께 쓰여 웹소켓의 경로를 설정하는 역할을 한다. stomp를 쓰지 않는다면 경로를 설정하는데 코드가 보다 복잡해짐 (웹소켓 핸들러 인터페이스를 구현해야 한다)

ex)채팅방에 구현에 사용할 때

  • 메시지의 발행자와 구독자가 존재한다. 메시지를 보내는 사람과 받는 사람이 구분된다.
    • 채팅방 생성 : pub / sub 구현을 위한 Topic이 생성됨
    • 채팅방 입장 : Topic 구독
    • 채팅방에서 메세지를 송수신 : 해당 Topic으로 메세지를 송신(pub), 메세지를 수신(sub)

Stomp 특징

  • 언어-중립적이다. 다양한 언어로 작성된 클라이언트와 서버간에 메시지를 교환할 수 있게 한다.

  • 프레임 기반이다.

COMMAND
header1:value1
header2:value2
Body^@


  1. 명령(command): STOMP는 클라이언트와 서버 간의 통신을 위해 명령어를 사용한다. 가장 일반적인 명령어로는 CONNECT, SEND, SUBSCRIBE, UNSUBSCRIBE, BEGIN, COMMIT, ABORT, ACK, NACK 등이 있다.
  2. 헤더(headers): 각 명령어에는 헤더가 포함될 수 있다. 헤더는 키-값 쌍으로 이루어져 있으며, 메시지의 특정 속성을 나타낸다. 일반적인 헤더로는 destination, content-type, content-length 등이 있다.
  3. 메시지 본문(body): 명령어와 헤더 이후에는 선택적으로 메시지 본문이 포함될 수 있다. 메시지 본문은 일반적으로 텍스트나 이진 데이터 형태일 수 있다.

Spring이 Stomp를 사용하고 있을때 동작 흐름

  • SimpAnnotationMethod (/app 으로 전송후 /topic 구독자에게 전송)

    • publisher가 subscriber 들에게 직접적으로 보내지 않고 서버에서 데이터를 가공후 보낼때 이 메시지 핸들러를 지나게 된다. 이후 broker channel을 통해 SimpleBroker에 보내지고 subscriber들에게 보내진다.
  • SimpleBroker(/topic 구독자들에게 바로 전송)

    • publisher가 subscriber 들에게 직접적으로 데이터를 보낼때 사용된다.

💻 코드 예시1

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue","/topic"); // 메시지 브로커 설정
        registry.setApplicationDestinationPrefixes("/app"); // 클라이언트에서 메시지를 보낼 때의 prefix 설정
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS(); // STOMP 엔드포인트 설정
    }
}
  • enableSimpleBroker() : 내장 브로커를 사용, prefix가 붙은 메시지를 발행시 브로커가 처리

  • setApplicationDestinationPrefixes() : 메시지 핸들러로 라우팅되는 prefix

  • /app/hello 라는 destination 헤더를 가진 메시지들이 거치게 됨

  • /topic/greeting의 구독자들에게 전송됨 → simpleBroker로 전달됨

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
}

/ws : 웹소켓 핸드쉐이크를 위한 주소이다.

setAllowedOriginPatterns("*") : 이 부분은 모든 원본(Origin)의 요청을 허용하는 설정을 지정한다.

.withSockJS(): SockJS는 WebSocket을 지원하지 않는 일부 브라우저에서도 실시간 웹 소켓 통신을 가능하게 한다.

💻 코드 예시2

@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
            .addEndpoint("/websocket")
            .setAllowedOrigins("*");
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {

        config.enableSimpleBroker("/topic");

        config.setApplicationDestinationPrefixes("/app");
    }
}

Stomp 사용하는 이유

  • 하위 프로토콜 혹은 컨벤션을 따로 정해주지 않아도 된다.

  • 연결 주소마다 새롭게 핸들러를 구현해주지 않아도 된다.

  • 외부 Message Queue를 사용할 수 있다.(RabbitMQ, Kafka)

  • spring security를 사용할 수 있다.

  • 간단한 프로토콜: STOMP는 간단하고 이해하기 쉬운 텍스트 기반의 프로토콜이다. 이는 개발자들이 쉽게 구현하고 사용할 수 있도록 도와준다.

  • 다양한 언어 및 플랫폼 지원: STOMP는 여러 언어 및 플랫폼에서 구현되어 있으며, 대부분의 주요 프로그래밍 언어 및 브로커 시스템에서 지원된다.

  • 유연성: STOMP는 다양한 메시지 지향 미들웨어나 메시징 시스템과 통합할 수 있는 유연성을 제공한다. 이를 통해 시스템 간의 통신을 단순화하고 서로 다른 시스템 간의 상호 운용성을 개선할 수 있다.

  • 클라이언트-서버 모델: STOMP는 클라이언트-서버 모델을 따르며, 이는 클라이언트와 서버 간의 효율적인 통신을 가능하게 한다. 이 모델은 여러 클라이언트가 동일한 서버에 연결하여 메시지를 교환할 수 있도록 한다.

  • 가볍고 효율적: STOMP는 가볍고 효율적인 프로토콜로, 적은 대역폭과 자원을 사용하여 메시지를 전송할 수 있다. 이는 네트워크 부하를 최소화하고 빠른 메시지 전송을 가능케 한다.

  • 확장성: STOMP는 확장 가능한 프로토콜로, 복잡한 메시지 지향 미들웨어 시스템을 구축하는 데 유용하다. 필요에 따라 새로운 명령어나 헤더를 정의하여 기존의 기능을 확장할 수 있다.

profile
코딩 기록

0개의 댓글