spring webflux란?

Spring WebFlux란?

Spring WebFlux는 비동기(Asynchronous) & 논블로킹(Non-blocking) 방식의 웹 프레임워크
Spring MVC가 동기(Blocking) 방식이라면, WebFlux는 Reactive Streams 기반으로 동작해서 고성능, 낮은 리소스 사용량이 특징


논블로킹(Non-blocking)이란?

논블로킹(Non-blocking)이란, 어떤 작업을 요청한 후 결과가 나올 때까지 기다리지 않고 바로 다음 작업을 수행할 수 있는 방식

블로킹(Blocking) vs 논블로킹(Non-blocking)

블로킹(Blocking) 논블로킹(Non-blocking)

작업 방식 요청한 작업이 끝날 때까지 기다림 기다리지 않고 바로 다음 작업 수행
스레드 점유 하나의 작업이 끝날 때까지 스레드가 점유됨 스레드가 다른 작업을 처리할 수 있음
처리 방식 동기(Synchronous) 방식과 자주 사용됨 비동기(Asynchronous) 방식과 자주 사용됨
예시 전화 통화 (한 명이 말할 때 상대방은 기다려야 함) 문자 메시지 (답장을 기다리지 않고 다른 일 가능)

예제 코드 비교

1. 블로킹 방식 (Blocking)

public String getData() {
    // 5초 동안 데이터를 가져오는 작업
    Thread.sleep(5000); 
    return "데이터 완료";
}

public void process() {
    System.out.println("데이터 요청...");
    String result = getData(); // 여기서 5초 동안 멈춤
    System.out.println(result); // 데이터가 도착한 후 출력
}

문제점: getData()가 끝날 때까지 다른 작업을 할 수 없음.


2. 논블로킹 방식 (Non-blocking)

import reactor.core.publisher.Mono;

public Mono<String> getDataAsync() {
    return Mono.fromSupplier(() -> {
        try { Thread.sleep(5000); } catch (InterruptedException e) {}
        return "데이터 완료";
    });
}

public void process() {
    System.out.println("데이터 요청...");
    getDataAsync().subscribe(result -> System.out.println(result));
    System.out.println("다른 작업 수행 가능!");
}

장점: getDataAsync()가 실행된 후 바로 다음 코드(다른 작업 수행 가능!)를 실행할 수 있음.


논블로킹의 장점

  1. 높은 성능 & 효율적인 리소스 사용 : 하나의 스레드로 여러 작업을 동시에 처리 가능 → CPU & 메모리 사용 효율적
  2. 높은 동시성 처리 : API 서버, 채팅 서버, 대규모 요청을 처리하는 서비스에 적합
  3. 응답 지연이 있는 작업에 유리 : 예: 데이터베이스 조회, 외부 API 호출, 파일 I/O 등에서 유용

논블로킹이 사용되는 곳

  • Spring WebFlux: Mono, Flux를 이용해 논블로킹 HTTP 요청 처리
  • Netty: 논블로킹 I/O 기반으로 동작하는 네트워크 서버
  • Node.js: JavaScript 기반의 논블로킹 이벤트 루프 방식 서버
  • Kafka, RabbitMQ: 메시지 큐 시스템에서 논블로킹 방식으로 이벤트 처리

정리

블로킹(Blocking) → 요청이 끝날 때까지 기다림 (ex. 전화 통화)
논블로킹(Non-blocking) → 요청 후 기다리지 않고 바로 다음 작업 수행 (ex. 문자 메시지)

🚀 논블로킹은 많은 요청을 효율적으로 처리해야 하는 시스템(웹 서버, 마이크로서비스 등)에 유용함!


스레드(Thread)란?

스레드는 하나의 프로세스 내에서 실행되는 작은 작업 단위
운영체제가 프로그램을 실행할 때, 기본적으로 프로세스(Process)를 만들고, 그 안에서 여러 스레드(Thread)가 실행될 수 있다


1. 프로세스(Process) vs 스레드(Thread)

프로세스(Process) 스레드(Thread)

정의 실행 중인 프로그램 프로세스 내에서 실행되는 작은 작업 단위
메모리 공간 독립적 같은 프로세스 내에서 공유
통신 방식 프로세스 간 통신(IPC) 필요 같은 프로세스 내에서 쉽게 데이터 공유
예제 크롬 브라우저, 메모장, 게임 크롬 브라우저의 여러 탭, 게임의 여러 캐릭터 AI

2. 스레드의 특징

같은 프로세스 내에서 실행됨 → 여러 스레드가 하나의 프로그램을 동시에 실행 가능
메모리를 공유함 → 변수, 데이터 등을 공유할 수 있음
멀티스레딩 가능 → 여러 개의 스레드를 실행해서 동시에 여러 작업을 수행 가능


