최근 고성능 비동기 API를 구현해야 할 필요가 생기면서 기존의 Spring MVC로는 처리 성능에 한계가 있음을 느꼈다..🤔
더 많은 요청을 효율적으로 처리할 방법을 찾던 중, Spring이 기존 MVC 외에 논블로킹 비동기 웹 애플리케이션을 지원하는 Spring WebFlux를 제공한다는 것을 알게 되었습니다.
이글에서는 Spring WebFlux의 개념에 대해 알아보겠습니다.
Spring WebFlux 개요
Spring WebFlux는 비동기 및 논블로킹 I/O를 기반으로 한 웹 프레임워크입니다.
전통적인 Spring MVC의 동기적 구조와 달리 WebFlux는 Reactive Streams API를 기반으로 논블로킹 방식의 고성능 비동기 웹 애플리케이션을 구축할 수 있습니다.
전통적인 Servlet Stack과 Reactive Stack의 차이
- Spring MVC: Servlet 기반의 동기적 요청-응답 처리 모델을 사용하여 서버가 요청을 처리하는 동안 스레드를 블로킹합니다. 이는 다수의 요청을 처리할 때 성능상의 제한을 초래할 수 있습니다.
- Spring WebFlux: Reactive Stack을 사용하여 요청-응답을 논블로킹 방식으로 처리하므로 I/O 작업에서의 효율이 매우 높습니다. 이는 높은 동시성을 요구하는 환경에서 특히 유리합니다.
WebFlux의 핵심 구성 요소
Mono와 Flux
Spring WebFlux의 비동기 데이터 흐름은 Mono와 Flux라는 두 가지 리액티브 타입으로 이루어집니다.
- Mono
단일 값(또는 빈 값)을 나타내는 Publisher입니다.
예를 들어, 특정 사용자 정보를 조회할 때 하나의 결과가 반환되면 Mono로 처리할 수 있습니다. - Flux
다중 값 스트림을 나타내며 여러 개의 데이터를 순차적으로 방출할 때 사용합니다.
예를 들어, 모든 사용자의 정보를 스트리밍 방식으로 가져올 때 사용할 수 있습니다.
Non-blocking I/O와 Backpressure
WebFlux는 논블로킹 방식으로 I/O 작업을 처리하며, 데이터 소비 속도를 조절하기 위해 Backpressure를 지원합니다. Backpressure는 데이터가 너무 빠르게 흘러올 때 처리 속도를 제어하는 기능으로, 안정적인 데이터 흐름을 유지하는 데 도움이 됩니다.
Spring WebFlux를 이용한 비동기 REST API 개발
WebFlux에서는 두 가지 방식으로 REST API를 구현할 수 있습니다.
@RestController와 같은 전통적인 어노테이션 기반 방식을 사용할 수도 있지만, Router Function과 Handler Function을 사용한 함수형 프로그래밍 스타일로도 구현 가능합니다.
Router Function과 Hanler Function 사용 예제
아래의 간단한 사용자 API 예제를 살펴보겠습니다.
// Handler Function
@Component
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
public Mono<ServerResponse> getUserById(ServerRequest request) {
String userId = request.pathVariable("id");
return userService.findUserById(userId)
.flatMap(user -> ServerResponse.ok().bodyValue(user))
.switchIfEmpty(ServerResponse.notFound().build());
}
}
// Router Function
@Configuration
public class UserRouter {
@Bean
public RouterFunction<ServerResponse> route(UserHandler handler) {
return RouterFunctions.route(GET("/users/{id}"), handler::getUserById);
}
}
위 코드에서 `UserHandler`는 요청을 처리하는 비즈니스 로직을 포함하고, `UserRouter`는 HTTP 경로와 Handler를 연결합니다.
WebClient로 외부 API 호출
Spring WebFlux에서는 비동기 HTTP 요청을 위해 WebClient를 사용합니다. WebClient는 RestTemplate의 대안으로 비동기, 논블로킹 HTTP 클라이언트 역할을 합니다.
WebClient webClient = WebClient.create("https://rosytest.com");
public Mono<User> getUserById(String userId) {
return webClient.get()
.uri("/users/{id}", userId)
.retrieve()
.bodyToMono(User.class);
}
위 코드는 특정 사용자 ID에 따라 외부 API에 비동기 요청을 보내고 Mono<User> 타입으로 결과를 받습니다.
예외 처리와 예외 관리
WebFlux에서 에러는 데이터 흐름 내에서 처리할 수 있으며, onErrorResume 같은 에러 핸들링 메서드를 통해 예외 상황을 대처할 수 있습니다. 또한, WebFlux 애플리케이션의 전역 예외 처리를 위해 GlobalExceptionHandler를 구현하여 에러를 일관성 있게 관리할 수도 있습니다.
public Mono<User> getUser(String userId) {
return userService.findUserById(userId)
.onErrorResume(e -> {
log.error("Error occurred", e);
return Mono.empty(); // 에러 시 빈 Mono 반환
});
}
Spring WebFlux의 장단점
장점
- 고성능 비동기 처리: 논블로킹 I/O를 통해 높은 트래픽을 효율적으로 처리할 수 있습니다.
- CPU 효율성 증가: 비동기 방식이므로 CPU 사용률을 최적화하여 더 많은 요청을 동시에 처리할 수 있습니다.
- 유연성: RESTful 서비스뿐만 아니라 WebSocket, Server-Sent Events 같은 실시간 서비스 구현에도 유리합니다.
단점
- 코드 복잡성: 비동기 프로그래밍의 특성상 로직이 복잡해질 수 있습니다.
- 디버깅 어려움: 비동기 처리가 스택 트레이스 추적을 어렵게 하여 문제 해결이 복잡할 수 있습니다.
💡 Spring WebFlux는 비동기 논블로킹 웹 애플리케이션을 구축할 수 있는 강력한 도구로, 특히 높은 트래픽 환경에서 효율적인 성능을 발휘할 수 있습니다. WebClient, Router Function, 그리고 Mono와 Flux를 적절히 활용하면 고성능의 확장 가능한 애플리케이션을 개발하는 데 큰 도움이 됩니다.
'Spring' 카테고리의 다른 글
[Spring] RestTemplate에서 PATCH가 안 된다면? 원인과 해결 방법 정리 (1) | 2025.03.01 |
---|---|
[Spring] @Component vs @Bean (0) | 2024.11.16 |
[Spring] 프록시 패턴(Proxy Pattern) (1) | 2024.11.09 |
[Spring] @Async로 비동기 작업 최적화하기 (0) | 2024.11.08 |
동시성 문제 해결을 위한 ThreadLocal 이해하기 (1) | 2024.11.02 |