WebSocket & STOMP 그룹 채팅방 구현하기

2023. 5. 16. 23:55Spring 기초

프로젝트에서 WebSocket과 STOMP 프로토콜을 이용해 그룹채팅방 기능을 구현했습니다.
스프린트 초반에는 댓글과 Polling 기법을 이용해 유저 간 소통하도록 하였습니다. 하지만 Polling 기법의 단점과 애플리케이션 특성을 고려하였을 때 실시간 채팅이 필요하다고 생각되어 고도화를 진행했습니다.

 


Polling

출처: https://rubberduck-debug.tistory.com/123

 

클라이언트가 일정한 주기마다 서버로 요청을 보내는 방식입니다.
서버 데이터의 변경 여부와 상관없이 주기적으로 요청을 보내므로, 불필요한 요청에 따른 네트워크 부하가 늘어납니다.

또한 요청 인터벌을 너무 길게 잡을 경우 실시간성이 떨어지며, 짧게 잡을 경우 서버에 부하가 올라가는 trade off가 존재합니다. 

실시간성이 중요하지 않거나 일정한 주기로 갱신되는 데이터를 조회할 때는 사용할 수 있을 것입니다. 하지만 제 프로젝트에서는 최대 8명의 사용자가 구체적인 약속 시간과 장소를 정해야 한다는 점을 고려했을 때,  Polling + 댓글 방식은 사용하기 어렵다는 판단을 내렸습니다.

 


WebSocket

  • 하나의 TCP 연결을 통해 양방향 통신을 제공하는 프로토콜입니다.
    즉, 서버와 클라이언트가 소켓 연결을 유지하면서 양방향 통신을 가능케하는 기술이라고 할 수 있습니다.
  • 모든 HTTP를 사용한 통신은 클라이언트가 먼저 요청을 보내고, 그 요청에 따라 웹 서버가 응답하는 형태입니다. 서버는 응답을 보낸 뒤 연결을 종료하는 '비연결성'을 갖는 것이 HTTP의 특징입니다.
    WebSocket은 이런 HTTP의 비연결성을 극복하기 위해 고안된 기술입니다.

따라서 클라이언트가 접속 요청을 한 뒤, 웹 서버가 요청에 대한 응답을 하고서 연결을 끊는 것이 아니라 Connection을 그대로 이어갑니다. 따라서 클라이언트의 요청 없이도 서버에서 데이터를 전달할 수 있습니다.

 

WebSocket의 Handshake

출처: https://tecoble.techcourse.co.kr/post/2020-09-20-websocket/

WebSocket의 특징 중 하나는 접속까지는 HTTP로 연결하며, 이후 통신은 업그레이드 된 WebSocket 프로토콜로 통신한다는 점입니다. 따라서 HTTP, HTTPS 포트(40, 443) 위에서 동작하며 별도의 포트를 오픈할 필요가 없습니다.

웹소켓도 TCP/IP 위에서 동작하므로 서버와 클라이언트는 웹소켓을 연결하기 전에 Handshake를 통해 최초 TCP/IP 접속이 필요합니다. 이후 클라이언트가 웹소켓을 연결하는 Handshake 요청을 서버로 보냅니다. 

 

WebSocket Request

 

요청 헤더에는 ConnectionUpgrade가 존재하는데요, Upgrade는 다른 프로토콜로 전환해달라는 요청이며, Upgrade에는 교체할 프로토콜이 명시되어 있었습니다. 

이외에는
Sec-Websocket-Version: 클라이언트가 사용하려고 하는 웹소켓의 버전

Sec-Websocket-Extensions: 클라이언트가 Websocket 연결에서 사용할 수 있는 확장기능. 여기서는 permessage-deflate로, 데이터 압축을 위해 zlib 압축 알고리즘을 사용합니다. 클라이언트와 서버가 permessage-deflate 확장을 지원하는 경우, WebSocket 연결에서 전송되는 메시지는 압축되어 전송될 수 있습니다.

Sec-Websocket-Key:  클라이언트와 서버 간의 핸드셰이크 과정에서 사용되는 보안 키로, WebSocket 연결의 보안성과 무결성을 강화하는 역할을 합니다. 서버는 이 값을 해싱 및 인코딩한 뒤 클라이언트로 Sec-WebSocket-Accept를 전송하며, 클라이언트는 WebSocket 연결의 보안을 확인하게 됩니다.

 