3. 예제 코드 (싱글스레드 vs 멀티스레드)

1) 싱글스레드 (Single-thread)

public class SingleThreadExample {
    public static void main(String[] args) {
        System.out.println("작업 1 시작");
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
        System.out.println("작업 2 시작");
    }
}

✅ Thread.sleep(2000) 때문에 작업 1이 끝날 때까지 작업 2가 기다려야 함 (동기 실행)


2) 멀티스레드 (Multi-thread)

public class MultiThreadExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            System.out.println("작업 1 완료");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("작업 2 완료");
        });

        thread1.start();
        thread2.start();
    }
}

작업 1과 작업 2가 동시에 실행됨! (비동기 실행)


4. 스레드의 종류

  1. 싱글스레드 (Single Thread)
    • 하나의 작업을 순차적으로 실행
    • 작업이 많아지면 대기 시간이 길어짐
  2. 멀티스레드 (Multi Thread)
    • 여러 개의 스레드를 실행해서 동시에 여러 작업을 수행
    • CPU를 효율적으로 사용할 수 있음
    • 하지만 **스레드 간 동기화 문제(경쟁 상태, 데드락)**가 발생할 수 있음

5. 언제 멀티스레드를 사용할까?

CPU를 많이 사용하는 작업 (ex. 이미지 처리, AI 연산)
여러 요청을 동시에 처리해야 할 때 (ex. 웹 서버, 게임 서버)
비동기 작업을 수행할 때 (ex. 데이터베이스 조회, 파일 다운로드)


6. 정리

🚀 스레드는 프로세스 내에서 실행되는 작은 작업 단위
🚀 멀티스레드를 사용하면 여러 작업을 동시에 실행 가능
🚀 하지만 동기화 문제(데드락, 경쟁 상태)도 주의해야 함!


왜 WebFlux를 쓰나?

  1. 높은 동시성 처리
    • WebFlux는 Reactor(Project Reactor 기반)를 사용해 비동기 & 논블로킹 방식으로 동작함.
    • 적은 스레드로 더 많은 요청을 처리할 수 있음. (ex: API Gateway, 마이크로서비스)
  2. 백프레셔(Backpressure) 지원
    • 데이터를 소비하는 속도가 느릴 때, 생산자가 조절할 수 있도록 함.
    • 대량의 데이터를 스트리밍할 때 효율적임.
  3. Netty 기반 (Tomcat 필요 없음)
    • Spring MVC는 기본적으로 Tomcat을 사용하지만, WebFlux는 Netty, Undertow 같은 비동기 서버를 사용할 수 있음.

Spring MVC vs WebFlux 비교

Spring MVC Spring WebFlux

처리 방식 동기 (Blocking) 비동기 (Non-blocking)
기반 라이브러리 Servlet API Project Reactor
서버 Tomcat, Jetty 등 Netty, Undertow 등
주요 사용처 일반적인 웹 애플리케이션 API Gateway, 대용량 트래픽 서비스

Spring WebFlux 주요 개념

  1. Mono & Flux (Reactive Streams의 핵심 요소)
    • Mono<T> → 단일 데이터 (0~1개) 반환
    • Flux<T> → 여러 데이터 (0~N개) 반환 (스트리밍 가능)
  2. WebClient (RestTemplate 대체)
    • 기존의 RestTemplate은 동기 방식이라 WebFlux에서는 비동기 HTTP 요청을 보내는 WebClient를 사용함.
  3. 핵심 서버 엔진: Reactor Netty
    • Spring WebFlux의 기본 웹 서버는 Reactor Netty 기반이지만, Tomcat도 지원 가능함.

언제 WebFlux를 쓰면 좋을까?

✅ API Gateway, 마이크로서비스 아키텍처
✅ 높은 동시성이 필요한 서비스 (ex. 실시간 데이터 스트리밍, 채팅, 게임 서버)
✅ 비동기 요청을 많이 처리해야 하는 시스템 (ex. 외부 API 호출이 많은 서비스)

🚫 하지만, 단순한 CRUD 애플리케이션이라면 Spring MVC가 더 직관적이고 개발하기 쉬움.


WebFlux의 요청 처리 흐름

위에 그림은 Srping WebFlux의 클라이언트 요청 및 응답 처리 흐름을 표현한 것입니다.

