YoungSoo

[WebSocket] 명령과 조회를 분리하는 CQRS로 개발하기 - 2 본문

프로젝트

[WebSocket] 명령과 조회를 분리하는 CQRS로 개발하기 - 2

YoungSooSoo 2023. 7. 5. 23:10

WebSocket에 대해 알아보자!

웹소켓이란, TCP 접속에 전이중 통신 채널(양방향 통신 채널)을 제공하는 컴퓨터 통신 프로토콜이다. 이를 사용하면 서버와 브라우저 간에 연결된 상태로 데이터를 교환할 수 있습니다. 데이터는 패킷(packet)의 형태로 전달이 되고, HTTP 요청을 하지 않고 양방향 통신이 이루어집니다.

  • 패킷이란?
    • 네트워크 통신에서 데이터를 전송하는 최소 단위

WebSocket 패킷은 헤더(Header)와 페이로드(Payload)로 구성됩니다.

헤더에는 패킷의 메타데이터와 제어 정보, 패킷의 길이, 압축 여부, 메시지 타입 등의 정보가 헤더에 포함될 수 있습니다.

페이로드는 실제 전송되는 메시지, 이벤트 데이터, 상태 정보 등이 페이로드에 포함됩니다.

 

왜 WebSocket을 사용해?

WebSocket은 전이중 통신 채널을 제공해서 실시간성을 보장할 수가 있습니다. WebSocket을 기반으로 실시간으로 채팅 서비스를 개발하려고 합니다. 하지만 WebSocket은 연결을 유지해야 하기에 서버의 자원 부하가 증가할 수 있습니다. 이러한 서버 부하를 해결해 주기 위해서는 메시지 큐를 도입해 비동기로 메시지 처리를 분산하고 빠르게 처리할 수 있습니다.

 

HTTP를 통해서는 실시간성 통신이 불가능한가?

아닙니다. HTTP를 통해서도 실시간성을 보장하는 기술이 존재합니다!

Polling, Long Polling, Streaming와 같은 기술이 있습니다. 하지만 HTTP는 기본적으로 요청/응답 방식으로 동작하기 때문에 실시간성으로 동작하는 애플리케이션엔 WebSocket이 적절하다고 생각을 했습니다.

 

WebSocket의 동작방식 - 핸드 쉐이킹

STOMP(Simple Text Oriented Messaging Protocol)

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

  • Pub - Sub(발행 - 구독) 방식으로 발신자가 메시지를 발행하면 수신자가 그것을 수신하는 메시징 패러다임
  • 메시지 중개인: 발신자의 메시지를 받아와서 수신자들에게 메시지를 전달하는 것

WebSocket 위에 함께 사용할 수 있는 하위(서브) 프로토콜

 

STOMP를 왜 같이 사용해?

WebSocket만 사용한다면 키-값의 형태로 주고받아지지만 STOMP를 같이 사용하게 되면 커맨드, 헤더, 바디의 형태처럼 구조화된 메시지를 전송하고, 메시지 큐와 통합이 가능하게 됩니다. 또한 연결 주소마다 새로 핸들러를 구현하고 설정해 줄 필요가 없습니다.

동작흐름

발신자가 구독자에게 메시지를 보낼 때 메시지의 가공이나 처리가 필요하다면 MessageHandler를 통해 메시지를 처리하고 MessageBroker를 통해 다시 응답해 줄 수도 있고, 필요하지 않다면 바로 구독자에게 전송도 가능합니다.

 

실습

아래 링크를 통해 Spring Boot에서 WebSocket과 STOMP를 통해 실습을 진행해 주었습니다.

https://spring.io/guides/gs/messaging-stomp-websocket/

 

Getting Started | Using WebSocket to build an interactive web application

In Spring’s approach to working with STOMP messaging, STOMP messages can be routed to @Controller classes. For example, the GreetingController (from src/main/java/com/example/messagingstompwebsocket/GreetingController.java) is mapped to handle messages t

spring.io

의존성 추가

	implementation 'org.webjars:webjars-locator-core'
	implementation 'org.webjars:sockjs-client:1.0.2'
	implementation 'org.webjars:stomp-websocket:2.3.3'
	implementation 'org.webjars:bootstrap:3.3.7'
	implementation 'org.webjars:jquery:3.1.1-1'

Message 클래스 생성

public class HelloMessage {

  private String name;

  public HelloMessage() {
  }

  public HelloMessage(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}


public class Greeting {

  private String content;

  public Greeting() {
  }

  public Greeting(String content) {
    this.content = content;
  }

  public String getContent() {
    return content;
  }

}

 

컨트롤러 생성

@Controller
public class GreetingController {


  @MessageMapping("/hello")
  @SendTo("/topic/greetings")
  public Greeting greeting(HelloMessage message) throws Exception {
    Thread.sleep(1000); // simulated delay
    return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
  }

}

 

STOMP 메시징을 위한 Config 구성

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

  @Override
  public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    config.setApplicationDestinationPrefixes("/app");
  }

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/gs-guide-websocket").withSockJS();
  }

}


마무리


이렇게 간단하게 입력된 메시지가 화면에 출력되는 것을 확인할 수 있었습니다. 처음에는 @MessageMapping를 사용한다면 "Rest API와 비슷하게 동작하는 것이 아닐까?"라는 생각이 들었습니다. 하지만 @MessageMapping은 STOMP 기반의 메시징 엔드포인트를 처리하기 위한 어노테이션이고, WebSocket 연결을 통해 클라이언트로부터 수신된 메시지를 처리하는 핸들러 메서드에 적용됩니다. 그래서 이 어노테이션을 사용하면 실시간 메시징을 위해 사용되어 동작합니다. 다음 게시물은 실제 채팅 서비스를 구현하는 과정을 게시하려고 합니다!!