이전에 구현한 웹소켓 채팅 서버에 MessageBroker
를 적용하여 메시지 처리 시 STOMP
프로토콜로 전송하도록 개선해보겠습니다.
STOMP (Simple Text Oriented Messaging Protocol) 는 메세징 전송을 효율적을 하기 위해 탄생한 프로토콜입니다. 메시지 브로커를 통해 간단한 텍스트 기반의 메시징을 지원하며, 다양한 프로그래밍 언어 및 플랫폼에서 쉽게 사용할 수 있도록 설계되었습니다.
STOMP는 메시지 브로커와 함께 사용되며, 클라이언트가 발행한 메시지를 적절한 구독자에게 라우팅합니다.
메시지 브로커는 특정 토픽이나 큐에 클라이언트가 구독한 후, 해당 토픽 또는 큐로 발행된 메시지를 구독자에게 전달합니다. WebSocket 기반 애플리케이션에서 클라이언트 간의 메시지 전달을 관리하는 중요한 역할을 합니다. 메시지 브로커를 설정하기 위해 Spring Boot의 WebSocket 지원을 사용하며, STOMP 프로토콜을 통해 메시지 브로커와의 통신을 처리합니다.
STOMP에서 모든 통신은 프레임
이라고 불리는 메시지 단위로 이루어집니다. 각 프레임은 특정 명령을 나타내며, 서버와 클라이언트 간의 통신을 관리합니다.
클라이언트는 특정 토픽이나 큐를 구독하여, 해당 목적지로 발행된 메시지를 수신할 수 있습니다.
클라이언트는 특정 목적지로 메시지를 발행(SEND)할 수 있으며, 메시지 브로커는 해당 목적지에 구독한 클라이언트에게 메시지를 전달합니다.
클라이언트는 메시지를 받았을 때, 수신을 확인하는 ACK(Acknowledge) 프레임을 서버로 보낼 수 있습니다. 이를 통해 메시지가 성공적으로 수신되었음을 보장할 수 있습니다.
NACK(Negative Acknowledge)은 메시지가 제대로 처리되지 않았음을 나타내며, 브로커는 메시지를 다시 전달하거나 다른 처리를 수행할 수 있습니다.
기존에 작성했던 WebSocketConfigurer 구현체 대신에 WebSocketMessageBrokerConfigurer 를 구현합니다.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {
@Value("${spring.application.name}")
private String projectName;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint(this.projectName).withSockJS();
}
}
configureMessageBroker method 를 override 하여 메시지 브로커를 설정합니다.
클라이언트가 서버로 메시지를 보낼 때 경로 앞에 /app
이 붙여서 전송하도록 설정합니다. 브로커는 받은 메시지를 /topic
으로 시작하는 목적지로 전송합니다.
registerStompEndpoints method 를 override 하여 클라이언트가 웹소켓 서버에 연결할 수 있는 엔드포인트를 등록합니다. application.yml 에 설정된 spring.application.name 을 사용합니다.
클라이언트는 ws://localhost:8080/{projectName} 으로 접속을 하고 /topic 이나 /queue 로 시작하는 주소를 구독합니다. /app 으로 시작 하는 경로로 요청을 하면 아래에서 설정할 MessageController method가 메시지를 받아 특정 주소를 subscribe하고 있는 세션들에 publish합니다.
클라이언트로 부터 메시지를 받아 처리하는 MessageController 를 작성합니다.
constroller.MessageController
@Controller
@RequiredArgsConstructor
public class MessageController {
private final SimpMessagingTemplate simpMessagingTemplate;
@MessageMapping("/chat/message")
public void sendMessage(ChatMessage chatMessage) {
if (MessageType.JOIN == chatMessage.messageType()) {
simpMessagingTemplate.convertAndSend(chatRoom
, ChatMessage.builder()
.messageType(chatMessage.messageType())
.roomNumber(chatMessage.roomNumber())
.userName(chatMessage.userName())
.message(chatMessage.userName()
+ "님이 채팅방에 들어왔습니다.")
.build());
} else if (MessageType.CHAT == chatMessage.messageType()) {
simpMessagingTemplate.convertAndSend(chatRoom, chatMessage);
}
}
}
WebSocketMessageBrokerConfig 를 구현할 때 서버로 전송할 주소의 prefix를 /app
으로 설정했습니다. 위와 같이 @MessageMaping("/chat/message")
를 설정하면 Websocket 접속 후 /app/chat/message
으로 목적지를 설정하고 메시지를 전송하면 sendMessage method 가 실행되게 됩니다.
sendMessage method는 전달받은 ChatMessage 에서 roomNumber 를 추출해 /topic/room/{roomNumber} 로 ChatMessage record 를 publish 하게 되고 /topic/room/{roomNumber} 를 subscribe 하는 모든 유저들은 메시지를 수신하게 됩니다.
이전에 WebSocketHandler 에서 ConcurrentMap 으로 관리했던 세션이나 Set으로 관리했던 채팅룸 으로 메시지를 보내는 코드 없이 같은 기능이 동작합니다. 이처럼 MessageBroker를 쓰면 개발자가 구현하고 관리했던 부분을 대신 처리해 줍니다.
아직 채팅 프로그램을 만들기 위해서는 부족한 부분이 많습니다. 인증부터 채팅룸 생성 등 기능을 붙여나가도록 하겠습니다.
Spring Framework에서 제공하는 클래스 중 하나로, STOMP 기반 메시징 시스템에서 메시지를 쉽게 전송할 수 있도록 도와주는 템플릿입니다. 이 클래스는 주로 서버에서 클라이언트로 메시지를 보내는데 사용됩니다.