1. 개요

여러 채용 공고나 주변 이야기를 통해 웹소켓 얘기가 심심치 않게 들려왔기 때문에 실무나 프로젝트에서 자주 쓰이는 기술이라고 생각했다. 그래서 개발 실력 향상과 웹소켓이 어떻게 동작하는지 궁금해 웹소켓 공부 후 프로젝트에 적용하게 되었다.

 

사실상 개인 프로젝트 화면을 만들기 시작한 이유가 바로 이 웹소켓 때문이다(원래는 api 설계만 하고 프로젝트를 끝내려고 했다). 찾아보니 웹소켓이 클라이언트, 서버 양측에서 모두 설정해야 동작할 수 있기에 웹소켓을 정확하게 동작시키기 위해 view 구현까지 추가했다.

 

2. 웹소켓이란? 

OSI 7계층(애플리케이션 계층) 프로토콜

Socket Connection을 유지한 채로 실시간으로 양방향 통신 혹은 데이터 전송이 가능한 프로토콜이다.

주로 채팅, SNS, 화상회의, 게임, 실시간 주식거래 등의 분야에서 웹소켓이 사용된다.

 

웹소켓 vs HTTP

 

웹소켓 

  • stateful connection
  • 양방향 통신
  • 한 번 성립된 연결로 계속 클라이언트와 서버간 통신 가능
  • TCP 커넥션을 매번 실행할 필요가 없어 커넥션 시간/비용을 아낄 수 있다
  • 통신 
    • 연결이 계속 유지되어 요청없이 상대가 보낸 메시지를 계속 듣고 있기만 하면 된다
  • 전송 데이터
    • 최초 연결 시 : 최초 handshake 과정에는 HTTP 프로토콜을 이용하기 때문에 HTTP와 유사한 양의 데이터를 주고 받는다
    • 연결 수립 후 : 간단한 데이터가 오고가기에 모든 데이터를 주고 받을 필요가 없어 HTTP에 비해 통신 비용이 비교적 저렴하다

 

HTTP

    • stateless connection
    • 단방향 통신
    • 매번 클라이언트와 연결을 맺고(3-way handshake) 끊는 과정(4-way handshake)을 거친다
    • 통신 
      • (요청, 응답)이 하나의 쌍을 이루어 통신하고, 원하는 리소스에 대해 클라이언트가 서버에 요청해야한다
  • 전송 데이터 
    • 요청/응답 때마다 많은 정보를 메시지에 담아서 전송해야 한다
    • 이는 실시간성을 요하는 서비스에는 부담되는 구조이다

 

Spring에서 웹소켓 사용하기

Spring은 웹소켓을 위해 SockJS를 제공한다. 

웹소켓을 지원하지 않는 환경에서는 SockJS, socket.io 라이브러리를 사용하면 된다.

 

3. STOMP란 ?

스프링에 websocket 적용하기 위해서는 STOMP 프로토콜에 대해서 이해하고 넘어가야 한다.

  • Simple Text Oriented Messaging Protocol
  • 메시지 브로커를 활용해 pub/sub 방식으로 클라이언트와 서버가 메시지를 쉽게 주고 받을 수 있는 프로토콜
  • Pub-Sub(발행-구독) : 발신자가 메시지를 발행(publish)하면 수신자(subscribe)가 그것을 수신하는 메시징 패러다임
  • 메시지 브로커 : 발신자의 메시지를 받아와서 수신자들에게 메시지를 전달하는 어떤 것
  • 웹소켓 위에 얹어 함께 사용할 수 있는 하위 프로토콜
  • STOMP 프로토콜은 웹소켓 뿐만 아니라 몇몇 양방향 통신 프로토콜에서 사용할 수 있다.
  • Spring은 웹 소켓 위에 STOMP 프로토콜을 얹어 사용하는 방법을 지원해준다.

 

웹소켓 위에 STOMP를 얹어서 사용하는 이유가 무엇일까?

웹소켓은 메시지를 Text, Binary 형식으로 주고받기 때문에 따로 정해진 형식이 없다. 그렇기에 데이터 파싱을 추가로 구현해줘야 하기 때문에 프로젝트 규모가 커질 경우 로직이 겹치거나 복잡해질 수 있다. 

 

