WebSocket 웹소켓 채팅 서버 개선 STOMP, MessageBroker

GEONNY·2024년 8월 29일
0
post-thumbnail

이전에 구현한 웹소켓 채팅 서버MessageBroker 를 적용하여 메시지 처리 시 STOMP 프로토콜로 전송하도록 개선해보겠습니다.

📌STOMP

STOMP (Simple Text Oriented Messaging Protocol) 는 메세징 전송을 효율적을 하기 위해 탄생한 프로토콜입니다. 메시지 브로커를 통해 간단한 텍스트 기반의 메시징을 지원하며, 다양한 프로그래밍 언어 및 플랫폼에서 쉽게 사용할 수 있도록 설계되었습니다.

📌주요 개념

MessageBroker

STOMP는 메시지 브로커와 함께 사용되며, 클라이언트가 발행한 메시지를 적절한 구독자에게 라우팅합니다.
메시지 브로커는 특정 토픽이나 큐에 클라이언트가 구독한 후, 해당 토픽 또는 큐로 발행된 메시지를 구독자에게 전달합니다. WebSocket 기반 애플리케이션에서 클라이언트 간의 메시지 전달을 관리하는 중요한 역할을 합니다. 메시지 브로커를 설정하기 위해 Spring Boot의 WebSocket 지원을 사용하며, STOMP 프로토콜을 통해 메시지 브로커와의 통신을 처리합니다.

📍Frame

STOMP에서 모든 통신은 프레임이라고 불리는 메시지 단위로 이루어집니다. 각 프레임은 특정 명령을 나타내며, 서버와 클라이언트 간의 통신을 관리합니다.

📍Publish & Subscribe

클라이언트는 특정 토픽이나 큐를 구독하여, 해당 목적지로 발행된 메시지를 수신할 수 있습니다.
클라이언트는 특정 목적지로 메시지를 발행(SEND)할 수 있으며, 메시지 브로커는 해당 목적지에 구독한 클라이언트에게 메시지를 전달합니다.

📍ACK/NACK

클라이언트는 메시지를 받았을 때, 수신을 확인하는 ACK(Acknowledge) 프레임을 서버로 보낼 수 있습니다. 이를 통해 메시지가 성공적으로 수신되었음을 보장할 수 있습니다.
NACK(Negative Acknowledge)은 메시지가 제대로 처리되지 않았음을 나타내며, 브로커는 메시지를 다시 전달하거나 다른 처리를 수행할 수 있습니다.

📌구현

📍WebSocketMessageBrokerConfig

기존에 작성했던 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

클라이언트로 부터 메시지를 받아 처리하는 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를 쓰면 개발자가 구현하고 관리했던 부분을 대신 처리해 줍니다.
아직 채팅 프로그램을 만들기 위해서는 부족한 부분이 많습니다. 인증부터 채팅룸 생성 등 기능을 붙여나가도록 하겠습니다.

📚참고

📕SimpMessagingTemplate

Spring Framework에서 제공하는 클래스 중 하나로, STOMP 기반 메시징 시스템에서 메시지를 쉽게 전송할 수 있도록 도와주는 템플릿입니다. 이 클래스는 주로 서버에서 클라이언트로 메시지를 보내는데 사용됩니다.

profile
Back-end developer

0개의 댓글