티스토리 뷰

728x90

Spring WebFlux 기본 개념

Spring WebFlux는 Spring 5에서 도입된 리액티브 웹 프레임워크로, 비동기 논블로킹 방식으로 HTTP 요청과 응답을 처리합니다. 이는 높은 동시성 처리를 지원하며, 특히 리액티브 스트림 기반의 데이터 흐름을 다루기에 적합합니다. WebFlux는 Reactor를 기반으로 하여 Mono와 Flux를 사용해 비동기적으로 스트림을 처리합니다.

1. Spring MVC와 WebFlux의 차이점

  • Spring MVC: 전통적인 동기, 블로킹 방식으로 각 요청은 별도의 스레드에서 처리됩니다. 요청이 많아지면 스레드 수가 증가하고, 자원 낭비가 발생할 수 있습니다.
  • Spring WebFlux: 비동기, 논블로킹 방식으로 요청을 처리합니다. 이벤트 루프와 리액티브 스트림을 활용해 요청을 처리하며, 스레드 수를 적게 사용하면서도 높은 동시성 처리를 가능하게 합니다.

2. WebFlux의 주요 구성 요소

1) Mono와 Flux

  • Mono: 0 또는 1개의 데이터를 처리하는 단일 스트림입니다. 예를 들어, 데이터베이스에서 하나의 결과를 조회할 때 사용됩니다.
  • Flux: 0개 이상의 데이터를 처리하는 멀티 스트림입니다. 여러 개의 데이터나 실시간 데이터 스트림을 처리할 때 사용됩니다.

2) WebHandler와 DispatcherHandler

  • WebHandler: WebFlux의 핵심 인터페이스로, 요청을 받아 처리하고 응답을 반환하는 역할을 합니다.
  • DispatcherHandler: 요청을 받아 적절한 WebHandler에 요청을 위임하는 역할을 합니다. Spring MVC의 DispatcherServlet과 유사한 역할을 합니다.

3) Router Function과 Handler Function

  • Router Function: 경로 기반 라우팅을 정의하는 함수형 API입니다. 요청 URL에 따라 적절한 Handler Function으로 매핑합니다.
  • Handler Function: 각 요청을 처리하는 함수로, 전통적인 Controller 클래스 대신 함수형 스타일로 요청을 처리합니다.

예제:

@Configuration
class RouterConfig {
    @Bean
    fun route(handler: MyHandler): RouterFunction<ServerResponse> {
        return RouterFunctions.route()
            .GET("/hello", handler::sayHello) // GET 요청을 sayHello로 라우팅
            .build()
    }
}

@Component
class MyHandler {
    fun sayHello(request: ServerRequest): Mono<ServerResponse> {
        return ServerResponse.ok().bodyValue("Hello, WebFlux!")
    }
}

위 예제에서는 /hello 경로로 들어오는 GET 요청을 sayHello 함수로 처리하는 간단한 예제를 보여줍니다.

4) WebClient

  • WebClient는 RestTemplate을 대체하는 WebFlux의 비동기 HTTP 클라이언트입니다. 외부 API와 비동기적으로 통신할 때 사용합니다.

예제:

val webClient = WebClient.create("http://example.com")
val responseMono = webClient.get()
    .uri("/data")
    .retrieve()
    .bodyToMono(String::class.java)

responseMono.subscribe { response -> println("Response: $response") }

위 코드는 WebClient를 사용해 외부 API에 GET 요청을 보내고, 결과를 비동기적으로 받아오는 예제입니다.

3. 애노테이션 기반의 요청 처리 방식

WebFlux는 전통적인 Spring MVC처럼 애노테이션을 사용한 요청 매핑 방식도 지원합니다.

예제:

@RestController
class MyController {

    @GetMapping("/hello")
    fun sayHello(): Mono<String> {
        return Mono.just("Hello, WebFlux with Annotation!")
    }

    @GetMapping("/items")
    fun getItems(): Flux<String> {
        return Flux.just("Item1", "Item2", "Item3")
    }
}

위 예제는 애노테이션을 사용해 MonoFlux를 반환하여 비동기 요청을 처리하는 방법을 보여줍니다.

4. 비동기 처리 흐름 제어

1) publishOn과 subscribeOn

  • publishOn: 이후의 연산을 특정 스레드에서 실행하도록 설정합니다.
  • subscribeOn: 구독 시점부터 전체 파이프라인을 특정 스레드에서 실행하도록 설정합니다.

예제:

Flux.just(1, 2, 3, 4)
    .subscribeOn(Schedulers.boundedElastic()) // 시작 시점에 새로운 스레드로 전환
    .map { it * 10 }
    .publishOn(Schedulers.parallel()) // 이후의 작업을 다른 스레드에서 실행
    .subscribe { println("Processed Data: $it") }

위 코드는 subscribeOnpublishOn을 사용해 스레드 풀을 바꿔가며 작업을 처리하는 예제입니다.

5. WebFlux의 비동기적 요청 흐름

WebFlux는 요청이 들어오면 DispatcherHandler가 이를 처리할 수 있는 Handler로 라우팅하고, Mono나 Flux를 통해 데이터가 준비될 때까지 기다립니다. 데이터가 준비되면 응답을 반환하는 구조로, 특정 스레드에 얽매이지 않기 때문에 높은 동시성을 지원할 수 있습니다.

6. WebFlux의 백프레셔

WebFlux는 데이터의 생산과 소비 속도를 조절하기 위해 Reactor의 백프레셔를 지원합니다. 이를 통해 소비자가 처리 가능한 수준으로 데이터 흐름을 조절함으로써, 시스템이 과부하되는 것을 방지할 수 있습니다.

요약

  • Mono와 Flux: WebFlux의 핵심 데이터 처리 방식으로, 단일 또는 다중 데이터 스트림을 비동기적으로 처리합니다.
  • Router와 Handler: 함수형 스타일로 경로와 요청을 처리하며, 유연한 라우팅과 처리를 제공합니다.
  • WebClient: 외부 API와의 비동기 통신을 담당하며, RestTemplate의 대체로 사용됩니다.
  • 비동기 처리: Scheduler와 publishOn, subscribeOn을 통해 비동기 스레드 제어가 가능합니다.
  • 백프레셔: 과도한 데이터 스트림을 조절하여 자원 낭비를 방지합니다.

sequenceDiagram
    actor Client
    participant DH as DispatcherHandler
    participant H as Handler
    participant MFP as Mono/Flux Pipeline
    participant DB as Database/External API

    Client ->> DH : HTTP Request
    DH ->> H : Route to Handler
    H ->> MFP : Process Request
    MFP ->> DB : Fetch Data (Non-blocking)
    DB -->> MFP : Data Response
    MFP ->> H : Process Data
    H ->> DH : Return Response
    DH ->> Client : HTTP Response
728x90
댓글