Stateful VS Stateless
REST API (Stateless 방식)
✔ 개념
- REST API는 Stateless(상태 비유지) 방식으로 동작합니다.
- 클라이언트 요청(Request)은 항상 독립적으로 처리되며, 서버는 클라이언트의 이전 요청 상태를 기억하지 않습니다.
- 각 요청은 필요한 모든 정보를 포함해야 하며, 서버는 이를 기반으로 응답을 반환합니다.
✔ 장점
✅ 확장성(Scalability) 높음
- 서버는 클라이언트의 상태를 유지하지 않으므로 로드 밸런싱 및 수평 확장이 용이합니다.
- 여러 서버에서 요청을 처리해도 문제가 없으며, 서버 간 상태 동기화가 필요하지 않습니다.
✅ 단순하고 유지보수 용이
- 상태를 관리할 필요가 없기 때문에 개발이 단순하고 유지보수가 쉬워집니다.
- API 요청이 독립적이므로 디버깅과 로깅이 용이합니다.
✅ 캐싱 활용 가능
- HTTP 기반이므로 응답을 캐싱하여 성능을 최적화할 수 있습니다.
- 클라이언트 측에서 캐시를 활용하면 서버 부하를 줄일 수 있습니다.
❌ 단점
⛔ 실시간성이 부족함
- 클라이언트가 변경 사항을 지속적으로 확인하려면 주기적인 폴링이 필요하여 비효율적입니다.
- WebSocket처럼 서버에서 즉시 데이터를 Push할 수 없습니다.
⛔ 매 요청마다 인증이 필요
- Stateless 방식이므로 요청마다 인증 정보를 포함해야 하며, 이를 처리하는 과정에서 성능 오버헤드가 발생할 수 있습니다.
- JWT, OAuth와 같은 인증 토큰을 사용하여 이 문제를 해결할 수 있지만, 여전히 매 요청마다 인증 검증이 필요합니다.
🔹 WebSocket (Stateful 방식)
✔ 개념
- WebSocket은 Stateful(상태 유지) 방식으로 동작하며, 클라이언트와 서버 간의 지속적인 연결(Persistent Connection)을 유지합니다.
- 한 번 연결되면, 양방향 통신(Bi-directional Communication)이 가능하며, 서버는 클라이언트의 상태를 기억하고 필요할 때 데이터를 Push할 수 있습니다.
✔ 장점
✅ 실시간 데이터 전송 최적화
- 서버는 클라이언트의 요청 없이도 데이터를 즉시 전송(Push)할 수 있어, 실시간 애플리케이션(예: 채팅, 주식 거래, 게임)에 적합합니다.
✅ 네트워크 오버헤드 감소
- REST API와 달리, 매 요청마다 새로운 연결을 생성하지 않으므로 네트워크 리소스 사용이 효율적입니다.
- 연결이 유지되므로 인증 과정을 한 번만 수행하면 됩니다.
✅ 양방향 통신 가능
- REST API는 클라이언트 → 서버 방향의 요청-응답 방식이지만, WebSocket은 서버 → 클라이언트도 가능하여 보다 유연한 데이터 흐름을 제공합니다.
❌ 단점
⛔ 확장성이 낮음
- 서버가 각 클라이언트와의 연결을 유지해야 하므로 많은 사용자가 접속할 경우 리소스 부담이 증가합니다.
- 로드 밸런싱이 어렵고, 클러스터링 시 세션 공유 또는 연결 유지 로직이 추가적으로 필요합니다.
⛔ 복잡한 상태 관리 필요
- 연결 상태를 유지해야 하므로, 클라이언트가 예기치 않게 연결 해제되거나 재접속할 경우 상태를 관리하는 추가적인 로직이 필요합니다.
- 예: 세션 유지, 연결 끊김 감지, 재연결 로직 구현.
⛔ 브라우저 및 네트워크 제한 사항 존재
- 방화벽, 프록시 등의 네트워크 환경에서 WebSocket 연결이 차단될 수 있습니다.
- HTTP 기반보다 보안 및 안정성을 고려한 추가적인 설정이 필요할 수 있습니다.
🔹 어떤 방식을 선택해야 할까?
✔ REST API가 적합한 경우
- 요청과 응답이 독립적인 경우 (예: CRUD API, 데이터 조회 서비스)
- 서버 확장성과 유지보수가 중요한 경우
- 네트워크 및 보안 이슈가 신경 쓰이는 경우
✔ WebSocket이 적합한 경우
- 실시간성이 중요한 경우 (예: 채팅, 스트리밍, 주식 데이터, 게임)
- 양방향 통신이 필요한 경우 (예: P2P 연결)
- 연결 유지가 가능하고 성능 최적화가 필요한 경우
Spring WebFlux의 org.springframework.web.reactive.socket
기반의 웹소켓과 전통적인 Spring MVC 기반 웹소켓을 비교해보겠습니다.
1. 아키텍처적 차이
비교 항목 | Spring WebFlux (org.springframework.web.reactive.socket ) | Spring MVC (전통적인 웹소켓) |
---|---|---|
스레드 모델 | Non-blocking, 이벤트 기반 (Netty, Undertow 등 지원) | Blocking, Thread-per-connection (Tomcat, Jetty, Undertow) |
리액티브 스트림 지원 | Flux<Message> , Mono<Message> 기반 | @MessageMapping 기반 |
동시성 처리 | 요청 처리에 많은 스레드를 사용하지 않음 (적은 리소스로 높은 성능) | 많은 동시 접속 시 스레드가 증가, 성능 저하 가능 |
서버 요구 사항 | Netty, Undertow, Jetty 등 리액티브 지원 서버 필요 | Tomcat, Jetty, Undertow 지원 |
부하 분산 | 가볍고 빠르며 부하를 효과적으로 처리 가능 | 높은 동접자 수 처리 시 성능 저하 가능 |
2. 성능 및 확장성
비교 항목 | Spring WebFlux (org.springframework.web.reactive.socket ) | Spring MVC (전통적인 웹소켓) |
---|---|---|
성능 | 대량의 동시 요청 처리에 유리 | 동시 연결이 많아지면 성능 저하 |
부하 분산 | 비동기 이벤트 기반 처리로 효율적 | 스레드 증가로 인한 오버헤드 발생 |
리소스 관리 | 적은 스레드로 많은 요청 처리 가능 | 많은 동시 요청 시 스레드 리소스 소모 증가 |
지연 처리 | 이벤트 기반으로 낮은 지연 시간 | 스레드가 많아지면 응답 지연 증가 |
- WebFlux 기반 웹소켓은 비동기 이벤트 기반 모델이기 때문에 최소한의 리소스로 높은 동시성을 처리할 수 있습니다.
- 반면 Spring MVC 웹소켓은 Blocking 모델로서 요청당 하나의 스레드를 할당해야 하므로 동접자가 많아질수록 성능 저하가 발생할 수 있습니다.
3. 프로그래밍 모델 차이
✅ Spring WebFlux 웹소켓 (비동기, 리액티브)
@Component class ReactiveWebSocketHandler : WebSocketHandler { override fun handle(session: WebSocketSession): Mono<Void> { val input = session.receive() .map { it.payloadAsText } .doOnNext { println("Received: $it") } val output = Flux.interval(Duration.ofSeconds(1)) .map { "Server response $it" } .map(session::textMessage) return session.send(output) } }
✅ Spring MVC 웹소켓 (동기, Blocking)
@Controller class TraditionalWebSocketHandler { @MessageMapping("/message") @SendTo("/topic/response") fun handleMessage(message: String): String { println("Received: $message") return "Server response: $message" } }
4. 사용 시 고려 사항
✅ WebFlux 웹소켓이 유리한 경우
- 대규모 동시 연결 처리 (ex. 실시간 채팅, 금융 데이터 스트리밍)
- 리소스가 제한적인 환경 (ex. 마이크로서비스)
- Reactive Stack을 기반으로 애플리케이션을 구축할 경우
- Netty 기반 서버를 사용할 때 최적화된 성능 제공
✅ Spring MVC 웹소켓이 유리한 경우
- 기존 MVC 프로젝트와의 통합이 필요할 경우
- 비교적 적은 연결 수를 처리하는 서비스 (ex. 내부 알림 시스템)
- 기존의
@Controller
기반 프로그래밍이 익숙한 경우 - 톰캣(Tomcat)을 사용해야 하는 경우 (WebFlux는 기본적으로 Netty를 사용)
5. 정리
비교 항목 | Spring WebFlux (org.springframework.web.reactive.socket ) | Spring MVC (전통적인 웹소켓) |
---|---|---|
비동기 지원 | ✅ 완전한 비동기 리액티브 모델 | 🚫 동기적 처리 |
성능 | ✅ 높은 동시성 처리 가능 | 🚫 동접자가 많으면 성능 저하 |
사용하기 쉬운가? | 🚫 다소 복잡한 코드 구조 | ✅ 전통적인 MVC 패턴과 유사 |
서버 호환성 | 🚫 Netty, Undertow 등 필요 | ✅ Tomcat, Jetty 지원 |
적용 사례 | ✅ 실시간 데이터 스트리밍, 채팅, 알림 | ✅ 단순한 웹소켓 기능 |
🔥 결론
- 대규모 동시 연결 및 성능이 중요한 서비스에서는
Spring WebFlux
의 비동기 리액티브 웹소켓이 적합합니다. - 기존 Spring MVC 기반 프로젝트와의 호환성이 중요하고, 동접자가 많지 않다면 전통적인
Spring MVC 웹소켓
이 더 쉬운 선택이 될 수 있습니다.
🚀 즉, 성능과 확장성이 중요한 경우 WebFlux, 익숙한 개발 방식과 기존 시스템 통합이 중요한 경우 MVC 웹소켓을 선택하는 것이 좋습니다.