서론
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도 돌려보고 했으나 결국에는 안되었습니다.. 그러던 중..!!!!
이 사이트를 발견하게 되었습니다. 사실 초반에 발견하긴 했는데 연결에 문제가 있는 줄 알고 넘어갔다가 한참을 빙빙 돈 후에 코드 수정 후 다시 시도했더니 되었습니다.. ㅋㅋㅋㅋㅋㅋㅋ (안될 것 같이 생긴 블로그 탓하기..)
+ 2024-10-27 Update : 사이트 주소가 변경되어서 사이트가 잘 안나왔었네요 ㅠㅠ 변경된 주소로 바꿔 놓았습니다!!
위의 이 사이트를 클릭하셔도 좋고
변경된 주소는 다음과 같습니다. -> https://jiangxy.github.io/websocket-debug-tool/
수정하기 전 제 처음 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이 사라짐으로써 많이 해맸던 것 같습니다. 다른 분들이 개발하실 때 시간 낭비를 안하셨으면 해서 이 글을 남깁니다. 급하게 쓰느라 과정이 비어있을 수도 있고, 설명이 부족한 부분이 있을 수 있습니다. 그 부분을 댓글로 달아주시면 함께 고민하겠습니다!!
'Project' 카테고리의 다른 글
[Project] TDD 개발 시 given의 코드 양 줄이기 (Fixture, Persister) + @spy 활용 (1) | 2024.06.08 |
---|---|
[Project] 실시간 채팅 구현 시 FCM Token 발송 여부 결정하기 (0) | 2024.03.25 |
[Project] 멀티 모듈을 왜 쓸까? (멀티 모듈 도입 전) (1) | 2024.01.15 |
[Project] google Oauth2 로그인 시 refreshToken을 받는 방법 (최초 로그인에서 저장을 못했을 때) (0) | 2023.11.29 |
[Project] Redis(Elasticache Redis)로 RefreshToken을 관리하기 (1) | 2023.11.25 |