본문 바로가기

Language/Java

Stomp Heartbeat (for Spring)

최근 업무 도중 Stomp 기반 웹소켓을 사용하여 개발할 일이 있었는데, 공유차 기록...

 

클라이언트와 heartbeat 를 주고 받는데, 에러가 계속 발생하였다.

원인은 물론 내가 잘못 코드를 개발한것이지만, 찾기 힘들어서 몇일 고생하였다.

 

보통 Spring 을 사용하여 웹소켓 서비스를 개발할때 웹소켓 설정중에,

configureClientInboundChannel, configureClientOutboundChannel 설정이 있다.

단순하게 요청의 앞뒤에 인터셉트 기능을 하는 설정인데, 

Stomp Command를 조회하여, Command별로 기능을 분기하는 역할을 만들었었다.

그런데 Heartbeat 메세지만 오면 에러가 났었는데, 결론부터 말하면

StompCommand와 SimpMessageType을 잘못 사용했기 때문이다.

 

예를들어 구글링 및 깃헙에서 예제코드들을 찾아보면 이런식의 구조를 찾아볼수있다.

 

 @Override
 public Message<?> preSend(Message<?> message, MessageChannel channel) {
 	// 클라이언트(외부)에서 받은 메세지를 Stomp 프로토콜 형태의 메세지로 가공하는 작업
 	StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
    StompCommand command = accessor.getCommand();
    
    switch (command) {
    	case CONNECT:
        	log.info("유저접속...")
            break;
        case DISCONNECT:
        	log.info("유저퇴장...")
            break;
        default:
        	log.info("다른커맨드...")
            break;
    }
    
    return message;
 }

 

 

문제가 되는 메소드만 작성했는데, ChannelInterceptor 의 구현체이다.

 

혹시 저 코드만 보고 문제를 알것 같으신분은 안보셔도 될듯하다.

 

문제는 switch 문이다.

왜 문제가 되냐면, access.getCommand(); 이 메소드는 @Nullable이라는 메소드인데, heart beat 메세지가 오면 Stomp Command 는 null이 리턴되서, NPE가 발생한다.

따라서 에러를 수정하려면,

 

 @Override
 public Message<?> preSend(Message<?> message, MessageChannel channel) {
 	// 클라이언트(외부)에서 받은 메세지를 Stomp 프로토콜 형태의 메세지로 가공하는 작업
 	StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
    StompCommand command = accessor.getCommand();
    
    // 1번 방법: command null일 경우는 무시
    if (command != null) {
    	switch (command) {
    	case CONNECT:
        	log.info("유저접속...")
            break;
        case DISCONNECT:
        	log.info("유저퇴장...")
            break;
        default:
        	log.info("다른커맨드...")
            break;
    	}
    }
    // 2번 방법: SimpMessageType을 사용
	SimpMessageType messageType = accessor.getMessageType();
    switch (messageType) {
    	...
        case HEARTBEAT:
        	log.info("핫빗 날라옴...");
        break;
        ...
    }
   
    
    return message;
 }

1번방법 or 2번방법을 사용하면 해결 된다.

 

처음 개발했을때는, Stomp 프로토콜을 사용하는데 당연히 null이 될 수 있을거란 생각을 못하고 코딩을 했다가 삽질을 했다. 물론 삽질때문에 스프링에서 메세징 처리하는 방법을 다 훑어볼수있는 기회가 되서 좋았지만, 해결을 하니까 좋은 기회였다고 생각한다.

 

윗 부분에 SimpMessageType이란 enum 타입과 StompCommand라는 enum 타입이 있는데, 메세지 타입에 따라서 분기를 처리할 경우 SimpMessageType을 추천한다. (예를들면 CONNECT_ACK, DISCONNECT_ACK 타입도 SimpMessageType에만 있고, StompCommand에는 없다.)

'Language > Java' 카테고리의 다른 글

에러처리  (0) 2021.03.23
Stack 대신에 Deque를 사용하자  (0) 2021.03.09
Abstract VS Interface  (0) 2020.05.22
JVM 정리  (0) 2020.05.22