[Chatting Server] Stomp로 채팅 기능 확장

원알렉스·2020년 9월 9일
1

Chatting Server

목록 보기
2/2
post-thumbnail

🚀 WebSocket과 Stomp를 통해 채팅 서버 구현

메시징 방식을 잘 정의한다면 WebSocket 만으로도 충분히 좋은 Server/Client 소켓 서버를 완성할 수 있습니다. 하지만 단순한 통신 구조로 인해 WebSocket만을 이용해 채팅을 구현하면 해당 메시지가 어떤 요청인지, 어떻게 처리하고 어디로 보내야하는지에 따라 채팅방과 session을 일일히 구현하고 메시지 전송 부분을 관리하는 추가 코드를 작성해야 합니다.

그래서 이번에는 Stomp 를 사용해서 이 부분에 대해서 해결해보려고 합니다. Stomp는 메시징 전송을 효율적으로 하기 위해 나온 프로토콜이며 기본적으로 Pub/Sub 구조로 되어 있어 메시지를 발송하고, 메시지를 받아 처리하는 부분이 확실히 정해져 있기 때문에 개발하는 입장에서 명확하게 인지하고 개발할 수 있는 이점이 있습니다. 또한 Stomp를 이용하면 통신 메시지의 헤더에 값을 세팅할 수 있어 헤더 값을 기반으로 통신 시 인증 처리를 구현하는 것도 가능합니다.

여기서 Pub/Sub은 메시지를 공급하는 주체와 소비하는 주체를 분리하여 제공하는 메시징 방법입니다.

  • 채팅방을 생성한다: Pub/Sub 구현을 위한 Topic 이 하나 생성된다.
  • 채팅방에 입장한다: Topic 을 구독한다.
  • 채팅방에서 메시지를 보내고 받는다: 해당 Topic으로 메시지를 전송하고(Pub) 메시지를 받는다(Sub)

💻 프로젝트 구성하기

✓ WebSocketConfig 수정

  • Stomp를 사용하기 위해 @EnableWebSocketMessageBroker를 선언하고 WebSocketMessageBrokerConfigurer를 상속 받도록 합니다.
  • pub/sub 구조를 구현하기 위해 메시지를 발행하는 요청의 prefix는 /pub으로 시작하도록 설정하고 메시지를 구독하는 요청의 prefix는 /sub으로 시작하도록 설정해줍니다.
  • Stomp WebSocket의 연결 endpoint는 /ws/chat으로 설정해줍니다. 서버의 접속 주소는 ws://localhost:8080/ws/chat으로 설정됩니다.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Autowired
    private WebSocketHandler webSocketHandler;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/sub");
        registry.setApplicationDestinationPrefixes("/pub");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
                .addEndpoint("/ws/chat")
                .setAllowedOrigins("*")
                .withSockJS();
    }
}

✔ 채팅방 모델 수정

pub/sub 구조를 이용하게 되면 구독자 관리가 알아서 되므로 WebSocket Session 관리가 필요 없어집니다. 그리고 전송의 구현도 알아서 해결되므로 일일이 Client에게 메시지를 전송하는 구현이 필요없어집니다.

public class ChatRoom {

    private String id;
    private String name;

    protected ChatRoom() {

    }

    private ChatRoom(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public static ChatRoom of(String id, String name) {
        return new ChatRoom(id, name);
    }
    ...
}

Publisher 구현 (ChatController 수정)

  • @MessageMapping 을 통해 WebSocket 으로 들어오는 메시지 발행을 처리합니다.
  • Client에서는 prefix를 붙여서 /pub/chat/message 로 발행 요청을 하면 Controller가 해당 메시지를 받아 처리합니다.
  • 메시지가 발행되면 /sub/chat/room/{roomId} 로 메시지를 send 하는 것을 볼 수 있는 Client에서는 해당 주소(/sub/chat/room/{roomId})를 구독(subscribe)하고 있다가 메시지가 전달되면 화면에 출력하면 됩니다.
  • 여기서 /sub/chat/room/{roomId} 는 채팅룸을 구분하는 값이므로 pub/sub에서 Topic의 역할이라고 보면 됩니다.
@RestController
public class ChatController {

    @Autowired
    private SimpMessageSendingOperations messagingTemplate;

    @MessageMapping(value = "/chat/message")
    public void send(ChatMessage message) {
        if (MessageType.ENTER.equals(message.getMessageType())) {
            message.setMessage(message.getSender() + " 님이 입장하셨습니다.");
        }
        messagingTemplate.convertAndSend("/sub/chat/room/" + message.getChatRoomId(), message);
    }
}
profile
Alex's Develog 🤔

0개의 댓글