STOMP

  • Simple Text Oriented Messaging Protocol의 약자로, 메시지 전송을 효율적으로 하기 위한 프로토콜이다.
  • 기본적으로 Publish/Subscribe(pub/sub)구조로 되어있다.
  • 클라이언트가 서버로 메시지를 보내는 것을 pub, 클라이언트가 서버로부터 메시지를 받는 것을 메시지를 구독한다는 개념으로 sub이 사용된다.
  • WebSocket과 달리 STOMP는 메세지 형식이 COMMAND, HEADER, BODY로 이뤄져 있다.
  • STOMP는 websocket위에서 동작하는 프로토콜로, 클라이언트와 서버가 전송할 메세지의 형식이 정해져 있고, pub/sub 기능을 제공한다.

기본적인 동작 과정으로는

1. Client(Sender)가 메세지를 보내면 서버에 메세지가 전달된다.

2. Controller의 @MessageMapping에 의해 메세지를 받는다.

3. Controller의 @SendTo로 특정 topic을 구독하는 클라이언트에게 메세지를 보낸다.

cf) topic이란 메세지를 구분하고 분류하는 논리적인 개념으로 pub/sub는 topic을 기준으로 메세지를 전달한다.

 

config 설정

@Configuration
@EnableWebSocketMessageBroker // stomp를 사용하여 socket 통신을 하겠다는 어노테이션
public class stompConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws-stomp") // 클라이언트에서 소켓을 생성할 때 여기에 정의한 문자열로 해야 함
                .setAllowedOrigins("http://localhost:8080")
                .withSockJS(); // 클라이언트와 연결은 SockJS로 함을 명시
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic"); // topic의 prefix 이름 설정 앞으로 이 topic 이름을 가지고 pub/sub을 거침
        registry.setApplicationDestinationPrefixes("/send"); // 클라이언트에서 보낸 메세지를 받을 떄의 prefix
    }
}

 

controller 설정

@Controller
@RequiredArgsConstructor
@RequestMapping("/chat")
public class ChatController {
    private final ChatService chatService;
    private final RoomService roomService;
    @GetMapping("/chatRoom")
    public String chatRoom(@RequestParam(name = "roomId") Long roomId, Model model, Principal principal) {
        ChatRoom chatRoom = roomService.getOne(roomId);
        model.addAttribute("chatRoom", chatRoom);
        model.addAttribute("name", principal.getName());
        return "room/chatRoom";
    }

    @MessageMapping("/{topicNum}") // 클라이언트에서 /send/{roomId}를 통해 컨트롤러의 @MessageMapping과 매핑된다
    								//@RequestBody의 역할을 수행
    @SendTo("/topic/{topicNum}") // 해당 문자열의 주소를 구독하고 있는 클라이언트에게 데이터 전송
    								//@ReponseBody의 역할을 수행
    public ChatMessageDTO chat(/*@PathVariable과 같은 역할*/@DestinationVariable Long topicNum, @RequestBody ChatMessageDTO message){
        chatService.createChat(topicNum, message.getSender(), message.getMessage());
        return ChatMessageDTO.builder()
                .sender(message.getSender())
                .message(message.getMessage())
                .build();
    }
}

 

script 설정

 

<script th:inline="javascript">
    var stompClient = null;
    var roomId = $("#roomId").val();
    var sender = $("#name").val();
    //스크립트에서 타임리프가 안 먹혀 저장된 채팅메세지 불러오는 것은 포기
    var socket = new SockJS("/ws-stomp"); // SockJS를 새로 만든다. 이 때 문자열은 endpoint로 설정한다.
    stompClient = Stomp.over(socket); // SockJS가 Stomp 프로토콜 위에서 동작하도록 한다.
    // 아래의 connect()는 서버와 연결할 때 사용하는 함수로 {} 안에는 같이 전송할 헤더를 설정하는 것이고 function(frame)은 콜백함수로서
    // 서버와 성공적으로 연결되었을 시에 시행할 동작들을 넣어주면 된다.
    stompClient.connect({}, function (frame) {
        // subscribe() 함수로 현재 클라이언트가 구독할 topic과 서버에서 @SendTo로 메세지를 보냈을 때 메세지를 받아서 처리할 함수를 정의해주면 된다.
        stompClient.subscribe('/topic/' + roomId, function (frame) {
            showChat(JSON.parse(frame.body)); //frame의 body는 서버에서 전송한 데이터를 의미한다.
        });
    });
    $("#button-send").on("click", function (e) {
        // 클라이언트에서 서버로 메세지를 전송할 때는 send()함수를 이용하면 된다. 두 번째 인자는 frame을 전송할 때 같이 보낼 헤더를 설정하는 것이다.
        stompClient.send("/send/" + roomId, {},
            JSON.stringify({
                'sender': sender,
                'message': $("#msg").val()
            }));
        $('#msg').val('');
    });

    function showChat(chatMessageDTO) {
        $("#chat-content").append(
            "<div class = 'chat'><div class='chat-body'><div class='chat-message' id='id_chat'>" +
            "<tr><td>" + "[" + chatMessageDTO.sender + "] " + chatMessageDTO.message + "</td></tr>" +
            "</div></div></div>"
        );
    }
</script>

+ Recent posts