WebSocket Response

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

응답 헤더에도 마찬가지로 Upgrade와 Connection 항목이 존재합니다.
Sec-WebSocket-Accept는 이전에 Request에 담긴 Sec-Websocket-Key에 대한 응답입니다. 

 

이렇게 WebSocket Handshake까지 완료되면 서버와 클라이언트는 웹소켓 프로토콜을 이용해 양방향 통신이 가능합니다.


STOMP (Simple/Stream Text Oriented Message Protocol)

  • WebSocket 위에서 동작하는, 메시지 전송을 효율적으로 하기 위해 고안된 프로토콜입니다.
    WebSocket만 이용할 경우 해당 메시지가 어떤 요청인지, 어떤 포맷으로 오는지, 메시지 통신 과정을 어떻게 처리해야 하는지 정해져 있지 않으므로 각각 구현해야 합니다.

  • STOMP는 메시지의 형식, 유형, 내용 등을 정의해주는 프로토콜이라고 할 수 있습니다. STOMP를 사용하게 되면 단순한 Binary, Text가 아닌 규격을 갖춘 메시지를 보낼 수 있게 됩니다.

  • 기본적으로 pub / sub 구조로 되어있어서, 메시지를 전송하는 부분과 메시지를 발행하는 부분이 확실히 구분되어 있습니다.

  • @MessageMapping과 같은 어노테이션을 이용해서, 메시지를 발행할 때 엔드포인트 별로 분리해서 관리할 수 있습니다. 저희는 식사 모임별로 그룹 채팅방이 필요했기에, @MessageMapping 어노테이션을 이용해 식사 모임별로 채팅방을 관리하였습니다. 

 

형식

COMMAND
header1:value1
header2:value2

Body^@

Command: Send, Subscribe를 지정할 수 있는 명령입니다.
header : 기존의 WebSocket으로는 표현이 불가능한 header를 작성할 수 있는 부분입니다
"destination" 헤더를 이용해 메세지를 보내거나(SEND), 구독(SUBSCRIBE)할 수 있습니다.

 

클라이언트가 5번 채팅방을 구독하는 경우

SUBSCRIBE
destination: /topic/chat/room/5
id: sub-1

^@

클라이언트가 5번 채팅방에 메시지를 보내는 경우

SEND
destination: /pub/chat/room/5
content-type: application/json

{"type": "MESSAGE", "writer": "clientB"} ^@

5번 채팅방에 메시지를 보낼 경우, 메시지 브로커는 5번 채팅방을 구독(Subscribe)하고 있는 모든 유저에게 메시지를 전송합니다.


구체적인 구현부와 트러블 슈팅은 다음 포스팅을 통해 올리겠습니다.

아래는 구현된 채팅방 모습입니다. 

 

 

 

소스코드

https://github.com/prgrms-web-devcourse/Team-DarkNight-Kkini-BE

 

GitHub - prgrms-web-devcourse/Team-DarkNight-Kkini-BE: 배고프면 합류하라! 위치기반 밥 친구 만들기 플랫폼

배고프면 합류하라! 위치기반 밥 친구 만들기 플랫폼 🍚. Contribute to prgrms-web-devcourse/Team-DarkNight-Kkini-BE development by creating an account on GitHub.

github.com

 

참고자료

https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism#upgrading_http1.1_connections

 

Protocol upgrade mechanism - HTTP | MDN

The HTTP/1.1 protocol provides a special mechanism that can be used to upgrade an already established connection to a different protocol, using the Upgrade header field.

developer.mozilla.org

https://brunch.co.kr/@springboot/695

 

Spring Websocket & STOMP

오랫만에 작성하는 기술블로그 포스팅입니다. 이 글에서는 스프링 부트 기반의 웹소켓 및 STOMP에 대해서 설명합니다. 이 글을 읽기 위해서는 기본적인 HTTP 지식이 있어야 하며, 스프링 프레임워

brunch.co.kr

https://dev-gorany.tistory.com/212

 

[Spring Boot] WebSocket과 채팅 (1)

일전에 WebSocket(웹소켓)과 SockJS를 사용해 Spring 프레임워크 환경에서 간단한 하나의 채팅방을 구현해본 적이 있다. [Spring MVC] Web Socket(웹 소켓)과 Chatting(채팅) 기존 공부 용도의 게시판(?)에 여러

dev-gorany.tistory.com