WebFlux의 개념과 사용법
0. 목차
- WebFlux, 넌 누구냐
- Reactive Programming
- WebFlux 개념
- WebFlux란?
- WebFlux의 등장 배경
- WebFlux 사용의 권장되는 환경
- WebFlux가 무조건 좋은가?
- Spring MVC vs WebFlux
- Mono vs Flux
1. WebFlux, 넌 누구냐
2024년부터 팀원분과 새로운 프로젝트를 진행하게 됐다.
이미 어느정도 기본 셋팅이 이루어진 프로젝트였는데, 이 프로젝트에서는 WebFlux
로 외부 API를 호출하고 있었다.
WebFlux
는 들어만봤지 실제로는 사용한 적이 없었는데, 대략적으로 전통적인 Spring MVC
에 비해 스레드를 상대적으로 덜 사용한다 정도까지만 알고 있었다.
제대로 사용하기 전에 왜 이 프로젝트에서는 WebFlux
를 사용했는지, Spring MVC
에 비해 상대적으로 어떤 장단점을 가지고 있는지 알고가면 좋겠다는 생각이 들어서 이 글을 정리해본다.
2. Reactive Programming
Reactive Programming
이라고하면 보통은 아래 4가지를 준수한다.
- 성능과 확장성
- 비동기적으로 동작하고, 병렬성을 통해 성능과 확장성을 향상
- 다수의 이벤트를 동시에 처리할 수 있으며, 필요에 따라 시스템을 확장
- 반응성과 사용자 경험
- 리액티브 시스템은 실시간으로 데이터 변화에 반응하여 빠른 응답을 제공
- 이용자 경험을 향상시키는데 도움이 됨
- 장애 처리와 회복력
- 장애가 발생하더라도 탄력적으로 대응 가능
- 오류를 격리시키고, 다른 컴포넌트에 영향을 주지 않으면서 정상 동작을 유지
말로만 봐서는 한 번에 이해가 안되는데, 코드에서 예시를 찾아보자.
기존의 명령형 프로그래밍
var string = 'THis is the midday show with Cheryl Waters';
var urlFriendly = "";
for(var i=0; i<string.length; i++){
if (string[i] === " ") {
urlFriendly += "-";
} else {
urlFriendly += string[i];
}
}
console.log(urlFriendly);
새로운 패러다임, 선언적 프로그래밍
const string = 'This is the midday show with Cheryl Waters';
const urlFriendly = string.replace(/ /g, '-');
console.log(urlFriendly);
보다시피 선언적 프로그래밍에서는 구체적인 절차 대신 replace라는 함수를 사용해 추상적인 개념을 표현한다.
선언적 프로그래밍의 코드 구문에서는 어떤 일이 발생해야 하는지 기술하고, 실제로 그 작업을 처리하는 방법은 추상화로 아랫단에 감추어진다.
코드만 보자면 Java 8
버전부터 제공된 Lambda
의 간결함이 떠오르는데, 결론적으로 Reactive Programming
에 대해서 아래와 같이 요약을 정리했고, 이러한 특징들로 인해 자원을 보다 효율적으로 활용하고 동시다발적인 이벤트를 빠르게 처리하는 것이 가능하다는 특징을 이해했다.
- 비동기 (Asynchronous)
- 논 블록킹 (Non-Blocking)
참고로 동기와 비동기, 블록킹과 논 블로킹의 개념은 서로 비슷하면서도 다른데, 간략하게 요약하자면 아래와 같은 특징들이 있다.
- 동기 & 비동기 : 리턴 값을 확인 유무로 구분
- 동기 : 리턴 값을 기다리거나, 완료가 안되고 리턴 값을 받더라도 작업 완료 여부를 계속 확인
- 비동기 : 리턴 값을 신경쓰지 않고 따로 동작
- 블로킹 & 논 블로킹 : 제어권의 주체를 넘기느냐 안 넘기느냐의 구분
- 블로킹 : 제어권을 넘기고 제어권을 다시 돌려받을 때까지 기다렸다가 돌려받고 동작을 시작
- 논블로킹 : 제어권을 넘기지 않고 동작
디테일한 내용이 궁금하다면 다른 개발자분이 작성한 동기와 비동기 / 블로킹과 논블로킹 - Click을 참고하자.
3. WebFlux 개념
(1) WebFlux란?
Spring 5
에서 새롭게 추가된 Reactvie-Stack의 웹 프레임워크SpringBoot
는 2.0 부터 사용 가능- Reactive Application 개발을 위한 논 블록킹 리액티브 스트림을 지원
(2) WebFlux의 등장 배경
- 적은 양의 스레드로 동시성 이슈를 해결하기 위한 논 블록킹 웹스택의 필요성으로 인해 등장
- 비동기 논블로킹 환경의 서버
Netty
가 부상하고 있었으며,Netty
와의 연동을 위해 Srping은 새로운 API가 필요했음 - 기존 SpringMVCdml Servlet API는 v3.1부터 논블로킹을 위한 API를 제공해왔지만, 동기적으로 처리하는 모듈(Filter, Servlet)과 블로킹 방식의 API(getParameter, getPart)들이 있기에 완벽한 논블로킹 환경의 개발을 할 수 었었음
(3) WebFlux가 권장되는 환경
- 비동기, non-blocking service 개발에 사용하는 경우
- 효율적으로 동작하는 고성능 웹어플리케이션 개발에 사용
- 서비스간 호출이 많은 MSA에 적합
- NoSQL을 사용하는 환경
- WebFlux로 개발하고 DB가 blocking이라면 WebFlux를 쓸 필요가 없음
- Reactive를 지원하는 DB를 사용해야 하는데, 일반적으로 RDBMS는 지원하지 않고 Redis, MongoDB 등을 지원
(4) WebFlux가 무조건 좋은가?
- 프로젝트의 비즈니스 로직이 모두 비동기 + 논블로킹으로 동작한다면 빠를 수 있음
- 그러나 현실적으로 이런 프로젝트를 구성하기는 쉽지 않음
- MVC는 못해도 본전이지만, Reactive는 못하면 MVC를 적용하느니만 못한 결과를 가져올 수 있음
- 트래픽이 높은 서비스가 아니라면 MVC로도 충분히 감당 가능
(5) Spring MVC vs WebFlux
- SpringMVC : 하나의 요청에 하나의 스레드 사용. 따라서 다량의 요청에 대비해 미리 스레드 풀을 생성
- 1 Request : 1 Thread
- 동기 + 블로킹
- WebFlux : 논블로킹 + 고정된 스레드 수 만으로 요청을 처리하여 Spring MVC의 문제점을 해결
- Many Requests : 1 Thread
- 비동기 + 논블로킹
- 서버는 스레드 1개로 운영, 디폴트로 CPU 코어 수 개수의 스레드를 가진 워커 풀을 생성하여 해당 풀 내의 스레드로 모든 요청을 처리
- 단, 논블로킹으로 동작해야만 하며, 블로킹 라이브러리가 필수적으로 사용되어야한다면 워커 프로세스가 아닌 별도의 스레드로 요청해야 함
- 요청을 처리하는 EventLoop가 절대로 블로킹되지 않아야 하기 때문
(6) Mono vs Flux
Spring Webflux
에서 사용하는 reactive library
가 Reactor
이고, Reactor
가 Reactive Strems
의 구현체이다.
Flux와 Mono는 Reactor
객체 이며, 차이점은 발행하는 데이터 개수이다.
- Mono : 0 ~ 1개의 데이터 전달
- Flux : 0 ~ N개의 데이터 전달
보통 여러 스트림을 하나의 결과로 모아줄 때 Mono를, 각각의 Mono를 합쳐서 한 번에 여러개의 값을 전달할 때는 Flux를 사용한다.
그런데 Flux도 0 ~ 1개의 데이터를 전달하는게 가능한데, 굳이 한 개 까지만 데이터를 처리할 수 있는 Mono 타입이 왜 필요한걸까?
이는 데이터 설계시, 결과가 없거나 하나의 결과값만 받는 것이 명백할 경우 굳이 List나 배열을 사용하지 않는 것 처럼, Multi Result가 아닌 하나의 결과값만 받게 될 경우 불필요하게 Flux를 사용하지 않고 Mono를 사용하면 되기 때문이다.