Project

[Project] Spring + Stomp 테스트 하는 과정.. (실시간 채팅 구현)

woo0doo 2024. 2. 27. 23:02

서론

 tincle이라는 친구 기반 SNS 서비스를 개발하는 도중, 1:1 채팅 즉 DM같은 기능을 개발하게 되었습니다. 그 과정에서 소켓과 Stomp를 도입하게 되어서 테스트를 하려고 알아보던 중.... 난관에 대해 소개하려합니다. 해당 글은 소켓이 무엇인지, Stomp가 무엇인지가 아닌 테스트하는 방법에 대해 서술합니다.

 


 

테스트 과정 전 필자의 설정

 저의 webSocket 설정은 다음과 같습니다.

 

WebConfig

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

    private final StompHandler stompHandler; // jwt 인증

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue/chat");
        config.setApplicationDestinationPrefixes("/ws");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/connection")
                .setAllowedOriginPatterns("*")
                .withSockJS();
        registry.addEndpoint("/connection")
                .setAllowedOriginPatterns("*"); // 중요...
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(stompHandler);
    }
}

 

 

StompHandler

@Configuration
@RequiredArgsConstructor
public class StompHandler implements ChannelInterceptor {

    private final JwtProvider jwtProvider;

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

        if(accessor.getCommand() == StompCommand.CONNECT) {
            validateToken(accessor);
        }

        return message;
    }

    private void validateToken(StompHeaderAccessor accessor) {
        String accessToken = jwtProvider.resolveToken(accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION));

        if (accessToken == null)
            throw new AccountException(StatusCode.FILTER_ACCESS_DENIED);

        jwtProvider.validate(accessToken);
    }

 

 

MessageController

@Controller
@RequiredArgsConstructor
public class MessageController {

    private final MessageService messageService;

    @MessageMapping("/rooms/{roomId}/message")
    public void message(@DestinationVariable final Long roomId, SaveMessageRequest saveMessageRequest) {
        messageService.createMessage(roomId, saveMessageRequest);
    }
}

 

MessageService의 일부(메세지 전송)

    @Transactional
    public void createMessage(Long roomId, SaveMessageRequest saveMessageRequest) {
        Account sender = accountService.getAccountById(saveMessageRequest.accountId());
        Room room = roomService.getRoomById(roomId);

        Message message = messageRepository.save(handleMessage(sender, room, saveMessageRequest.content()));

        ChatResponse chatResponse = ChatResponse.of(roomId, message);
        messagingTemplate.convertAndSend("/queue/chat/rooms/" + roomId, chatResponse);
    }

 


테스트 도전기

 

다음을 정리하자면 local의 경우 localhost:포트/connection으로 연결을 하고,

/ws/rooms/{roomId}/message로 메세지를 보내면 (Web Config에 있는 setApplicationDestinationPrefixes + Message Controller에 있는 주소)

/queue/chat/rooms/{roomId} 로 해당 메세지가 전달되게 됩니다.

 

이를 테스트 하고 싶어서 자료를 열심히 찾아보았으나.....

 

모두... 테스트 툴로 APIC을 사용하고 있었습니다.. 그래서 저도 APIC을 사용하려 했더니????

 

 

????? 알 수 없는 이유로... apic을 더이상 지원하지 않는 것 같습니다... 여러 툴들을 열심히 찾아보고 Chat GPT도 돌려보고 했으나 결국에는 안되었습니다.. 그러던 중..!!!! 

 

이 사이트를 발견하게 되었습니다. 사실 초반에 발견하긴 했는데 연결에 문제가 있는 줄 알고 넘어갔다가 한참을 빙빙 돈 후에 코드 수정 후 다시 시도했더니 되었습니다.. ㅋㅋㅋㅋㅋㅋㅋ (안될 것 같이 생긴 블로그 탓하기..)

 

수정하기 전 제 처음 webConfig의 일부는 다음과 같습니다.

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/connection")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }

이렇게 작성했더니 ws://localhost:포트/connection을 하게 되어도 응답이 없고, http://localhost:포트/connection으로 해도 응답이 없었습니다.

하지만 어떤 글을 참고하면서 다음과 같이 수정했습니다.

 

정확하지 않지만 앱과 소켓을 사용할 때 withSockJs를 사용하면 안된다고...? 하네요 이건 알아보아야 할 것 같습니다

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/connection")
                .setAllowedOriginPatterns("*")
                .withSockJS();
        registry.addEndpoint("/connection")
                .setAllowedOriginPatterns("*");
    }

 

왜 이렇게 했는지 생각해 보았는데 ws://로의 접근을 허용시킨 것 같았습니다.

이렇게하고 다시 stomp로 시도했더니

어라? 연결이 되었습니다..!!!!

서버도 확인해본 결과 connect(1)로 바뀌어 있었습니다.

 

동시에 송수신을 확인하고 싶어서 사이트를 하나 더 띄워서 연결 후에 1번 room에 연결을 시켜보았습니다.

위와 같이 구독을 성공했다고 표시가 나옵니다. 

 

그리고 이제 대망의 메세지 전송을 해봅니다.

 

 

구독하는 쪽에선..?

무사히 값을 잘 전달 받는 것을 볼 수 있습니다!

 

저는 WebConfig에서 설정하는 값과 테스트 툴을 찾는 과정에서 시간을 많이 소모했던 것 같습니다. 특히 여러 개발자분들이 사용하시던 apic이 사라짐으로써 많이 해맸던 것 같습니다. 다른 분들이 개발하실 때 시간 낭비를 안하셨으면 해서 이 글을 남깁니다. 급하게 쓰느라 과정이 비어있을 수도 있고, 설명이 부족한 부분이 있을 수 있습니다. 그 부분을 댓글로 달아주시면 함께 고민하겠습니다!!