1. Client가 요청을 합니다.
2. Client로부터 요청이 들어오면 Netty 등의 서버 엔진을 거쳐 HttpHandler가 들어오는 요청을 전달받습니다. 그리고 ServerWebExchange를 생성한 후 WebFilter 체인으로 전달합니다.
3. ServerWebExchange는 WebFilter 체인에서 전처리 과정을 거친 후 WebHandler 인터페이스의 구현체인 DispatcherHandler에게 전달됩니다.
4. DispatcherHandler에서는 HandlerMapping List를 원본 Flux의 소스로 전달받습니다.
5. ServerWebExchange를 처리할 핸들러를 조회합니다.
6. 조회된 핸들러의 호출을 HandlerAdapter에게 위임합니다.
7. HandlerAdapter는 ServerWebExchange를 처리할 핸들러를 호출합니다.
8. Controller 또는 HandlerFunction 형태의 핸들러에서 요청을 처리합니다.
9. 요청 처리가 완료된 데이터를 모델에 담아 return합니다.
10. return 받은 데이터를 처리할 HandlerResultHandler를 조회합니다.
11. 조회된 HandlerResultHandler가 return 받은 데이터를 적절하게 처리한 후 response를 return 합니다.

Spring mvc와 spring webFlux의 공통점과 차이점


Spring boot와 webFlux

https://adjh54.tistory.com/232

 

- 반응형 및 비동기적인 웹 애플리케이션 개발을 지원하는 모듈입니다. 이 모듈은 Reactive Streams 사양을 기반으로 하여, 비동기적인 이벤트 지향 프로그래밍을 통해 높은 확장성과 성능을 제공합니다.

- Webflux를 사용하는 목적은 반응형 프로그래밍을 통해 '높은 처리량'과 '확장성'을 갖는 애플리케이션을 만드는 것을 목표로 합니다.

 

webflux를 사용하기 위한 spring boot 버전
Webflux는 Spring Framework 5.0부터 지원한다. Spring Framework 5.0은 Spring Boot 2.0 이상을 요구하므로 Webflux를 사용하는데 Spring Boot 2.0 이상 버전이 최소 요구사항이 된다.

 

Webflux 주요 특징
Webflux는 ‘반응형 프로그래밍(Reactive Programming)’을 구현하기 위해 Reactor라는 라이브러리를 이용합니다. Reactor는 Netty 서버를 통해 비동기식 이벤트 기반의 서버를 환경을 제공하며 이를 이용하여 비동기 방식의 논 블로킹 요청을 통해 이벤트 기반의 반응형 스트림으로 데이터를 주고받습니다.

 

반응형 프로그래밍(Reactive Programming)
- Spring Webflux는 반응형 프로그래밍(Reactive Programming) 방식을 통해 ‘이벤트 기반의 비 동기식 애플리케이션’을 구축할 수 있습니다.
- 이 이벤트는 ‘비 동기적’으로 처리되며 새로운 이벤트가 발생하면 이벤트 스트림이 생성이 되며 스트림을 구독하면 이벤트를 처리할 수 있습니다.

 

명령형 프로그래밍 <-> 반응형 프로그래밍은 뭐가 다른가
- 명령형 프로그래밍의 경우는 컴퓨터가 수행해야 하는 일을 명령의 목록으로 나열을 하고 이를 순서대로 실행합니다. (Spring MVC)

- 반응형 프로그래밍의 경우는 데이터 스트림을 처리하며 이 데이터 스트림이 변경될 때마다 반응하는 것을 의미합니다. (WebFlux)

 

Reactor

- 반응형 프로그래밍(Reactive Programming)을 구현하기 위한 Reactor는 Reactive 라이브러리 중 하나입니다.
- Publisher-Subscriber 패턴을 중심으로 동작하며 데이터를 생성하고 가공하고 구독자에게 전달하는 역할을 합니다.
Reactor에서는 Mono와 Flux의 데이터 스트림 유형을 지원합니다.

 

Publisher-Subscriber 패턴: 반응형 스트림(Reactive Stream)

반응형 스트림(Reactive Stream) 수행과정

Mono

- Reactor 라이브러리에서 제공하는 Reactive Streams의 Publisher 중 하나로 오직 ‘0개 또는 하나의 데이터항목 생성’하고 이 결과가 생성되고 나면 스트림이 종료되면 결과 생성을 종료합니다.
- Mono를 사용하여 비동기적으로 결과를 반환하면 해당 결과를 구독하는 클라이언트는 결과가 생성될 때까지 블로킹하지 않고 다른 작업을 수행할 수 있습니다.

 

 [ Mono 사용 예시 ]

Mono.just("Hello, world!")
    .map(String::toUpperCase)
    .flatMap(s -> Mono.just("Mono: " + s))
    .subscribe(System.out::println);

- "Hello, world!"라는 문자열을 Mono.just를 통해 Mono로 만든 후, map 연산자를 이용해 문자열을 대문자로 변환합니다.
- 이후, flatMap 연산자를 이용해 "Mono: "이라는 문자열과 결합합니다.
- 마지막으로, 비동기적으로 처리된 결과값을 subscribe 메서드를 이용하여 출력합니다.

