STOMP (Simple Text Oriented Messaging Protocol) was originally created for scripting languages (such as Ruby, Python, and Perl) to connect to enterprise message brokers. It is designed to address a minimal subset of commonly used messaging patterns. STOMP can be used over any reliable two-way streaming network protocol, such as TCP and WebSocket. Although STOMP is a text-oriented protocol, message payloads can be either text or binary.
STOMP is a frame-based protocol whose frames are modeled on HTTP. The following listing shows the structure of a STOMP frame:
COMMAND
header1:value1
header2:value2
Body^@
-spring docs STOMP overview
STOMP의 자세한 설명은 다음 페이지를 통해 확인해 볼 수 있습니다
스프링의 STOMP 지원 기능을 사용하면 직접 세션을 관리하지 않아도 내장 메시지 브로커를 통한 pub/sub 모델을 사용하여 1:N 실시간 채팅 기능을 만들 수 있습니다
스프링의 STOMP기능을 사용하여 실행을 목표로 구현해보았습니다
WebSocketConfig.java
package com.example.chat.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").setAllowedOrigins("*");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/pub");
registry.enableSimpleBroker("/topic");
}
}
이전 코드와 달리 WebSocketMessageBrokerConfigurer를 구현한 WebSocketConfig 클래스를 만들어줍니다
- registerStompEndpoints 메서드
StompEndpointRegistry 인터페이스의 addEndpoint 메서드를 사용하여 웹소켓 핸드셰이크를 위해 연결해야 하는 엔드포인트의 HTTP URL을 설정합니다
클라이언트가 localhost:8080/chat 에 웹소켓 연결이 되면 웹소켓을 타고 STOMP 프레임을 전달하고, 전달받을 수 있습니다
- configureMessageBroker 메서드
MessageBrokerRegistry 클래스를 사용해 라우팅 설정을 해줍니다.
- setApplicationDestinationPrefixes 는 해당 문자열로 시작하는 destination의 STOMP 메시지를 @Controller 클래스의 @MessageMapping 메서드로 라우팅합니다.
- enableSImpleBroker 메서드는 구독 및 브로드캐스팅을 위해 내장된 메시지 브로커를 사용하게 해 주고, 헤더가 /topic으로 시작하는 메시지를 브로커로 라우팅합니다.
MessageController.java
package com.example.chat.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@Slf4j
public class MessageController {
private final SimpMessageSendingOperations simpMessageSendingOperations;
@MessageMapping("/message/{roomId}")
public void message(@DestinationVariable Long roomId, String content) {
log.info("roomId = {}", roomId);
log.info("content = {}", content);
simpMessageSendingOperations.convertAndSend("/topic/" + roomId.toString(), content);
}
}
@MessageMapping 어노테이션을 을 통해 클라이언트가 메시지를 보낼 수 있습니다
WebSocketConfig 에서 설정한 applicationDestinationPrefixes와 합쳐집니다
ex) /pub/message/1
파라미터에 @DestinationVariable 어노테이션을 붙여서 @MessageMapping의 값에서 추출된 템플릿 변수에 접근할 수 있습니다
content 에는 payload의 값이 들어갑니다
SimpMessageSendingOperations 인터페이스에는 SimpMessagingTemplate 클래스가 주입되며 convertAndSend 메서드를 호출하면 타고타고 올라가서 SimpMessagingTemplate 클래스의 convertAndSend, doConvert로 헤더를 만들고 send, doSend 메서드를 호출해 줍니다.
결국 해당 roomId를 구독하고 있는 클라이언트들에게 메시지를 브로드캐스트해줍니다.
테스트
테스트 도구로 다들 APIC을 사용하시는 것 같던데 사이트가 들어가지질 않아서 한참 찾은 결과
https://jxy.me/websocket-debug-tool/
위의 사이트에서 비슷하게 테스트해볼 수 있었습니다.
ws://localhost:8080/chat 웹소켓 연결 후 /topic/1 구독하고, 다른 창에서 /pub/message/1 로 hello 라는 메시지를 보내보았습니다
doSend 메서드에 브레이크포인트를 걸어놓고 디버그를 돌려보니 이전 doConvert에서 메시지 객체가 만들어져서 페이로드와 헤더들이 잘 만들어져 있는 모습을 확인할 수 있었습니다.
더 나아가 AbstractMessageChannel 에서 send메서드를 호출하고 이때는 Message에 destination 까지 만들어져 있었습니다.
일단 실행은 잘 되고 좀 파고들어 봤는데
디비 저장하는 부분, 여러 정보들 추가로 담아서 Json 보내기, 보안 처리, 웹소켓 연결 끊긴 상태에서 온 채팅 개수 확인, 알람 등 공부할 내용이 많은 것 같습니다
참고
https://docs.spring.io/spring-framework/reference/web/websocket/stomp.html
https://dev-gorany.tistory.com/235
'Java' 카테고리의 다른 글
SpringBoot WebSocket 채팅기능 만들기 (1) (1) | 2024.02.09 |
---|---|
[Java] 람다 표현식 (0) | 2023.09.06 |
[Java] 동작 파라미터화 전달하기 (0) | 2023.08.31 |
[Java] Collections Framework 정리 (1) (0) | 2023.08.29 |