WebSocket API

Dev.Hammy·2024년 4월 8일
0

반응형 스택에서 이에 상응하는 내용 보기

Spring Framework는 WebSocket 메시지를 처리하는 클라이언트 측 및 서버 측 애플리케이션을 작성하는 데 사용할 수 있는 WebSocket API를 제공합니다.

WebSocketHandler

반응형 스택에서 이에 상응하는 내용 보기

WebSocket 서버를 생성하는 것은 WebSocketHandler를 구현하거나 TextWebSocketHandler 또는 BinaryWebSocketHandler를 확장하는 것만큼 간단합니다. 다음 예제에서는 TextWebSocketHandler를 사용합니다.

public class MyHandler extends TextWebSocketHandler {

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

}

다음 예제와 같이 이전 WebSocket 핸들러를 특정 URL에 매핑하기 위한 전용 WebSocket Java 구성 및 XML 네임스페이스 지원이 있습니다.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

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

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

}

다음 예에서는 앞의 예와 동일한 XML 구성을 보여줍니다.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/websocket
		https://www.springframework.org/schema/websocket/spring-websocket.xsd">

	<websocket:handlers>
		<websocket:mapping path="/myHandler" handler="myHandler"/>
	</websocket:handlers>

	<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>

앞의 예제는 Spring MVC 애플리케이션에서 사용하기 위한 것이며 DispatcherServlet 구성에 포함되어야 합니다. 그러나 Spring의 WebSocket 지원은 Spring MVC에 의존하지 않습니다. WebSocketHttpRequestHandler의 도움으로 WebSocketHandler를 다른 HTTP 제공 환경에 통합하는 것은 비교적 간단합니다.

WebSocketHandler API를 직접 사용하거나 간접적으로 사용하는 경우. 기본 표준 WebSocket 세션(JSR-356)은 동시 전송을 허용하지 않으므로 STOMP 메시징을 통해 애플리케이션은 메시지 전송을 동기화해야 합니다. 한 가지 옵션은 WebSocketSessionConcurrentWebSocketSessionDecorator로 래핑하는 것입니다.

WebSocket Handshake

반응형 스택에서 이에 상응하는 내용 보기

초기 HTTP WebSocket 핸드셰이크 요청을 사용자 정의하는 가장 쉬운 방법은 핸드셰이크 "이전"과 "이후"에 대한 메서드를 노출하는 HandshakeInterceptor를 사용하는 것입니다. 이러한 인터셉터를 사용하여 핸드셰이크를 방지하거나 WebSocketSession에서 모든 속성을 사용할 수 있도록 할 수 있습니다. 다음 예에서는 내장 인터셉터를 사용하여 HTTP 세션 속성을 WebSocket 세션에 전달합니다.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

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

}

다음 예에서는 앞의 예와 동일한 XML 구성을 보여줍니다.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/websocket
		https://www.springframework.org/schema/websocket/spring-websocket.xsd">

	<websocket:handlers>
		<websocket:mapping path="/myHandler" handler="myHandler"/>
		<websocket:handshake-interceptors>
			<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
		</websocket:handshake-interceptors>
	</websocket:handlers>

	<bean id="myHandler" class="org.springframework.samples.MyHandler"/>

</beans>

더 고급 옵션은 클라이언트 원본 유효성 검사, 하위 프로토콜 협상 및 기타 세부 정보를 포함하여 WebSocket 핸드셰이크 단계를 수행하는 DefaultHandshakeHandler를 확장하는 것입니다. 아직 지원되지 않는 WebSocket 서버 엔진 및 버전에 적응하기 위해 사용자 정의 RequestUpgradeStrategy를 구성해야 하는 경우 애플리케이션은 이 옵션을 사용해야 할 수도 있습니다(이 주제에 대한 자세한 내용은 배포 참조). Java 구성과 XML 네임스페이스 모두를 통해 사용자 정의 HandshakeHandler를 구성할 수 있습니다.

[Tip]
Spring은 추가 동작으로 WebSocketHandler를 장식하는 데 사용할 수 있는 WebSocketHandlerDecorator 기본 클래스를 제공합니다. WebSocket Java 구성 또는 XML 네임스페이스를 사용할 때 기본적으로 로깅 및 예외 처리 구현이 제공되고 추가됩니다. ExceptionWebSocketHandlerDecoratorWebSocketHandler 메서드에서 발생하는 포착되지 않은 모든 예외를 포착하고 서버 오류를 나타내는 상태 1011로 WebSocket 세션을 닫습니다.

Deployment

Spring WebSocket API는 DispatcherServlet이 HTTP WebSocket 핸드셰이크와 기타 HTTP 요청을 모두 제공하는 Spring MVC 애플리케이션에 쉽게 통합됩니다. WebSocketHttpRequestHandler를 호출하여 다른 HTTP 처리 시나리오에 쉽게 통합할 수도 있습니다. 이것은 편리하고 이해하기 쉽습니다. 그러나 JSR-356 런타임과 관련하여 특별한 고려 사항이 적용됩니다.

Jakarta WebSocket API(JSR-356)는 두 가지 배포 메커니즘을 제공합니다. 첫 번째는 시작 시 Servlet 컨테이너 클래스 경로 검색(Servlet 3 기능)과 관련됩니다. 다른 하나는 서블릿 컨테이너 초기화 시 사용하는 등록 API입니다. 이러한 메커니즘 중 어느 것도 모든 HTTP 처리(WebSocket 핸드셰이크 및 기타 모든 HTTP 요청 포함)(예: Spring MVC의 DispatcherServlet)에 대해 단일 "프론트 컨트롤러"를 사용할 수 없습니다.

