STOMP을 알아보고 서버 구현해보자!

Karim·2023년 4월 13일
10

SpringBoot

목록 보기
11/15
post-thumbnail

1. STOMP 란

  • Simple Text Oriented Messaging Protocol
  • TCP 또는 WebSocket 같은 양방향 네트워크 프로토콜 기반으로 동작
  • Message Payload에는 Text or Binary 데이터를 포함 할 수 있다.
  • pub/sub 구조로 동작

2. Spring WebSocket STOMP

  • Spring에서 지원하는 stomp
  • 메세지 송수신에 대한 처리가 명확하게 정의할 수 있다.
  • WebSocketHandler 를 직업 구현할 필요 없이, @MessagingMapping 어노테이션을 사용해 메시지 발행 시 엔드포인트를 별로도 분리해서 관리할 수 있다.

3. PUB/SUB

💡

  • 발행/구독 구조
  • publisher가 특정 topic에 메세지를 보내면 해당 topic을 구독하고 있는 subsciber 모두에게 메세지가 전달되는 구조

4. 시작 전 확인사항

💬 version

IntelliJ : 2022.1.3.Ultimate
spring boot : 2.6.1

💬 gradle 추가

implementation 'org.springframework.boot:spring-boot-starter-websocket'

5. STOMP 동작 흐름

💻 stomp 동작 흐름도

6. STOMP Server

✒️ stomp config

@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private static final Logger LOGGER = LoggerFactory.getLogger( WebSocketConfig.class );

    // 세션 관리
    final StompHandler stompHandler;

    // 클라이언트가 웹 소켓 서버에 연결하는데 사용할 웹 소켓 엔드포인트 등록
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                // client가 sockjs로 개발되어 있을 때만 필요, client가 java면 필요없음
                .withSockJS()
                ;
    }

    /*한 클라이언트에서 다른 클라이언트로 메시지를 라우팅하는데 사용될 메시지 브로커*/
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {

        //sub으로 시작되는 요청을 구독한 모든 사용자들에게 메시지를 broadcast한다.
        registry.enableSimpleBroker("/sub");

        // pub로 시작되는 메시지는 message-handling methods로 라우팅된다.
        registry.setApplicationDestinationPrefixes("/pub");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        // connect / disconnect 인터셉터
        registration.interceptors(stompHandler);
    }
}

✒️ StompHandler

@Component
public class StompHandler extends ChannelInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger( StompHandler.class );

    @Override
    public void postSend(Message message, MessageChannel channel, boolean sent) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
        String sessionId = accessor.getSessionId();

        switch ((accessor.getCommand())) {
            case CONNECT:
            
                // 유저가 Websocket으로 connect()를 한 뒤 호출됨
                LOGGER.info("세션 들어옴 => {}", sessionId);
                break;
                
            case DISCONNECT:
            
                // 유저가 Websocket으로 disconnect() 를 한 뒤 호출됨 or 세션이 끊어졌을 때 발생
                LOGGER.info("세션 끊음 => {}", sessionId);
                break;
                
            default:
            
                break;
        }

    }
}

✒️ controller

@RestController
@RequiredArgsConstructor
public class SocketController {

    private static final Logger LOGGER = LoggerFactory.getLogger( SocketController.class );

    private final SimpMessageSendingOperations simpleMessageSendingOperations;

    // 새로운 사용자가 웹 소켓을 연결할 때 실행됨
    // @EventListener은 한개의 매개변수만 가질 수 있다.
    @EventListener
    public void handleWebSocketConnectListener(SessionConnectEvent event) {
        LOGGER.info("Received a new web socket connection");
    }

    // 사용자가 웹 소켓 연결을 끊으면 실행됨
    @EventListener
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
        StompHeaderAccessor headerAccesor = StompHeaderAccessor.wrap(event.getMessage());
        String sessionId = headerAccesor.getSessionId();

        LOGGER.info("sessionId Disconnected : " + sessionId);
    }

    // /pub/cache 로 메시지를 발행한다.
    @MessageMapping("/cache")
    @SendTo("/sub/cache")
    public void sendMessage(Map<String, Object> params) {

        // /sub/cache 에 구독중인 client에 메세지를 보낸다.
        simpleMessageSendingOperations.convertAndSend("/sub/cache/" + params.get("channelId"), params);
    }
}

7. STOMP Client Test

💻 apic

💻 실행 화면


📌 여담

  • 서버가 있다면 클라이언트도 있어야지!

📚 참고

profile
나도 보기 위해 정리해 놓은 벨로그

0개의 댓글