✅ Spring Webflux란?
Spring webflux는 리액티브 웹 애플리케이션 구현을 위해 Spring 5.0부터 지원되는 Reactive Web Framework이다.
Spring MVC는 서블릿 기반의 Blocking I/O 방식을 사용한다.
📌Blocking I/O VS Non-Blocking I/O
⇒ 다른 주체가 작업할 때 물리적으로 자신의 제어권이 있는지가 기준이다. (물리적인 실행 특성)
✔️ Blocking I/O
- 자신의 작업을 진행하다가 다른 주체의 작업이 시작되면, 다른 작업이 끝날 때까지 기다렸다가 자신의 작업을 시작하는 방식이다.
물리적으로 I/O 작업이 끝날 때까지 다른 작업을 하지 않고 기다리는 것을 의미한다.
- ex) 파일을 읽거나 네트워크 요청을 처리할 때, I/O 작업이 완료될 때까지 해당 스레드는 다른 작업을 할 수 없다.
✔️ Non-Blocking I/O- 다른 주체의 작업에 관계없이 자신의 작업을 수행할 수 있는 방식이다.
I/O 작업이 진행되는 동안에도 물리적으로 대기하지 않고, 다른 작업을 수행할 수 있다.
- ex) 네트워크 요청을 보낸 후, 응답이 올 때까지 기다리지 않고 다른 요청을 처리하거나 다른 작업을 계속 수행하는 것.
✅ Spring MVC의 특징:
Spring MVC는 기본적으로 스레드 풀(thread pool)을 사용하여 요청을 처리한다.
기본적으로 약 200개의 스레드를 가지고 있다. 이 스레드가 모두 사용 중일 때 추가 요청이 들어오면 대기 상태가 된다.
트래픽 증가: 많은 사용자가 동시에 접근할 경우, 더 많은 스레드가 필요하게 된다. 만약 만 명이 동시에 접속하면 200개의 스레드로는 처리하기 어렵다.
스레드 스위칭 비용: 스레드를 많이 사용할수록 스레드 간의 전환 비용이 증가하게 된다. 이는 성능 저하를 초래한다.
Spring WebFlux는 대용량 트래픽을 효과적으로 처리하기 위해 비동기/Non-Blocking I/O 방식을 사용한다.
✅ Spring WebFlux의 특징:
1. 비동기/Non-Blocking I/O:
요청을 처리하는 동안 스레드가 차단되지 않는다. 즉, 요청 처리가 완료될 때까지 스레드가 대기 상태에 머물지 않는다.
이는 이벤트 루프(event loop) 모델을 사용하여 비동기적으로 작업을 처리한다.
높은 확장성: 대용량 트래픽을 안정적으로 처리할 수 있다.
효율성: 리소스 사용을 최적화하여 더 많은 요청을 처리할 수 있다.
Mono: 0 또는 1개의 요소를 비동기적으로 반환하는 객체.
Flux: 0부터 N개의 요소를 비동기적으로 반환하는 객체.
이들을 사용하여, 개발자는 비동기적으로 데이터를 처리하고, 트래픽이 많은 상황에서도 효율적으로 시스템을 운영할 수 있다.
📌 Spring WebFlux는 Spring MVC의 한계를 극복하기 위해 등장한 솔루션으로, 비동기/논블로킹 I/O 방식을 통해 대용량 트래픽을 안정적으로 처리할 수 있다. 이를 통해 스레드 사용량을 줄이고, 시스템 성능을 향상시킬 수 있다.
MVC와 WebFlux의 차이를 예시를 통해 이해하기
🎯시나리오
클라이언트가 백엔드 서버로 요청을 보낸다. 백엔드 서버는 외부 서버로 요청을 보내고, 외부 서버의 동작은 5초가 걸린다고 가정한다. 클라이언트의 요청이 총 5번 들어왔을 때의 상황을 비교해볼 것이다.
1. MVC의 경우
Spring MVC는 서블릿 기반의 동기식 처리를 수행한다. 외부 서버로 요청을 보낼 때 동기 처리 방식인 RestTemplate을 사용한다.
동작 과정:
이 과정을 5번 반복합니다. 즉, 각 요청이 완료될 때까지 백엔드 서버의 스레드는 블로킹된다.
5번 요청 * 5초(외부 서버 응답 시간) = 총 25초
각 요청이 순차적으로 처리되기 때문에, 총 25초가 걸린다.
코드 예시
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class MvcController {
// GET 요청을 받을 때 "/mvc" 엔드포인트를 처리하는 메소드
@GetMapping("/mvc")
public String handleRequest() {
// RestTemplate 객체를 생성. RestTemplate는 동기 방식으로 외부 서버와 통신할 때 사용됨.
RestTemplate restTemplate = new RestTemplate();
// 외부 서버로 GET 요청을 보내고 응답을 String 타입으로 받아온다.
// "http://external-service.com"은 요청을 보낼 외부 서버의 URL.
String response = restTemplate.getForObject("http://external-service.com", String.class);
// 외부 서버의 응답을 클라이언트에게 반환합니다.
return response;
}
}
2. WebFlux의 경우
Spring WebFlux는 비동기 논블로킹 방식의 리액티브 프로그래밍을 지원한다. 외부 서버로 요청을 보낼 때 비동기 방식인 WebClient를 사용합니다.
동작 과정:
이 과정이 비동기적으로 수행되기 때문에, 5번의 요청을 동시에 처리할 수 있다.
외부 서버 응답 시간 5초 + 비동기 처리 = 총 5초
블로킹 없이 모든 요청이 동시에 처리되기 때문에, 총 5초가 걸린다.
코드예시
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@RestController
public class WebFluxController {
// WebClient 객체를 생성. WebClient는 비동기식으로 외부 서버와 통신할 때 사용된다.
private final WebClient webClient = WebClient.create("http://external-service.com");
// GET 요청을 받을 때 "/webflux" 엔드포인트를 처리하는 메소드
@GetMapping("/webflux")
public Mono<String> handleRequest() {
// WebClient를 사용하여 외부 서버로 GET 요청을 보낸다.
// retrieve() 메소드는 응답 본문을 읽어온다.
// bodyToMono(String.class)는 응답 본문을 Mono<String> 타입으로 변환한다.
return webClient.get()
.retrieve()
.bodyToMono(String.class);
}
}