1. 개요
이 기사에서는 Spring WebFlux에서 제공하는 반응형 기능과 함께 새로운 Spring 5 WebSockets API를 사용하여 간단한 예제를 만들 것입니다.
WebSocket은 클라이언트와 서버 간의 전이중 통신을 가능하게 하는 잘 알려진 프로토콜로, 일반적으로 클라이언트와 서버가 짧은 대기 시간으로 높은 빈도로 이벤트를 교환해야 하는 웹 애플리케이션에서 사용됩니다.
Spring Framework 5는 프레임워크에서 WebSocket 지원을 현대화하여 이 통신 채널에 반응 기능을 추가했습니다.
Spring WebFlux에 대한 자세한 내용은 여기 에서 확인할 수 있습니다 .
2. 메이븐 의존성
현재 Spring Milestone Repository 에서 사용할 수 있는 spring-boot-integration 및 spring-boot-starter-webflux 에 대한 spring-boot-starters 의존성을 사용할 것 입니다.
이 예에서는 사용 가능한 최신 버전인 2.0.0.M7을 사용하고 있지만 항상 Maven 리포지토리에서 사용 가능한 최신 버전을 가져와야 합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
3. Spring의 WebSocket 구성
구성은 매우 간단 합니다. Spring WebSocket 애플리케이션에서 소켓 세션을 처리하기 위해 WebSocketHandler 를 주입합니다.
@Autowired
private WebSocketHandler webSocketHandler;
또한 요청과 핸들러 객체 간의 매핑을 담당할 HandlerMapping 빈 어노테이션 메서드를 생성해 보겠습니다.
@Bean
public HandlerMapping webSocketHandlerMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/event-emitter", webSocketHandler);
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(1);
handlerMapping.setUrlMap(map);
return handlerMapping;
}
연결할 수 있는 URL은 ws://localhost:<port>/event-emitter입니다.
4. Spring의 WebSocket 메시지 처리
ReactiveWebSocketHandler 클래스는 서버 측에서 WebSocket 세션 관리를 담당합니다 .
WebSocketHandler 인터페이스를 구현 하므로 WebSocket 클라이언트에 메시지를 보내는 데 사용할 핸들 메서드 를 재정의할 수 있습니다 .
@Component
public class ReactiveWebSocketHandler implements WebSocketHandler {
// private fields ...
@Override
public Mono<Void> handle(WebSocketSession webSocketSession) {
return webSocketSession.send(intervalFlux
.map(webSocketSession::textMessage))
.and(webSocketSession.receive()
.map(WebSocketMessage::getPayloadAsText)
.log());
}
}
5. 간단한 반응형 WebSocket 클라이언트 만들기
이제 WebSocket 서버와 연결하고 정보를 교환할 수 있는 Spring Reactive WebSocket 클라이언트를 생성해 보겠습니다.
5.1. 메이븐 의존성
먼저 Maven 의존성입니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
여기에서는 이전에 반응형 WebSocket 서버 애플리케이션을 설정하는 데 사용된 것과 동일한 spring-boot-starter-webflux를 사용하고 있습니다.
5.2. 웹소켓 클라이언트
이제 서버와의 통신 시작을 담당 하는 ReactiveClientWebSocket 클래스를 생성해 보겠습니다.
public class ReactiveJavaClientWebSocket {
public static void main(String[] args) throws InterruptedException {
WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute(
URI.create("ws://localhost:8080/event-emitter"),
session -> session.send(
Mono.just(session.textMessage("event-spring-reactive-client-websocket")))
.thenMany(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.log())
.then())
.block(Duration.ofSeconds(10L));
}
}
위의 코드에서 우리는 Reactor Netty와 함께 사용하기 위한 WebSocketClient 구현인 ReactorNettyWebSocketClient 를 사용하고 있음을 알 수 있습니다.
또한 클라이언트는 URL ws://localhost:8080/event-emitter 를 통해 WebSocket 서버 에 연결하여 서버에 연결되는 즉시 세션을 설정합니다.
또한 연결 요청과 함께 서버에 메시지(" event-spring-reactive-client-websocket ")를 보내고 있음을 알 수 있습니다.
또한 매개 변수로 Publisher<T> 유형의 변수를 기대하면서 메서드 send 가 호출됩니다. 이 경우 Publisher<T> 는 Mono<T> 이고 T 는 간단한 문자열 " event-me-from-reactive- java-client-websocket ".
또한 String 유형 의 Flux 를 예상하는 thenMany(…) 메서드 가 호출됩니다. receive() 메서드는 들어오는 메시지 의 흐름을 가져와 나중에 문자열로 변환합니다.
마지막으로 block() 메서드는 주어진 시간(예제에서는 10초) 후에 클라이언트가 서버에서 연결을 끊도록 강제합니다.
5.3. 클라이언트 시작
이를 실행하려면 Reactive WebSocket Server가 실행 중인지 확인하십시오. 그런 다음 ReactiveJavaClientWebSocket 클래스를 시작하면 sysout 로그에서 방출되는 이벤트를 볼 수 있습니다 .
[reactor-http-nio-4] INFO reactor.Flux.Map.1 -
onNext({"eventId":"6042b94f-fd02-47a1-911d-dacf97f12ba6",
"eventDt":"2018-01-11T23:29:26.900"})
또한 Reactive WebSocket 서버의 로그에서 연결 시도 중에 클라이언트가 보낸 메시지를 볼 수 있습니다.
[reactor-http-nio-2] reactor.Flux.Map.1:
onNext(event-me-from-reactive-java-client)
또한 클라이언트가 요청을 완료한 후(이 경우 10초 후) 종료된 연결 메시지를 볼 수 있습니다.
[reactor-http-nio-2] reactor.Flux.Map.1: onComplete()
6. 브라우저 WebSocket 클라이언트 생성
반응형 WebSocket 서버 애플리케이션을 사용하기 위해 간단한 HTML/Javascript 클라이언트 WebSocket을 만들어 보겠습니다.
<div class="events"></div>
<script>
var clientWebSocket = new WebSocket("ws://localhost:8080/event-emitter");
clientWebSocket.onopen = function() {
console.log("clientWebSocket.onopen", clientWebSocket);
console.log("clientWebSocket.readyState", "websocketstatus");
clientWebSocket.send("event-me-from-browser");
}
clientWebSocket.onclose = function(error) {
console.log("clientWebSocket.onclose", clientWebSocket, error);
events("Closing connection");
}
clientWebSocket.onerror = function(error) {
console.log("clientWebSocket.onerror", clientWebSocket, error);
events("An error occured");
}
clientWebSocket.onmessage = function(error) {
console.log("clientWebSocket.onmessage", clientWebSocket, error);
events(error.data);
}
function events(responseEvent) {
document.querySelector(".events").innerHTML += responseEvent + "<br>";
}
</script>
WebSocket 서버가 실행 중인 상태에서 이 HTML 파일을 브라우저(예: Chrome, Internet Explorer, Mozilla Firefox 등)에서 열면 에 정의된 대로 이벤트당 1초의 지연으로 화면에 이벤트가 인쇄되는 것을 볼 수 있습니다. WebSocket 서버.
{"eventId":"c25975de-6775-4b0b-b974-b396847878e6","eventDt":"2018-01-11T23:56:09.780"}
{"eventId":"ac74170b-1f71-49d3-8737-b3f9a8a352f9","eventDt":"2018-01-11T23:56:09.781"}
{"eventId":"40d8f305-f252-4c14-86d7-ed134d3e10c6","eventDt":"2018-01-11T23:56:09.782"}
7. 결론
여기에서는 Spring Webflux에서 제공하는 새로운 반응 기능을 구현하여 Spring 5 Framework를 사용하여 서버와 클라이언트 간의 WebSocket 통신을 생성하는 방법의 예를 제시했습니다.
항상 그렇듯이 전체 예제는 GitHub 리포지토리 에서 찾을 수 있습니다 .