Spring Boot Websocket 서버 실행과 테스트

CHR·2024년 3월 22일
0

저번 주에 이어서 이번 주도 웹소켓 공부를 했다. 웹 기초 지식이 없어서 서비스 흐름을 이해하는 데 너무 오래 걸렸다. 소켓 연결은 클라이언트에서 수행하고 서버에서는 전송된 메세지를 어디에 배포할지를 정의하는 메소드를 구현하면 된다고 이해했다.

WebSocketConfig 웹소켓 관련 설정 파일

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

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

Endpoint(/ws)으로 클라이언트가 웹 소켓 연결 요청을 하면 handshake 과정을 거쳐 연결이 됨!

withSocketJS()

  • SocketJS는 웹소켓을 지원하지 않는 브라우저에서 HTTP의 polling과 같은 방식으로 웹소켓의 요청을 수행하게 해준다.
  • SocketJS를 사용할 경우, 클라이언트에서 웹소켓 요청을 보낼 때 설정한 엔드포인트 뒤에 /websocket을 추가해줘야 한다고 함.
     

WebSocket은 단순한 통신 구조이기 때문에 메세지가 어떤 요청인지, 어떻게 처리해야 하는지에 대한 정보가 없음.

이 정보를 담아주는 프로토콜이 STOMP 프로토콜

클라이언트와 서버가 전송할 메세지의 유형, 형식, 내용 등을 정의한다.

STOMP는 구독-발행의 구조

DestinationPrefix(”/pub”)는 클라이언트에서 소켓에 메세지를 보낼 때 주소에 붙여야 하는 prefix

Broker(”/sub”)는 클라이언트에서 publish를 통해 메세지를 보내면 subscribe에 구독(연결 요청)을 했던 사용자들에게 메세지를 보냄

  • 프론트 예시

    구독

    function connect() {
        var socket = new WebSocket('/ws');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function () {
            setConnected(true);
            stompClient.subscribe('/sub/rooms/5', function (greeting) {
                console.log(greeting.body);
            });
        });
    }

    발행

    function sendMessage() {
        stompClient.send("/pub/rooms/5/chat", {}, JSON.stringify({
            'message': $("#name").val(),
            'senderId': 7,
            'receiverId': 14,
            'roomId': 5
        }));
    }

Controller

@RestController
@RequiredArgsConstructor
public class SocketController {

    private final SocketService socketService;
    @MessageMapping("/rooms/{roomId}/chat")
    @SendTo("/sub/rooms/{roomId}")
    public ChatResponse chat(@DestinationVariable Long roomId, ChatRequest request) {
        return ChatResponse.builder()
                .chatType(request.getChatType())
                .roomId(request.getRoomId())
                .userId(request.getUserId())
                .message(request.getMessage())
                .time(LocalDateTime.now())
                .build();
    }
}

@MessageMapping : DestinationPrefix와 결합해서 클라이언트가 SEND할 수 있는 주소가 된다. ("/pub/rooms/{roomId}/chat”)
@DestinationVariable : {roomId}처럼 목적지의 pathvariable 변수를 받는 용도

테스트

Postman에서도 웹소켓을 지원한다고 해서 테스트를 해보려 했으나 연결이 되지 않았다. 찾아보니 웹소켓, socketIO는 지원하지만 STOMP 프로토콜은 지원하지 않기 때문이었다. (raw 파일로 STOMP 설정을 다 작성하면 복잡하지만 가능은 하다고 함...)
따라서 STOMP 테스트를 지원하는 APIC이라는 웹 앱에서 테스트를 진행했다.

APIC의 화면이다. ws으로 new tab을 열고 Connection Type을 STOMP으로 설정해주면 된다.

Request URL에 웹소켓 handshake 주소를 입력하고 Connect를 클릭하면 소켓에 연결이 된다.


두 개의 APIC 창을 열어서 같은 소켓에 연결하고, 한 창에서 메세지를 보냈을 때 두 개의 창 모두 메세지를 받는 것을 확인할 수 있었다!
단, SockJS는 보안 이슈로 Origin 설정에 와일드카드를 허용하지 않는데, APIC에서는 Origin을 알 수 없어 EPIC 테스트 시에는 withSockJS();를 주석처리 해야 한다.

profile
🍷

0개의 댓글

관련 채용 정보