Spring WebSocket, STOMP

murphytklee·2023년 5월 27일
3
post-thumbnail

0. Socket, WebSocet이란?

  • 소켓(Socket) : 네트워크상에서 동작하는 프로그램 간 통신의 종착점. 1대1 통신의 경우 양 측다 소켓이 존재해야 통신이 가능하다. 현재 대부분의 통신은 인터넷 프로토콜(TCP, UDP)에 기반하고 있으므로 대부분의 네트워크 소켓은 인터넷 소켓이다.
  • 웹 소켓(Web Socket) : 웹소켓은 하나의 TCP 접속에 전이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜이다. 그리고 HTTP나 HTTPS 위에서 동작하도록 설계되었으며, 따라서 포트는 80번 혹은 443번이다. HTTP 프로토콜과 구별은 되지만 호환이 된다.

웹소켓은 전이중 통신 채널을 제공해서 실시간성을 보장할 수 있다.

웹소켓을 사용해야만 실시간성을 사용할 수 있을까?

  • HTTP에서도 실시간성을 보장하는 기법이 존재한다.
    • Polling, Long Polling, Streaming

1. HTTP vs WebSocket 차이

1.1 Http와 WebSocket Protocol의 가장 큰 차이는 수립된 커넥션을 어떻게 하느냐 이다.

  • Http는 비 연결성 프로토콜이다. 클라이언트가 요청을 보낼 때마다 연결을 맺고 응답을 받은 후 연결을 끊어버린다.
  • 반면에 웹소켓의 경우 한번 연결을 맺고 나면 어느 한쪽에서 연결을 끊으라는 요청을 보내기 전까지 연결을 유지한다. → 매번 연결할 때마다 발생하는 비용을 줄일 수 있다.

1.2 통신하는 방식

  • http는 요청과 응답이 한 쌍을 이루는 구조로 통신을 한다. → 내가 원하는 어떤 것을 얻기 위해서는 항상 그것을 달라고 요청을 해야한다.
  • 웹소켓은 연결이 계속 유지되고 있는 상태이기 때문에, 연결된 채널, 소켓을 통해 상대가 보내오는 메세지를 듣기만 하면 된다.

1.3 데이터의 양

  • 웹소켓을 처음 핸드쉐이크 하는 과정에서는 Http를 사용하기 때문에 유사한 양의 정보를 주고 받게 되지만, 한번 연결이 수립되고 나서는 간단한 메세지들만 오고가는 것을 확인할 수 있다. → 웹소켓을 사용하면 통신에 오가는 비용을 많이 줄일 수 있다.

2. Spring-WebSocket

2.1 WebSocketConfig

  • 스프링에서 웹소켓을 사용하려면 클라이언트가 보내오는 통신을 처리할 핸들러가 필요하다.
  • 필요에 따라 구현한 핸들러를 웹소켓이 연결될 때 핸드쉐이크할 주소와 함께 인자로 넣어주면 된다.

핸드쉐이크란?

  • 클라이언트와 서버 간의 연결을 설정하는 초기 프로세스
  • 클라이언트와 서버 간의 후속 통신을 위한 기반을 설정하여 데이터 교환, 작업 수행 및 연결 유지를 가능하게 한다.
  • WebSocket 프로토콜 컨텍스트에서 핸드셰이크는 클라이언트와 서버 간의 호환성을 확인하고 HTTP 프로토콜에서 WebSocket 프로토콜로의 전환을 허용한다.
  • 핸드셰이크가 성공적으로 완료되면 클라이언트와 서버는 WebSocket 연결을 통해 양방향으로 데이터를 교환할 수 있다.

2.2 WebSocketHandler

  • 웹소켓은 기본적으로 텍스트 또는 바이너리 타입을 지원한다.
  • 필요에 따라 TextWebSocketHandler 또는 BinaryWebSocketHandler라는 Spring이 기본적으로 제공하는 클래스를 상속하고 구현하면 된다.

이때 메소드 인자로 받아오는 WebSocketSession이 있다.

→ 흔히 말하는 http 세션과 다른 맥락이다.

→ 간단히 말하면 웹소켓이 연결될 때 생기는 연결정보를 담고있는 객체라고 보면 된다.

  • 또한 핸들러에서 웹소켓 통신에 대한 처리를 하기 위해 이 세션들을 컬렉션으로 담아 관리하는 경우가 많다.
  • 위에 코드를 보면 커넥션이 맺어질 때 컬렉션에 웹소켓 세션을 추가하고 커넥션이 끊어질 때 제거를 하고 있다.

2.3 Cors

  • 스프링에서 웹소켓을 사용할 때 same-origin만 허용하는 것이 기본 정책이라고 한다.
  • withSockJS 한줄을 추가하는 것 만으로도 SockJs 라이브러리를 사용하도록 설정할 수 있다. → webSocket을 지원하지 않는 브라우저를 위해

3. STOMP