메소드 설명
just() 주어진 데이터를 포함하는 Mono를 생성합니다.
empty() 데이터가 없는 Mono를 생성합니다.
error() 에러 상황을 나타내는 Mono를 생성합니다.
fromCallable() Callable 객체를 이용해 Mono를 생성합니다.
fromFuture() Future 객체를 이용해 Mono를 생성합니다.
fromRunnable() Runnable 객체를 이용해 Mono를 생성합니다.

Flux

- Reactor 라이브러리에서 제공하는 Reactive Streams의 Publisher 중 하나Mono와 달리 ‘여러 개의 데이터 항목’를 생성하고 스트림이 종료되면 결과 생성을 종료합니다.
- 비동기 작업을 수행하면 작업이 완료될 때까지 블로킹하지 않고 다른 작업을 수행할 수 있습니다.
- Spring WebFlux에서 Flux를 사용하여 HTTP 요청을 처리하는 경우, 요청을 수신한 즉시 해당 요청을 처리하고 결과를 생성하는 대신 결과 생성이 완료될 때까지 다른 요청을 처리할 수 있습니다.

메소드 설명
just() 주어진 데이터를 포함하는 Flux를 생성합니다.
fromIterable() Iterable 객체를 이용해 Flux를 생성합니다.
fromArray() 배열을 이용해 Flux를 생성합니다.
fromStream() Stream을 이용해 Flux를 생성합니다.
empty() 데이터가 없는 Flux를 생성합니다.
error() 에러 상황을 나타내는 Flux를 생성합니다.
range() 지정된 범위의 정수를 포함하는 Flux를 생성합니다.
interval() 일정 시간 간격으로 값을 생성하는 Flux를 생성합니다.
merge() 여러 개의 Flux를 병합하여 하나의 Flux로 만듭니다.
concat() 여러 개의 Flux를 순차적으로 이어붙여 하나의 Flux로 만듭니다.
zip() 여러 개의 Flux를 조합하여 튜플 형태로 만듭니다.
collectList() Flux에 포함된 모든 데이터를 리스트로 수집합니다.
collectMap() Flux에 포함된 모든 데이터를 맵으로 수집합니다.

 

 [ Flux 예시 ]

Flux.just("apple", "banana", "cherry")
    .map(String::toUpperCase)
    .flatMap(s -> Mono.just("Flux: " + s))
    .subscribe(System.out::println);

- "apple", "banana", "cherry" 세 문자열을 Flux.just를 통해 Flux로 만든 후, map 연산자를 이용해 문자열을 대문자로 변환합니다.
- 이후, flatMap 연산자를 이용해 "Flux: "이라는 문자열과 결합합니다.
- 마지막으로, 비동기적으로 처리된 결과값을 subscribe 메서드를 이용하여 출력합니다.

Netty

- 자바 기반의 네트워크 애플리케이션 프레임워크로 ‘비동기식 이벤트 기반 서버’를 만드는 데 사용이 됩니다.

특성 Netty Tomcat
아키텍처 이벤트-기반 요청 당 쓰레드
성능 비동기 처리로 인한 높은 성능 동기 처리로 인한 상대적으로 느린 성능
확장성 다수의 동시 접속 처리가 가능한 높은 확장성 쓰레드 개수 제한으로 확장성 한계
프로토콜 지원 HTTP, WebSocket, TCP 등 다수의 프로토콜 지원 주로 HTTP에 특화
유연성 다양한 애플리케이션에 사용 가능한 높은 유연성 웹 애플리케이션에 특화된 제한된 유연성
사용 편의성 이벤트-기반 아키텍처로 인한 학습 곡선이 높음 전통적인 서블릿 기반 아키텍처에 익숙한 개발자에게 사용하기 편리

 

 

WebClient

- WebFlux의 일부인 Webclient는 비동기적인 방식으로 HTTP 요청을 보내고 응답을 받을 수 있는 라이브러리를 의미합니다.

- 다수의 외부 API 호출이나, 다른 서비스들과의 통합 작업에서 유용합니다.

- 이를 통해 Reactive Streams를 이용하여 높은 성능의 네트워크 통신을 구현할 수 있습니다.

WebClient webClient = WebClient.builder().build();

Mono resultMono = webClient.get()
        .uri("<https://www.example.com/>")
        .retrieve()
        .bodyToMono(String.class);

String result = resultMono.block();

System.out.println(result);

 

 

Mono와 Flux는 데이터 스트림이다. 비동기 방식의 HTTP 요청인 WebClient를 사용한다.