스프링 웹 소켓 알아보기(3)

Spring WebSocket API
이번 포스팅에서는 스프링에서 제공하는 웹소켓 관련 API에 대하여 알아보자.

WebSocketHandler

WebSocket 서버를 구현하는 방법은 간단하다.

  1. WebSocketHandler 인터페이스를 직접 구현
  2. TextWebSocketHandler 또는 BinaryWebSocketHanlder 등의 클래스를 상속하여 사용

일반적으로는 스프링에서 기본 구현을 제공하는 2번 방식이 더 많이 사용된다고 한다.

WebSocketHandler WebSocketHandler 인터페이스를 직접 구현할 시 재정의해야하는 메소드들은 위와 같다.
메소드 명을 통해 알 수 있듯 커넥션 연결 후, 메시지 처리방식, 전송에러시 등 WebSocket 연결의 라이프사이클과 메시지 처리를 구현하면 된다.

아래는 웹 소켓 구현에 대한 스프링 공식문서의 예제코드이다.

public class MyHandler extends TextWebSocketHandler {

	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) {
		// ...
	}
}


이제 이렇게 구현한 WebSocketHandler를 특정 URL에 매핑하기 위하여 Configuration파일에 등록하면 된다.

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(myHandler(), "/myHandler");
	}

	@Bean
	public WebSocketHandler myHandler() {
		return new MyHandler();
	}
}
  • WebSocket에 대한 설정 클래스는 WebSocketConfigurer를 구현해야한다.

  • 구현 메소드에서 핸들러와, 매핑 URL을 등록해주면 된다.

기타 사항

  • Spring MVC와 통합하면 DispatcherServlet에 등록해야함
  • 그러나 Spring MVC에 의존적인 것이 아닌 WebSocketRequestHandler를 사용하여 WebSocket 요청을 처리할 수 있음
  • WebSocket 표준(JSR-356)은 동시메시지 전송을 허용하지 않으므로 WebSocketSession을ConcurrentWebScketSessionDecorator를 사용하여 래핑해야함

WebSocket Handshake

스프링에서 WebSocket 핸드셰이크 과정을 커스터마이징하거나 확장할 수 있다.

1. HandshakeInterceptor 사용

  • 핸드셰이크 과정에 개입할 가장 쉬운 방법은 HandshakeInterceptor를 사용하는 것이다.
  • HandshakeIntercpetor는 beforeHandshake와 afterHandshake 메서드를 제공하여 핸드셰이크 전후 작업을 처리할 수 있다.

아래는 내장 인터셉터를 사용하여 HTTP 세션 속성을 WebSocket 세션에 전달한다.

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new MyHandler(), "/myHandler")
                .addInterceptors(new HttpSessionHandshakeInterceptor());
    }
}

2. DefaultHandshakeHandler 상속

  • 더 고급 작업이 필요할 경우 DefaultHandshakeHanlder를 상속하여 확장할 수 있다.

DispatcherServlet과 RequestUpgradeStrategy

기존 웹소켓 API 표준(JSR-356)에서는 프론트 컨트롤러라는 개념이 존재하지 않아 핸드셰이크 요청과 HTTP 요청을 따로 처리해야했다.
또한 JSR-356방식은 DispatcherServlet의 매핑체계와 별도로 작동하여 Spring MVC와 통합되지 않는다.
이를 위해 스프링에서는 RequestUpgradeStrategy를 사용하여 DispatcherServlet에서 WebSocket요청과 HTTP요청을 함께 처리할 수 있도록 구현체를 제공한다.
여기까지 접근할 일은 거의 없을것 같으므로 키워드만 알아두고 필요할때 찾아보자.

WebSocket 서버 설정

ServletServerContainerFactoryBean을 활용해 서버 설정을 커스터마이징할 수 있다.

@Configuration
public class WebSocketConfiguration {

	@Bean
	public ServletServerContainerFactoryBean createWebSocketContainer() {
		ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
		container.setMaxTextMessageBufferSize(8192);  // 텍스트 메시지 버퍼 크기
		container.setMaxBinaryMessageBufferSize(8192);  // 바이너리 메시지 버퍼 크기
		return container;
	}
}

만약 Jetty서버를 사용한다면 JettyRequestUpgradeStrategy를 활용해 서버 설정을 커스터마이징 할 수 있다.

@Bean
	public DefaultHandshakeHandler handshakeHandler() {
		JettyRequestUpgradeStrategy strategy = new JettyRequestUpgradeStrategy();
		strategy.addWebSocketConfigurer(configurable -> {
			configurable.setInputBufferSize(8192);  // 입력 버퍼 크기
			configurable.setIdleTimeout(Duration.ofSeconds(600));  // 유휴 타임아웃 설정
		});
		return new DefaultHandshakeHandler(strategy);
	}

Allowed Origin

기본적으로 Spring에서 WebSocket은 same-origin requests만 허용하도록 설정되어있다.
간단하게 setAllowedOrigins메서드로 Origin을 설정할 수 있다.

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		// 특정 Origin만 허용
		registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
		
		// 모든 Origin 허용
		// registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("*");
	}

	@Bean
	public WebSocketHandler myHandler() {
		return new MyHandler();
	}
}

마무리

여기까지 스프링 공식문서에서 제공하는 주요 WebSocket API에 대하여 알아보았다.
다음 포스팅부터는 STOMP에 대하여 공부해 볼 예정이다. 이후 STOMP가 주요 사용되는곳인 채팅방을 구현하여 이론과 실습까지 겸하면 좋을것같다!