2025-04-08T19:56:24
This commit is contained in:
163
docs/webflux/11_웹플럭스와 웹소켓.md
Normal file
163
docs/webflux/11_웹플럭스와 웹소켓.md
Normal file
@@ -0,0 +1,163 @@
|
||||
아래는 **"스프링부트 웹플럭스 시리즈"**의 **11장: 웹플럭스와 웹소켓"**에 대한 초안입니다. 10장의 마이크로서비스 기반을 활용하여 웹소켓을 통해 실시간 통신을 구현하며, 간단한 채팅 애플리케이션 예제를 포함했습니다. 코드도 간략히 유지하며 초보자가 따라 하기 쉽게 자연스러운 문체로 작성했습니다.
|
||||
|
||||
---
|
||||
|
||||
## 11. 웹플럭스와 웹소켓
|
||||
|
||||
10장에서 마이크로서비스를 구축하며 웹플럭스의 비동기 특성을 실감했습니다. 이번 장에서는 한 단계 더 나아가 웹소켓(WebSocket)을 활용해 실시간 통신을 구현해보겠습니다. 웹플럭스와 웹소켓은 실시간 데이터 전송에 최적화된 조합으로, 채팅 같은 기능을 쉽게 만들 수 있습니다. 간단한 예제를 통해 실습해보죠. 준비되셨나요?
|
||||
|
||||
### 실시간 통신을 위한 웹소켓 구현
|
||||
|
||||
웹소켓은 클라이언트와 서버 간 양방향 통신을 가능하게 하는 프로토콜입니다. HTTP와 달리 연결을 유지하며, 데이터를 주고받는 데 지연이 적습니다. 웹플럭스는 이를 기본 지원하니, 추가 의존성 없이 바로 시작할 수 있습니다.
|
||||
|
||||
10장의 User Service를 확장해 웹소켓 기반 채팅 기능을 추가해보겠습니다. 먼저, 웹소켓 핸들러를 만듭니다.
|
||||
|
||||
#### 웹소켓 핸들러 작성
|
||||
`src/main/java/com/example/demo`에 `ChatWebSocketHandler` 클래스를 추가합니다:
|
||||
|
||||
```java
|
||||
package com.example.demo;
|
||||
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class ChatWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
private final Flux<String> messageFlux;
|
||||
|
||||
public ChatWebSocketHandler() {
|
||||
// 간단한 메시지 스트림 예제
|
||||
this.messageFlux = Flux.interval(Duration.ofSeconds(1))
|
||||
.map(i -> "Message " + i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession session) {
|
||||
// 클라이언트로부터 메시지 수신
|
||||
Flux<String> input = session.receive()
|
||||
.map(message -> "Echo: " + message.getPayloadAsText());
|
||||
|
||||
// 클라이언트로 메시지 전송
|
||||
return session.send(messageFlux.mergeWith(input)
|
||||
.map(session::textMessage));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `messageFlux`: 서버에서 주기적으로 보내는 메시지 스트림 (예시용).
|
||||
- `handle`: 클라이언트 메시지를 받아 에코로 반환하고, 서버 메시지도 함께 전송.
|
||||
|
||||
#### 웹소켓 라우팅 설정
|
||||
`src/main/java/com/example/demo`에 `WebSocketConfig`를 추가합니다:
|
||||
|
||||
```java
|
||||
package com.example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
|
||||
@Bean
|
||||
public HandlerMapping webSocketMapping() {
|
||||
Map<String, WebSocketHandler> map = new HashMap<>();
|
||||
map.put("/chat", new ChatWebSocketHandler());
|
||||
|
||||
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
|
||||
mapping.setUrlMap(map);
|
||||
mapping.setOrder(-1); // 다른 매핑보다 우선
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `/chat` 경로로 웹소켓 연결을 처리.
|
||||
|
||||
### 웹플럭스와 웹소켓의 조합 예제
|
||||
|
||||
User Service (`port: 8081`)에 위 코드를 추가한 뒤 실행합니다. 이제 클라이언트에서 웹소켓 연결을 테스트할 수 있습니다.
|
||||
|
||||
#### 클라이언트 테스트
|
||||
간단한 HTML/JS 클라이언트를 만들어 보죠. `src/main/resources/static`에 `index.html`을 추가합니다:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebSocket Chat</title>
|
||||
</head>
|
||||
<body>
|
||||
<input id="message" type="text">
|
||||
<button onclick="sendMessage()">Send</button>
|
||||
<div id="messages"></div>
|
||||
|
||||
<script>
|
||||
const socket = new WebSocket("ws://localhost:8081/chat");
|
||||
const messagesDiv = document.getElementById("messages");
|
||||
|
||||
socket.onmessage = function(event) {
|
||||
const msg = document.createElement("p");
|
||||
msg.textContent = event.data;
|
||||
messagesDiv.appendChild(msg);
|
||||
};
|
||||
|
||||
function sendMessage() {
|
||||
const input = document.getElementById("message");
|
||||
socket.send(input.value);
|
||||
input.value = "";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
`http://localhost:8081/index.html`에 접속하면:
|
||||
1. 서버에서 1초마다 "Message 0", "Message 1" 등이 표시.
|
||||
2. 입력창에 메시지를 입력하고 Send 버튼을 누르면 "Echo: [입력값]" 반환.
|
||||
|
||||
### 활용 사례 (채팅 애플리케이션 등)
|
||||
|
||||
위 예제는 기본적인 에코 서버지만, 이를 확장하면 실시간 채팅 애플리케이션을 만들 수 있습니다:
|
||||
- **다중 사용자 지원**: `Flux`를 공유 가능한 스트림으로 만들어 모든 클라이언트에 브로드캐스트.
|
||||
```java
|
||||
private final Sinks.Many<String> messageSink = Sinks.many().multicast().onBackpressureBuffer();
|
||||
private final Flux<String> messageFlux = messageSink.asFlux();
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession session) {
|
||||
Flux<String> input = session.receive()
|
||||
.map(message -> session.getId() + ": " + message.getPayloadAsText())
|
||||
.doOnNext(messageSink::tryEmitNext);
|
||||
|
||||
return session.send(messageFlux.map(session::textMessage));
|
||||
}
|
||||
```
|
||||
- **상태 관리**: Redis나 데이터베이스로 채팅 기록 저장.
|
||||
- **인증**: 스프링 시큐리티로 웹소켓 연결에 토큰 기반 인증 추가.
|
||||
|
||||
채팅 외에도 주식 시세 업데이트, 실시간 알림 등 다양한 활용이 가능합니다.
|
||||
|
||||
### 테스트해보기
|
||||
|
||||
1. User Service 실행.
|
||||
2. 브라우저에서 `http://localhost:8081/index.html` 열기.
|
||||
3. 메시지 입력 후 Send 버튼 클릭 → 서버 메시지와 에코 확인.
|
||||
|
||||
### 마무리
|
||||
|
||||
웹플럭스와 웹소켓으로 실시간 통신의 세계에 입문했습니다. 비동기 스트림과 양방향 연결이 얼마나 강력한지 느끼셨나요? 다음 장에서는 웹플럭스의 현재와 미래를 돌아보며, 이 기술의 여정을 마무리하겠습니다. 이번 실습으로 실시간 애플리케이션의 가능성을 열어보셨길 바랍니다!
|
||||
|
||||
---
|
||||
|
||||
이 장은 웹소켓의 기본 구현과 채팅 예제를 중심으로 구성했으며, 실습과 활용 사례를 간략히 다뤘습니다. 추가 기능이나 수정이 필요하면 말씀해주세요!
|
||||
Reference in New Issue
Block a user