3.1 STOMP란

  • Simple Text Oriented Messaging Protocol

  • 메세지 브로커를 활용하여 쉽게 메세지를 주고 받을 수 있는 프로토콜

    • Pub, Sub (발행, 구독) : 발신자가 메세지를 발행하면 수산지가 그것을 수신하는 메시징 패러다임
    • 메세지 브로커 : 발신자의 메세지를 받아와서 수신자들에게 메세지를 전달하는 어떤 것
  • 웹 소켓 위에 얹어 함께 사용할 수 있는 하위(서브) 프로토콜

  • STOMP는 웹소켓만을 위해서 만들어진 것은 아니다.

  • 웹소켓과 같은 몇몇 양방향 통신 프로토콜에서 함께 사용할 수 있다는 것

  • Spring이 웹소켓 위에 STOMP를 얹어 사용하는 방법을 지원한다는 것이 중요

3.2 왜 STOMP를 같이 사용하는지?

  • WebSocket만 구현해도 간단한 어플리케이션을 만들 수 있다.

  • 하지만 프로젝트가 커지고 협업하는 사람들이 많아진다면 어떤 문제점들이 있을까

    • 클라이언트와 서버가 서로 어떤 형식으로 메세지를 주고 받을지,
    • 메세지 타입은 어떻게 명시할 것인지,
    • 메세지의 본문과 설정 정보와 같은 데이터들은 어떻게 서로 구분할 것인지 등등을 정의해야 하고 그것들을 파싱하는 로직들도 구현해야 한다.
  • STOMP를 사용하면 형식을 따로 고민할 필요도, 파싱하기 위한 코드를 구현할 필요도 없다.

    • STOMP는 프레임이라고 해서 Command, header, body로 이미 형식을 정의해 두었다.

  • 좌 : WebSocket, 우 : STOMP

3.3 통신 흐름

발신자는 구독자들에게 메세지를 보내고 싶어 하고, 구독자들은 /topic 이라는 경로를 구독하고 있다고 가정을 하면,

  1. 발신자는 바로 /topic을 destination 헤더로 넣어서 메세지를 송신할 수도 있지만, 서버 내에서 가공이나 처리가 필요하다면 /app으로 메세지를 송신하게 되고 /topic 이라는 경로에 담아 다시 전송하게 되면 이 메세지는 브로커에 의해 최종적으로 /topic을 구독하고 있는 구독자들에게 전달하게 된다.
  2. 서버의 어떤 처리나 가공이 필요없다면 발신자가 바로 브로커를 통해 구독자에게 보내는 것도 가능하다.

3.4 Spring - WebSocketMessageBroker

STOMP는 메세지 브로커를 사용하고 메세지 브로커를 설정하는 부분이다.

  • 파라미터로 받고있는 MessageBrokerRegistry 값의 의미는 해당 값이 prefix로 붙은 메시지가 송신되었을 때 그 메시지를 메세지 브로커가 처리하겠다는 의미

  • 메세지가 /queue 혹은 /topic 이 앞에 붙은 경로로 송신되었을 때 심플 브로커가 그 메세지들을 받고 구독자 들에게 전달

  • 일종의 컨벤션으로 /queue 라는 prefix는 메세지가 일대일로 갈때, /topic 은 메세지가 일대다로 브로드 캐스팅 될 떄 주로 사용

  • setApplicationDestinationPrefixes() 는 바로 브로커로 가는 경우가 아니라 메세지의 처리나 가공이 필요한 경우 핸들러로 타게 할 수 있다는 부분

  • registerStompEndpoints 인자로 들어가는 경로는 웹소켓의 /user 처럼 처음 웹소켓 핸드쉐이크를 위한 주소이다.

  • cors 설정과 sockJs 설정을 그대로 해줄 수 있다.

  • 다른점은 여기서는 handler를 설정해 줄 필요가 없다.

  • STOMP를 사용하게 되면 아까 웹소켓만을 사용했을 때와는 다르게 하나의 연결 주소마다 핸들러 클래스를 구현하고 설정해줄 필요 없이 컨트롤러 방식으로 간편하게 사용하기 때문이다. → STOMP를 Spring에서 사용하는 장점

3.5 Message Handler

  • STOMP를 사용하게 되면 따로 상속을 받거나 할 필요없이 컨트롤러 어노테이션을 사용할 수 있다.

  • MessageMapping 어노테이션은 기존에 알던 requestMapping과 비슷한 역할을 한다.

  • → Http 요청이 들어왔을 때 그 요청의 경로에 맞는 핸들러에게 처리를 위임하듯이 STOPM 웹소켓 통신을 통해 메시지가 들어왔을 때도 메시지의 destination 헤더와 MessageMapping에 설정된 경로가 일치하는 핸들러를 찾고, 그 핸들러가 처리하게 된다.

  • 여기에서는 아까 Configuration에서 설정해둔 /app 이라는 prefix와 합쳐져서 /app/hello destination 헤더를 가진 메시지들이 이 핸들러를 거치게 된다.

  • @SendTo 어노테이션의 경우 핸들러에서 처리를 마친 후 반환 값을 /topic/greeting 의 경로로 메세지를 보내겠다는 의미

3.6 STOMP를 Spring에서 사용하는 장점

  • 하위 프로토콜 혹은 컨벤션을 따로 정의할 필요가 없다.
  • 연결 주소마다 새로 핸들러를 구현하고 설정해줄 필요가 없다.
  • 외부 Messaging Queue를 사용할 수 있다. (RabbitMQ, 카프카, …)
  • Spring Security를 사용할 수 있다.




Reference

0개의 댓글