따라서 STOMP를 사용하면 웹소켓 데이터 형식을 통일해 파싱 단계를 편리하게 이용할 수 있기 때문에 웹소켓 위에 STOMP를 얹어서 사용하는 것이 좋다.

 

메시지 비교 : 웹소켓 vs STOMP 

웹소켓

날것의 데이터만 전송되는 것을 확인할 수 있다

 

STOMP

커맨드, 헤더, 바디의 형태로 데이터가 오가는 것을 확인할 수 있다. 이를 frame 기반이라고 한다.

 

스프링이 STOMP 프로토콜을 사용할 때의 동작흐름

메시지를 보내는 발신자(pub)와 메시지를 받으려는 구독자(sub)가 있고, 구독자는 /topic 경로를 구독하고 있다. 

 

/topic 경로

발신자는 /topic을 destination 헤더로 넣어 메시지 브로커를 통해 메시지를 구독자들에게 곧바로 송신할 수 있다.

 

/app 경로

서버 내에서 어떤 가공처리가 필요하다면 /app 경로로 메시지를 송신할 수 있다

서버가 가공 처리가 끝난 데이터를 /topic 경로를 담아 메시지 브로커에게 전달하면

메시지 브로커는 전달받은 메시지를 /topic을 구독하는 구독자들에게 최종적으로 전달한다 

 

순서 정리

  • /app
  • -> 서버 가공 후 /topic 경로로 메시지 전송
  • -> 메시지 브로커
  • -> /topic 구독자에게 메시지 전송

 

4. STOMP - client

 

destination 헤더

메시지를 어디에 전송할지, 어디에서 구독할지 알려주는 헤더이다. 

SEND(메시지 전송), SUBSCRIBE(수신 메시지에 관심 표현) 같은 명령을 사용하기 위해서는 destination 헤더가 필요하다.

  • SUBSCRIBE 메서드 사용시 destination 헤더로 메시지 종착지를 명시해서 보내야 한다(로그 사진)

 

destination 헤더 값

destination 값은 어떤 문자열이든 될 수 있기에 서버에서 정한 규칙에 따라 설정할 수도 있다. 경로 prefix는 topic/…, queue/…, app/… 등이 될 수 있고, 구체적으로 의미하는 바는 다음과 같다. 

 

  • topic/… : 메시지 브로커로 흐르게 하기 위한 라우팅, publish-subscribe 방식의 one to many 일 때 사용
  • queue/… : 메시지 브로커로 흐르게 하기 위한 라우팅, point-to-point 방식의 one to one 일 때 사용
  • /app/… : 서버측에서 메시지를 처리할 수 있도록 annotated method로 흐르게 하기 위한 라우팅(본 프로젝트에서는 이 prefix를 사용했다)

 

클라이언트에서 destination에 /app prefix를 주었을 때 흐름

  1. /app destination으로 메시지 전송 
  2. @messagemapping된 스프링 컨트롤러에서 요청 수신 
  3. 서버에서 작업 처리 
  4. /topic이라는 prefix를 통해 메시지 브로커에 메시지 전송 
  5. STOMP MESSAGE 메서드 이용해 특정 topic을 subscribe한 대상에게 response 전송

 

클라이언트에서 destination에 /topic prefix를 주었을 때 흐름

  1. 매핑된 스프링 스크롤러를 안 거치고 메시지 브로커에 직접 접근한다

 

Spring 속 STOMP 프로토콜의 장점

  1. STOMP 프레임 단위를 정해주기 때문에 하위 프로토콜, 컨벤션을 따로 정의할 필요가 없다
  2. @Controller 어노테이션을 사용하면 되기 때문에 연결 주소마다 새로운 Handler 구현하고 설정해줄 필요가 없다
  3. 외부 Messaging Queue(Kafka, RabbitMQ,,,) 사용할 있다
  4. Spring Security 사용해 오고 가는 메시지에 대한 보안설정이 가능하다

 

 

5. 마치며

여기에서 공부한 WebSocket, SockJS, STOMP를 스프링에 적용한 내용은 다음 포스팅에서 확인할 수 있다. 

 

참고

https://velog.io/@guswns3371/WebSocket-Spring

https://nobase2dev.tistory.com/25

 

 

+ Recent posts