지난번 Spring Reactive Programming이 왜 필요하고, 비동기 논블로킹 방식이 어떤 장점을 가져다주는지 함께 이야기했다. 이제 그 리액티브 패러다임의 핵심을 웹 애플리케이션에 적용한 Spring WebFlux에 대해 더 자세히 알아보자. Spring WebFlux는 기존의 Spring MVC와는 다른 방식으로 웹 요청을 처리하며, 특정 상황에서 매우 강력한 성능을 발휘한다.
1. Spring WebFlux : Reactive Programming의 실전 구현
지난번 이야기했던 것처럼, 대량의 동시 접속과 IO 작업이 많은 환경에서 전통적인 동기 방식의 웹 프레임워크는 스레드 고갈이나 블로킹으로 인한 성능 저하를 겪을 수 있다.
Spring WebFlux는 이런 문제를 해결하기 위해 Spring 5부터 도입된 리액티브 웹 프레임워크이다. 적은 수의 스레드로 높은 처리량과 확장성을 제공하는 논블로킹 웹 애플리케이션을 구축하는 데 핵심적인 역할을 한다.
Spring WebFlux는 고성능 IO 집약적 서비스, 마이크로서비스 간의 통신을 담당하는 API 게이트웨이, 웹소켓이나 서버 센트 이벤트(SSE)처럼 실시간 양방향 통신이 필요한 애플리케이션을 구축할 때 특히 빛을 발한다.
기존 Spring MVC와는 전혀 다른 스레드 모델 위에서 동작하기 때문에, WebFlux를 이해하는 것은 Spring 개발자에게 새로운 웹 애플리케이션 설계의 시야를 열어주는 것이라고 할 수 있다.
2. Spring WebFlux의 핵심 구성 요소
Spring WebFlux는 Spring Reactive Stack의 핵심 웹 모듈이며, 몇 가지 중요한 구성 요소들이 있다.
리액티브 서버 (Reactive Servers) Spring WebFlux 는
전통적인 서블릿 컨테이너 위에서 동작하지 않는다. 대신 Netty, Undertow, Jetty와 같은 논블로킹 웹 서버 위에서 동작한다. Spring Boot를 사용하면 별다른 설정 없이 기본적으로 내장된 Reactor Netty 서버가 사용된다. 필요하다면 application.yml 설정을 통해 다른 논블로킹 서버로 변경할 수 있다.
두 가지 프로그래밍 모델 Spring WebFlux는 웹 요청을 처리하는 두 가지 프로그래밍 모델을 제공한다.
애노테이션 기반 컨트롤러 (@RestController)
=> 가장 익숙한 방식이다. 기존 Spring MVC와 거의 동일하게 @RestController, @GetMapping, @PostMapping 등의 애노테이션을 사용하여 컨트롤러를 작성한다. 다만, 메서드의 반환 타입으로 Mono나 Flux를 사용한다. 이 방식은 기존 Spring MVC 개발자에게 진입 장벽을 낮춰준다.
package com.example.demo.webflux;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class ExampleController {
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("Hello WebFlux!");
}
}
함수형 엔드포인트 (Functional Endpoints)
=> Spring WebFlux의 특징을 더욱 잘 보여주는 모델이다. 람다 표현식을 활용하여 라우터 함수(RouterFunction)와 핸들러 함수(HandlerFunction)를 조합해서 요청을 라우팅하고 처리한다. 더 함수형 프로그래밍에 가깝고 유연하며, 설정 기반이 아닌 코드 기반의 라우팅을 제공한다.
// FunctionalRoutes.java
package com.example.demo.webflux;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration
public class FunctionalRoutes {
@Bean
public RouterFunction<ServerResponse> routes(ExampleHandler handler) {
return route(GET("/functional-hello"), handler::hello);
}
}
package com.example.demo.webflux;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class ExampleHandler {
public Mono<ServerResponse> hello(ServerRequest request) {
return ServerResponse.ok().body(Mono.just("Hello Functional WebFlux!"), String.class);
}
}
리액티브 클라이언트 (WebClient)
Spring WebFlux와 함께 사용하는 비동기 논블로킹 HTTP 클라이언트이다. 기존 RestTemplate의 블로킹 방식과는 다르게, WebClient는 적은 수의 스레드로 외부 API 호출이나 다른 마이크로서비스와의 통신을 효율적으로 처리한다. 마이크로서비스 간의 통신이 빈번한 환경에서 WebClient는 필수적인 구성 요소이다.
리액티브 데이터 접근 (Reactive Data Access) WebFlux의 논블로킹 특성을 온전히 활용하려면 데이터베이스 연동 역시 논블로킹으로 이루어져야 한다.
Spring Data R2DBC(Reactive Relational Database Connectivity)는 관계형 데이터베이스를 위한 논블로킹 드라이버와 Spring Data의 편리함을 제공한다. MongoDB 같은 NoSQL 데이터베이스는 이미 Reactive 드라이버를 제공한다.
Spring Data MongoDB Reactive 등을 사용하면 된다. 전통적인 JDBC 드라이버는 블로킹 방식이므로 WebFlux 환경에서는 적합하지 않다.
3. Spring MVC vs Spring WebFlux
Spring MVC와 Spring WebFlux는 웹 애플리케이션을 구축하는 두 가지 다른 접근 방식이다. 어떤 것을 선택할지는 프로젝트의 요구사항과 특성에 따라 달라진다.
스레드 모델 차이
MVC:
요청당 스레드 할당 모델이다. 클라이언트 요청이 들어오면 서블릿 컨테이너가 스레드를 할당하고, 해당 스레드가 모든 작업을 블로킹 방식으로 처리한다. IO 작업 시 스레드가 대기 상태에 들어간다.
WebFlux:
이벤트 루프(Event Loop) 모델이다. 적은 수의 워커 스레드가 논블로킹 방식으로 여러 요청을 동시에 처리한다. IO 작업이 발생하면 해당 스레드는 대기하지 않고 다른 요청을 처리하며, IO 작업이 완료되면 콜백으로 결과를 받는다.
적합한 사용 사례 비교
MVC:
- 전통적인 CRUD 애플리케이션이나 복잡한 비즈니스 로직이 많고, 동기 방식의 데이터베이스 연동이 주를 이루는 시스템에 적합하다.
- 이미 많은 레거시 라이브러리나 인프라가 블로킹 방식이라면 MVC를 사용하는 것이 더 현실적일 수 있다.
- 팀원들이 Reactive 프로그래밍에 익숙하지 않다면 학습 비용이 적은 MVC가 유리하다.
WebFlux:
- 대규모 동시 요청을 처리해야 하는 고성능 API 게이트웨이나 마이크로서비스.
- 데이터 스트리밍 서비스 (예: 웹소켓 기반 채팅, 대용량 파일 전송).
- 실시간 애플리케이션이나 이벤트 기반의 시스템.
- 외부 시스템과의 비동기 IO 통신이 많은 서비스.
무조건 WebFlux가 좋은 것은 아니다. Spring MVC는 여전히 대부분의 웹 애플리케이션에 매우 효과적이다. 프로젝트의 특성, 예상되는 트래픽 패턴, 팀의 Reactive 프로그래밍 숙련도, 그리고 기존 인프라가 논블로킹을 지원하는지 등을 종합적으로 고려하여 현명하게 선택해야 한다.
4. Spring WebFlux 개발 시 고려할 점
Spring WebFlux는 강력하지만, 제대로 활용하려면 몇 가지 주의할 점이 있다.
논블로킹의 강제성
WebFlux의 가장 큰 함정은 시스템의 어느 한 부분이라도 블로킹 코드가 섞이면 전체 논블로킹의 이점이 사라진다는 점이다. 예를 들어, Reactive Stream 내부에서 블로킹 방식의 JDBC를 사용하거나, 다른 블로킹 API를 호출하면 결국 스레드가 블로킹되어 리액티브의 장점이 퇴색된다. 모든 구성 요소가 논블로킹 방식으로 설계되고 구현되어야 진정한 WebFlux의 성능을 얻을 수 있다.
오류 처리의 복잡성
Reactive Stream에서의 오류 처리(onErrorResume, onErrorReturn, doOnError 등)는 전통적인 try-catch 방식과는 매우 다르다. Stream 중간에서 발생하는 오류를 적절히 처리하지 못하면 예상치 못한 동작이나 메모리 누수로 이어질 수 있다. Reactive Stream의 오류 전파 방식과 연산자별 오류 처리 방법을 정확히 이해해야 한다.
테스팅 방법의 변화
논블로킹 애플리케이션의 테스트는 기존 방식과 다르다. Spring WebFlux는 WebTestClient를 제공하여 실제 HTTP 요청 없이도 컨트롤러나 함수형 엔드포인트를 테스트할 수 있게 한다. 또한 StepVerifier와 같은 Project Reactor의 테스트 유틸리티를 사용하여 비동기 스트림의 동작을 검증해야 한다.
모니터링 및 디버깅의 어려움
Reactive 애플리케이션은 스레드 풀 기반이 아니므로, 기존의 스레드 덤프나 스레드 기반의 모니터링 툴로는 성능 병목을 파악하기 어렵다. 비동기 흐름을 추적하기 위한 새로운 모니터링 도구나 Reactor Debugging Mode와 같은 방법을 익혀야 한다.
Spring WebFlux는 고성능, 고확장성 논블로킹 웹 애플리케이션을 구축하기 위한 Spring의 강력한 솔루션이다. 이전에 다룬 Reactive Programming의 개념을 웹 환경에 구체적으로 적용한 것이 WebFlux이다. 새로운 패러다임을 요구하며, 그만큼 깊이 있는 이해와 섬세한 설계가 필요하다.
'Study > Spring' 카테고리의 다른 글
Spring Reactive Programming (0) | 2025.06.22 |
---|