이는 JSR-356 런타임에서 실행될 때에도 Spring의 WebSocket 지원이 서버별 RequestUpgradeStrategy 구현으로 주소를 지원하는 JSR-356의 중요한 제한 사항입니다. 이러한 전략은 현재 Tomcat, Jetty, GlassFish, WebLogic, WebSphere 및 Undertow(및 WildFly)에 존재합니다. Jakarta WebSocket 2.1부터는 Tomcat 10.1 및 Jetty 12와 같은 Jakarta EE 10 기반 웹 컨테이너에서 Spring이 선택하는 표준 요청 업그레이드 전략을 사용할 수 있습니다.

두 번째 고려 사항은 JSR-356을 지원하는 서블릿 컨테이너가 애플리케이션 시작 속도를 크게 늦출 수 있는 SCI(ServletContainerInitializer) 스캔을 수행해야 한다는 것입니다. JSR-356을 지원하는 서블릿 컨테이너 버전으로 업그레이드한 후 상당한 영향이 관찰되면 web.xml<absolute-ordering /> 요소를 사용하여 웹 fragment(및 SCI 스캐닝)을 선택적으로 활성화 또는 비활성화할 수 있어야 합니다. (다음 예제와 같이):

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		https://jakarta.ee/xml/ns/jakartaee
		https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
	version="5.0">

	<absolute-ordering/>

</web-app>

그런 다음 Servlet 3 Java 초기화 API에 대한 지원을 제공하는 Spring 자체 SpringServletContainerInitializer와 같이 이름별로 웹 조각을 선택적으로 활성화할 수 있습니다. 다음 예에서는 그 방법을 보여줍니다.

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		https://jakarta.ee/xml/ns/jakartaee
		https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
	version="5.0">

	<absolute-ordering>
		<name>spring_web</name>
	</absolute-ordering>

</web-app>

Configuring the Server

반응형 스택에서 이에 상응하는 내용 보기

입력 메시지 버퍼 크기, 유휴 시간 제한 등과 같은 기본 WebSocket 서버를 구성할 수 있습니다.

Jakarta WebSocket 서버의 경우 Java 구성에 ServletServerContainerFactoryBean을 추가할 수 있습니다. 예를 들어:

@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
    ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
    container.setMaxTextMessageBufferSize(8192);
    container.setMaxBinaryMessageBufferSize(8192);
    return container;
}

또는 XML 구성에:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/websocket
		https://www.springframework.org/schema/websocket/spring-websocket.xsd">

	<bean class="org.springframework...ServletServerContainerFactoryBean">
		<property name="maxTextMessageBufferSize" value="8192"/>
		<property name="maxBinaryMessageBufferSize" value="8192"/>
	</bean>

</beans>

[Note]
클라이언트 Jakarta WebSocket 구성의 경우 Java 구성에서 ContainerProvider.getWebSocketContainer()를 사용하거나 XML에서 WebSocketContainerFactoryBean을 사용하십시오.

Jetty의 경우 Consumer 콜백을 제공하여 WebSocket 서버를 구성할 수 있습니다.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(echoWebSocketHandler(), "/echo").setHandshakeHandler(handshakeHandler());
	}

	@Bean
	public DefaultHandshakeHandler handshakeHandler() {
		JettyRequestUpgradeStrategy strategy = new JettyRequestUpgradeStrategy();
		strategy.addWebSocketConfigurer(configurable -> {
				policy.setInputBufferSize(8192);
				policy.setIdleTimeout(600000);
		});
		return new DefaultHandshakeHandler(strategy);
	}

}

[Tip]
WebSocket을 통해 STOMP를 사용하는 경우 STOMP WebSocket 전송 속성도 구성해야 합니다.

Allowed Origins

반응형 스택에서 이에 상응하는 내용 보기

Spring Framework 4.1.5부터 WebSocket 및 SockJS의 기본 동작은 동일한 출처 요청만 허용하는 것입니다. 원본 목록 전체 또는 특정 목록을 허용하는 것도 가능합니다. 이 검사는 주로 브라우저 클라이언트용으로 설계되었습니다. 다른 유형의 클라이언트가 Origin 헤더 값을 수정하는 것을 막을 수 있는 방법은 없습니다(자세한 내용은 RFC 6454: The Web Origin Concept 참조).

가능한 세 가지 동작은 다음과 같습니다.

  • same-origin 요청만 허용(기본값): 이 모드에서 SockJS가 활성화되면, Iframe HTTP 응답 헤더 X-Frame-OptionsSAMEORIGIN으로 설정되고, request의 origin을 확인(check)하는 것을 허용하지 않으므로 JSONP 전송이 비활성화됩니다. 결과적으로 이 모드가 활성화되면 IE6 및 IE7은 지원되지 않습니다.

  • 지정된 origin 목록 허용: 허용되는 각 origin은 http:// 또는 https://로 시작해야 합니다. 이 모드에서는 SockJS가 활성화되면 IFrame 전송이 비활성화됩니다. 결과적으로 이 모드가 활성화되면 IE6부터 IE9까지 지원되지 않습니다.

  • 모든 origin 허용: 이 모드를 활성화하려면 허용된 origin 값으로 *를 제공해야 합니다. 이 모드에서는 모든 운송 수단을 사용할 수 있습니다.

다음 예제와 같이 WebSocket 및 SockJS 허용 origin을 구성할 수 있습니다.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
	}

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

}

다음 예에서는 앞의 예와 동일한 XML 구성을 보여줍니다.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:websocket="http://www.springframework.org/schema/websocket"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/websocket
		https://www.springframework.org/schema/websocket/spring-websocket.xsd">

	<websocket:handlers allowed-origins="https://mydomain.com">
		<websocket:mapping path="/myHandler" handler="myHandler" />
	</websocket:handlers>

	<bean id="myHandler" class="org.springframework.samples.MyHandler"/>

</beans>

0개의 댓글