Spring/Spring Stomp

[Stomp] Spring Boot with React 채팅 서버 : 3-1. Stomp 정리 및 설명

noahkim_ 2022. 3. 2. 02:22

이제 서버에서 클라이언트에게 STOMP 연결을 할 수 있도록 핸드쉐이크 endpoint, messageBroker의 pub 및 sub prefix를 설정해 주어야 한다.

해당 설정은 스프링에서 설정하기에 매우 쉽다.

하지만 개념을 알아야 요구사항에 따른 구현이 가능하므로 일단 개념부터 잡아보자

1. Websocket

웹소켓 소개

  • http는 tcp/ip 기반 비연결지향 프로토콜과 대비
  • websocket 프로토콜(RFC6455) - 연결지향(연결을 끊지 않음), 양방향통신(클라이언트 <-> 서버)
    • 서버와 클라이언트가 지속적으로 TCP 라인을 통해 연결되어 양방향 통신이 가능하게 됨
    • 소켓이 유지되는데 동의할 수 있도록 소켓을 수정함
      • http header의 keep-alive 속성의 값이 계속되도록 설정되어있음
  • HTTP 와는 다른 TCP 프로토콜이지만 HTTP에서 동작가능하게 디자인 되었음
  • 비동기식 이벤트 기반 메시징 아키텍쳐
  • 거래소(주식, 암호화폐), 게임등 realtime이 보장되어야하는 상황에서 커넥션비용을 줄이고 불필요한 네트워크 비용을 감소시켜줌.

웹소켓 핸드쉐이크

  • websocket 프로토콜에 의해 양쪽이 데이터를 해석하는 방법에 대한 합의를 해야 함
  • 이를 위해 핸드쉐이크라는 과정을 거치게 됨

동작 과정

  • 클라이언트가 서버로 HTTP UPGRADE 요청을 보냄
    • 프로토콜 스위치가 일어나 웹서버의 웹소켓 서버로 전달받도록 설정함
    • Sec-WebSocket-Key 와 Sec-WebSocket-Version, Upgrade: websocket 등의 헤더가 필요하다.
    • Upgrade: websocket 헤더는 클라이언트가 연결을 다른 프로토콜로 업그레이드 할 것임을 나타내며, websocket으로 업그레이드 할 것임을 나타낸다

  • 서버는 websocket이 열려있는 지 확인하기 위해 101 헤더로 응답함
    • 서버는 클라이언트의 Sec-Websocket-Key 에 대해서 GUID 키를 붙여 sha1로 해슁한 값을 Sec-Websocket-Accept 헤더로 응답함
    • Sec-Websocket-Extensions 값으로 클라이언트는 자신이 제공하는 확장 기능에 대해 알려줄 수 있음
    • handshaking 요청에서 Sec-WebSocket-Protocol 을 사용하여 STOMP와 같은 높은 레벨의 메시징 프로토콜을 사용할 수 있음

2. SockJS

  • 우선 WebSocket을 시도하고, upgrade 헤더요청에 실패할 경우 HTTP Streaming, Long-Polling 같은 HTTP 기반의 다른 기술로 전환하는 fallback 기술로, Websocket interaction을 에뮬레이트하고 같은 api를 노출하는 방식
  • 이러한 기술을 쓰는 이유는 모든 브라우저가 웹소켓을 지원하지는 않기 때문이다
  • Spring에서는 Servlet 스택에서 SockJS 프로토콜에 대한 서버 및 클라이언트를 모두 제공한다

WebSocket Emulation Process

  • SockJS Client는 서버의 기본 정보를 얻기 위해 “GET /info”를 호출함
    • /info 요청의 의미는 서버의 Websocket 지원 여부, Cookie 지원 여부 및 CORS 를 위한 Origin 정보를 응답받기 위함이다
    • 응답받은 메시지를 토대로 앞으로 통신에 사용할 프로토콜을 결정함
      • WebSocket 사용 가능하면 WebSocket 사용
        • WebSocket 사용 불가능할 경우
          • Options의 Transports 항목에 HTTP streaming 설정이 존재하면 HTTP streaming 사용
          • Options의 Transports 항목에 HTTP Long Polling이 존재한다면, HTTP Long Polling 사용
          • Optional 기능 사용시, 서버에 전송하는 요청 URL Schema

메시지 형식

  • /info 요청 이후 핸드쉐이크가 성공할 경우 커넥션 유지를 위해 xhr?t 의 요청을 계속해서 보냄
  • Message Frame 크기를 최소화하기 위해 노력함
    • open frame (o) : 초기에 전송하는 메시지 프레임
    • heartbeat frame (h) : 커넥션 유지 여부를 확인하는 heartbeat
    • Array of json-encoded messages (a) : 메시지 프레임을 복수개로 전달받을 경우 사용됨
      • 초기 연결시 전달받음
      • ex). a["CONNECTED↵version:1.2↵heart-beat:0,0↵user-name:test2@naver.com"]
    • close frame (c)

3. STOMP

  • Streaming Text Oriented Message Protocol
    • 토픽 구독방식
    • pub/sub 구조로 되어있으며 브로커가 메시지를 받아 구독자에게 전송함
  • Frame 기반 프로토콜
    • 메시징 전송을 효율적으로 하기위한 websocket 위에서 동작하는 프로토콜
    • 텍스트 기반이며 메시지큐의 동작을 프로토콜로 정의하여 사용됨
    • 통신 메시지의 헤더에 값을 세팅할 수 있어 헤더 값 기반 통신시 인증처리 구현도 가능함

 

메시지 형식

Command + Header + Body의 프레임 단위

SEND
destination:/queue/trade
content-type:application/json
content-length:44

{"action":"BUY","ticker":"MMM","shares",44}^@
MESSAGE
message-id:nxahklf6-1
subscription:sub-1
destination:/topic/price.stock.MMM

{"ticker":"MMM","price":129.45}^@

 

메시지 전체 흐름

 

  • 스프링에서 메세지를 받음
  • 외부 Broker에게 메시지 전달
  • Websocket으로 연결된 구독자 Client에게 전달
    • send한 메시지에 대해 handler를 붙여 처리 가능

 

 

출처 :

https://wedul.site/692

 

Spring webSocket with stomp 기본 개념 정리

웹 소켓 관련하여 정리가 필요하여 spring document 내용을 발췌 하여 필요한 부분만 정리했다. 공유용 보다는 개인적인 정리용으로 크게 도움이 되지 않을 수 있다. 출처 : https://docs.spring.io/spring-fram

wedul.site

출처 : https://docs.spring.io/spring-fram

https://sjh836.tistory.com/166

https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/html/websocket.html https://d2.naver.com/helloworld/1336 

https://ichi.pro/ko/socket-ioleul-sayonghayeo-silsigan-chaeting-aepeullikeisyeon-eul-guchughayeo-websocket-e-daehae-baeun-geos-4234324206852

https://docs.spring.io/spring-framework/docs/4.3.x/spring-framework-reference/html/websocket.html**
https://docs.google.com/presentation/d/1Y9q0rkgT9qIgjDXr_vILJUtlwmgc9Zj_BgNLBfyNBnU/edit?fbclid=IwAR2P0i4GnJbjVpF00WdBII6DDcYAI2vdIP7GUAFhZGqBmxRNF1WAtgUOCec#slide=id.g512074322f_0_915**

http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-26