스프링에서의 WebSocket 설정과 SockJS

2024. 6. 18. 11:19CS

반응형

WebSocket
ws프로토콜을 기반으로 클라이언트와 서버 사이에 지속적인 완전 양방향 연결 스트림을 만들어주는 기술이다.

 

사전 지식

<폴링(Polling)>

- 단방향

- 클라이언트 -> 서버

- 클라이언트에서 주기적으로 서버에 업데이트 있는지 확인하는 요청을 보냄

 

<서버센트 이벤트(SSE; Server Sent Event)>

- 단방향

- 서버 -> 클라이언트

- 한 번 연결하면 서버가 클라이언트에 지속적으로 데이터를 보냄

- 클라이언트에서 서버로 데이터를 보낼 수 없음

 

<웹 소켓(WebSocket)>

- 양방향

- 서버 <-> 클라이언트

- 한 번 웹 소켓이 연결되면 계속 연결된 상태로 있어서 따로 업데이트가 있는지 요청을 보낼 필요가 없음

- node의 모듈 및 라이브러리: ws, Socket.IO

 

WebSocket 객체 생성하기

WebSocket 프로토콜을 사용하여 통신하기 위해서는 WebSocket 객체를 생성해야 한다. 이 객체는 자동으로 서버로의 연결을 열려고 한다.

 

예시 코드

let protocol = window.location.protocol === "http:" ? "ws://" : "wss://";
let host = window.location.hostname;
let port = window.location.port;

// 웹소켓이 등록되면 자동으로 onOpen()호출
let websocket = new WebSocket(protocol + host + ":" + port + "/ws/play");

//웹소켓 이벤트핸들러 할당
websocket.onmessage = onMessage;
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onerror = onError;

//-- API 실행
function doExampleApi() {
     $.ajax({
         url: '/api/example',
         method: 'POST',
         contentType: 'application/json',
         data: JSON.stringify(params),
         success: function (data) {
             send(data); 
         },
         error: function (error) {
             alert(error);
         }
     });
 }
    
//-- 웹소켓 시작 핸들러
function onOpen() {
     console.log("open socket...");
     doExampleApi();
}

    //-- 웹소켓 종료 핸들러
function onClose() {
     console.log("close socket....");
}

//-- 웹소켓 에러 핸들러
function onError(e) {
     console.log("error socket...")
     websocket.close();
     alert("알 수 없는 오류로 소켓이 닫힘");
     console.log(e);
}

//-- 웹소켓을 통해 서버에서 수신받는 메서드
function onMessage(msg) {
        console.log("get socket....");
        let data = JSON.parse(msg.data);

        if (data["status"] === "done" || data["status"] === "error") {
            console.log("웹소켓 중단...");
            websocket.close();
        }
}

//-- 웹소켓을 통해 서버로 전송하는 메서드
function send(name) {
    console.log("send socket...");
    let msg = {
        "name": name
    };
    websocket.send(JSON.stringify(msg));
}

 

<WebSocket Config 파일>

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {

    private final WebSocketHandler webSocketHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler, "/ws/simul")
                .setAllowedOrigins("*")
                .addInterceptors(new HttpSessionHandshakeInterceptor());
    }

}

 

AbstractWebSocketHandler 추상클래스를 상속받은 WebSocketHandlerOverride된 메소드들

Method Description
afterConnectionClosed(WebSocketSession session, CloseStatus status) 한쪽에서 WebSocket 연결을 닫은 후 또는 전송 오류가 발생한 후에 호출됩니다.
afterConnectionEstablished(WebSocketSession session) WebSocket 협상이 성공하고 WebSocket 연결이 열리고 사용할 준비가 된 후에 호출됩니다.
handleMessage(WebSocketSession session, WebSocketMessage<?> message) WebSocket 메시지가 도착하면 호출됩니다.
handleTransportError(WebSocketSession session, Throwable exception) 기본 WebSocket 메시지 전송의 오류를 처리합니다.
supportsPartialMessages() WebSocketHandler가 부분 메시지를 처리하는지 여부입니다.

 

WebSocket의 한계와 SockJS

순수 WebSocket만 가지고 구현하면 FireFox, Chrome, Edge, Whale에서는 동작하지만, 모바일 크롬 브라우저와 IE에서는 WebSocket이 동작하지 않는다.

문제점은 모든 클라이언트의 브라우저에서 WebSocket을 지원한다는 보장이 없다는것이다.

이를 해결하는 방법은 WebSocket Emulation을 이용하는 것이다. WebSocket Emulation이란, 우선 웹 소켓으로 소켓 연결을 시도하고, 실패할 경우 HTTP Streaming, Long-Polling같은 HTTP 기반의 다른 기술로 전환해 다시 연결을 시도하는 것을 말한다. WebSocket Emulation을 위해서 node.js를 사용한다면 Socket.io를 사용하고, Spring을 사용한다면 SockJS를 사용하는 것이 일반적이다.

SockJS는 어플리케이션이 WebSocket API를 사용하도록 허용하지만, 브라우저에서 WebSocket을 지원하지 않는 경우에 대안으로 어플리케이션의 코드를 변경할 필요 없이 런타임에 필요할 때 대체를 하는 것이다.

 

SockJS의 구성

1.     SockJS Protocol

2.     SockJS JavaScript Client: 브라우저에서 사용되는 클라이언트 라이브러리

3.     SockJS Server: spring-websocket 모듈을 통해 제공

4.     SockJS Java Client: spring-websocket 모듈을 통해 제공

 

SockJS 클라이언트는 서버의 기본 정보를 얻기위해 GET /info 를 호출하는데, 이는 서버가 WebSocket을 지원하는지, 전송과정에서 Cookies 지원이 필요한지, CORS를 위한 Origin 정보를 얻기위함이다.

 

스프링에서 SockJS 설정은 WebSocket Config 파일의 registerWebSocketHandlers에서 WebSocketHandlerRegistry.withSockJS() 인자로 설정할 수 있다.

반응형