CS 정리

Spring WebFlux: 왜, 언제, 어떻게?

문쿼리 2025. 6. 29. 16:11

Spring WebFlux비동기(Async), 논블로킹(Non-blocking) 기반의 웹 프레임워크로, Spring 5부터 도입

동시성이 높은 환경에서 적은 리소스로 많은 요청을 처리

 

왜? MVC와 WebFlux

항목  Spring MVC Spring WebFlux
처리 방식 동기, 블로킹 비동기, 논블로킹
스레드 모델 요청당 스레드 1개 이벤트 루프 기반 (Netty 등)
데이터 흐름 객체 기반 리액티브 스트림 (Mono, Flux)
적합한 상황 CPU 중심, 트래픽 적음 I/O 중심, 동시성 많음

 

언제? 도입 배경

 

  • 외부 API 여러 개를 병렬로 호출할 때
  • 실시간 데이터 처리 (SSE, WebSocket 등)
  • 대용량 파일 처리나 스트리밍이 필요할 때
  • 느린 외부 시스템과의 연동이 많은 서비스

 

 

어떻게? MVC → WebFlux 전환 전략

dto -> Mono<dto> or Flus<dto>
RestTemplate -> WebClient
JPA -> R2DBC
MockMvc → WebTestClient

 

1. 전환 범위 정하기

2. 반환 타입 변경

// MVC
@GetMapping("/user")
public User getUser() { ... }

// WebFlux
@GetMapping("/user")
public Mono<User> getUser() { ... }

 

3. RestTemplate → WebClient 교체

WebClient.create()
    .get().uri("/api")
    .retrieve()
    .bodyToMono(String.class);

 

4. DB 접근 방식 고려 (JPA → R2DBC 등)

@Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
    Mono<User> findByEmail(String email);
}

 

5. 테스트 도구도 변경


도입!

대시보드 구조 변경

기존 방식 (프론트 병렬 호출)

프론트에서 여러 api를 병렬 호출함,

요청 수가 많고, 응답 순서에 따라 렌더링 지연 발생

 

WebFlux 도입 후 (백엔드 병렬 조립)

백엔드에서 여러 api를 병렬 호출하여 통합 응답

@GetMapping("/dashboard")
public Mono<DashboardDto> getDashboard() {
    Mono<User> user = webClient.get().uri("/user-info").retrieve().bodyToMono(User.class);
    Mono<List<Order>> orders = webClient.get().uri("/orders").retrieve().bodyToFlux(Order.class).collectList();
    Mono<List<Product>> products = webClient.get().uri("/products").retrieve().bodyToFlux(Product.class).collectList();

    return Mono.zip(user, orders, products)
        .map(tuple -> new DashboardDto(tuple.getT1(), tuple.getT2(), tuple.getT3()));
}

 

변경 후 장점

 

  • 네트워크 요청 수 감소
  • 응답 속도 증가 (가장 느린 API만 기다리면 됨)
  • 구조 통제 및 화면 일관성 증가