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 메시징을 통해 애플리케이션은 메시지 전송을 동기화해야 합니다. 한 가지 옵션은 WebSocketSession
을 ConcurrentWebSocketSessionDecorator
로 래핑하는 것입니다.
초기 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 네임스페이스를 사용할 때 기본적으로 로깅 및 예외 처리 구현이 제공되고 추가됩니다.ExceptionWebSocketHandlerDecorator
는WebSocketHandler
메서드에서 발생하는 포착되지 않은 모든 예외를 포착하고 서버 오류를 나타내는 상태1011
로 WebSocket 세션을 닫습니다.
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>
입력 메시지 버퍼 크기, 유휴 시간 제한 등과 같은 기본 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 전송 속성도 구성해야 합니다.
Spring Framework 4.1.5부터 WebSocket 및 SockJS의 기본 동작은 동일한 출처 요청만 허용하는 것입니다. 원본 목록 전체 또는 특정 목록을 허용하는 것도 가능합니다. 이 검사는 주로 브라우저 클라이언트용으로 설계되었습니다. 다른 유형의 클라이언트가 Origin
헤더 값을 수정하는 것을 막을 수 있는 방법은 없습니다(자세한 내용은 RFC 6454: The Web Origin Concept 참조).
가능한 세 가지 동작은 다음과 같습니다.
same-origin 요청만 허용(기본값): 이 모드에서 SockJS가 활성화되면, Iframe HTTP 응답 헤더 X-Frame-Options
가 SAMEORIGIN
으로 설정되고, 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>