public void sendMessage(String publishMessage) {
if (publishMessage.startsWith("ENTER", 9)) {
Thread.sleep(3000);
ChatMessageEnterResponseDto chatMessageEnterResponseDto = objectMapper.readValue(publishMessage, ChatMessageEnterResponseDto.class);
messagingTemplate.convertAndSend("/sub/chat/" + chatMessageEnterResponseDto.getRoomId(), chatMessageEnterResponseDto);
}
}
Thread.sleep(3000);
: 구독 요청 후 3초 후에 입장메세지를 보내게끔 설정
해당 설정 후 문제 없이 입장메세지가 전송된 것으로 보아 client에서 구독을 완료하기도 전에 server에서 입장메세지를 전송해 버그가 발생한 것으로 문제 확인
하지만 인위적으로 delay를 주는 방식은 user의 컴퓨터 사양이 어떠냐에 따라 작동이 안될 수도 있기 때문에 근본적인 해결방안은 아님
public class StompHandler implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
if (StompCommand.SUBSCRIBE == accessor.getCommand()) {
String token = accessor.getFirstNativeHeader("Authorization").substring(7);
Long userId = Long.parseLong(jwtDecoder.decodeUserId(token));
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("해당 유저가 존재하지 않습니다."));
String roomId = getRoomId(Optional.ofNullable((String) message.getHeaders()
.get("simpDestination")).orElse("InvalidRoomId"));
chatMessageService.accessChatMessage(ChatMessageRequestDto.builder()
.type(ChatMessage.MessageType.Enter).roomId(roomId).userId(userId).build());
}
}
}
ChannelInterceptor
: 해당 인터셉터는 user 정보를 불러온 후 user를 인증하는 역할을 함
StompCommand.SUBSCRIBE
: 따라서 client에서 server로 구독 요청을 보낼 때는 구독을 완료하고 보내는 것이 아니라 구독을 하겠다고 요청을 보낸 것임.
chatMessageService.accessChatMessage
: 그런데 해당 로직에서 구독을 완료 했는지 확인 없이 바로 입장메세지 전송 로직으로 넘어갔었음
@MessageMapping("/chat/enter")
@ApiOperation(value = "채팅방 입장")
public void enterMessage(@RequestBody ChatMessageRequestDto chatMessageRequestDto,
@Header("Authorization") String rawToken) {
String token = rawToken.substring(7);
Long userId = Long.parseLong(jwtDecoder.decodeUserId(token));
chatMessageRequestDto.setUserId(userId);
chatMessageService.accessChatMessage(chatMessageRequestDto);
}
@MessageMapping("/chat/enter")
: pub destination을 하나 더 설정하여 client가 구독이 완료 되면 해당 destination으로 입장메세지 전송 요청(accessChatMessage()
)을 보내게끔 개선시킴