아래는 **"스프링부트 웹플럭스 시리즈"**의 **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 버튼 클릭 → 서버 메시지와 에코 확인.

### 마무리

웹플럭스와 웹소켓으로 실시간 통신의 세계에 입문했습니다. 비동기 스트림과 양방향 연결이 얼마나 강력한지 느끼셨나요? 다음 장에서는 웹플럭스의 현재와 미래를 돌아보며, 이 기술의 여정을 마무리하겠습니다. 이번 실습으로 실시간 애플리케이션의 가능성을 열어보셨길 바랍니다!

---

이 장은 웹소켓의 기본 구현과 채팅 예제를 중심으로 구성했으며, 실습과 활용 사례를 간략히 다뤘습니다. 추가 기능이나 수정이 필요하면 말씀해주세요!