Spring Boot 성능 최적화 8

2025. 11. 10. 21:56·TIL

WebClient 기반 비동기 처리 최적화

외부 API 호출은 많은 서비스에서 가장 큰 지연 요인이다.
동기 RestTemplate은 요청마다 스레드를 점유하기 때문에, 트래픽이 증가하면 스레드 고갈(thread exhaustion) 문제가 발생한다.
Spring WebClient는 비동기·논블로킹 방식으로 외부 API를 처리하여 더 높은 동시성과 효율을 제공한다.
이 글에서는 WebClient의 핵심 개념과 성능 최적화 전략을 정리한다.


#RestTemplate과 WebClient 비교

RestTemplate은 동기 방식으로, 요청마다 스레드가 점유된다.
반면 WebClient는 이벤트 기반 비동기 구조로, 요청 대기 시간 동안 스레드를 반환하므로 높은 동시성을 처리할 수 있다.

RestTemplate 한계

  • 요청 동안 스레드 점유 → 동시 요청 수 제한
  • 느린 외부 API가 전체 서비스 속도를 늦춤
  • 멀티 IO 작업에 비효율적

WebClient 장점

  • 논블로킹 IO 기반
  • 스레드 점유 없음 → 높은 동시성
  • 멀티 외부 API 호출에 최적
  • Stream 처리 및 SSE(Server Sent Event)까지 지원

Virtual Thread와 WebClient를 비교하면, WebClient는 “지연 시간이 긴 외부 API 병렬 호출”에서 더 강력하다.


#WebClient 기본 구성

Spring Boot 3.x에서는 WebClient를 기본 HTTP 클라이언트로 권장한다.
커넥션 재사용, 타임아웃, 풀 설정이 성능에 큰 영향을 준다.

WebClient Bean 등록

@Bean
public WebClient webClient() {
    return WebClient.builder()
            .baseUrl("https://api.example.com")
            .build();
}

간단한 GET 요청

Mono<UserResponse> user = webClient.get()
    .uri("/user/{id}", id)
    .retrieve()
    .bodyToMono(UserResponse.class);

Mono는 단일 결과, Flux는 여러 개의 스트림 결과를 의미한다.


#성능 최적화 설정

WebClient 성능은 HTTP 커넥션 풀 설정과 타임아웃 설정에 크게 영향을 받는다.
적절한 커넥션 수는 시스템의 동시 API 호출량에 따라 달라진다.

1) 커넥션 풀 크기 조절

HttpClient httpClient = HttpClient.create()
        .secure()
        .compress(true)
        .keepAlive(true)
        .responseTimeout(Duration.ofSeconds(3))
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000);

WebClient client = WebClient.builder()
        .clientConnector(new ReactorClientHttpConnector(httpClient))
        .build();

2) 타임아웃 설정

외부 API가 느릴 경우 서비스 전체가 느려지는 것을 방지한다.

.responseTimeout(Duration.ofSeconds(2))

3) Keep-Alive 활성화

매 요청마다 TCP 커넥션을 새로 생성하면 비용이 크다.
Keep-Alive를 통해 커넥션을 재사용하면 속도가 크게 개선된다.


#멀티 외부 API 호출 최적화

WebClient는 IO 대기 중 스레드를 붙잡지 않기 때문에,
여러 외부 API를 병렬 호출할 때 성능 차이가 극적으로 나타난다.

병렬 호출 예시

Mono<A> a = webClient.get().uri("/a").retrieve().bodyToMono(A.class);
Mono<B> b = webClient.get().uri("/b").retrieve().bodyToMono(B.class);

Mono<Result> result = Mono.zip(a, b)
        .map(tuple -> new Result(tuple.getT1(), tuple.getT2()));

RestTemplate에서는 2개의 API를 직렬로 호출해 2배 시간이 걸리지만,
WebClient는 병렬 처리로 대기 시간을 크게 줄일 수 있다.


#Resilience4j와의 조합

WebClient는 비동기이기 때문에 Circuit Breaker와 Rate Limit 전략과 함께 사용할 때 효과가 크다.

Circuit Breaker 적용

Mono<User> response = webClient.get()
    .uri("/user")
    .retrieve()
    .bodyToMono(User.class)
    .transformDeferred(CircuitBreakerOperator.of(circuitBreaker));

Retry 적용

.retryWhen(Retry.backoff(3, Duration.ofMillis(200)))

외부 API가 불안정해도 WebClient + Resilience4j 조합이면 안정적인 서비스를 유지할 수 있다.


#Virtual Thread와 WebClient 비교

Java 21 Virtual Thread와 WebClient는 모두 동시성 처리 능력이 뛰어나지만, 사용 목적이 다르다.

Virtual Thread가 적합한 상황

  • DB 중심의 내부 API
  • 외부 API 호출이 적은 서비스
  • 기존 동기 코드 유지하고 싶을 때

WebClient가 적합한 상황

  • 외부 API 호출이 매우 많을 때
  • API 병렬 호출이 필요한 서비스
  • IO 대기 시간이 긴 API
  • SSE 또는 스트리밍이 필요한 케이스

즉, WebClient는 외부 IO 중심 서비스에서 가장 강력하다.


마치며

WebClient는 Spring Boot에서 외부 API 성능을 획기적으로 개선할 수 있는 핵심 기술이다.
스레드를 점유하지 않는 비동기 구조 덕분에 대규모 트래픽 환경에서도 안정적이고 빠른 응답을 제공한다.

다음 글에서는 API 보안 및 인증 구조 성능 최적화 (JWT, 세션, OAuth2)를 다룬다.
보안과 성능을 동시에 충족시키는 실전 전략을 정리해볼 예정이다.

'TIL' 카테고리의 다른 글

Spring Boot 성능 최적화 10  (0) 2025.11.12
Spring Boot 성능 최적화 9  (0) 2025.11.11
11/7  (0) 2025.11.07
11/6  (0) 2025.11.06
11/5  (0) 2025.11.05
'TIL' 카테고리의 다른 글
  • Spring Boot 성능 최적화 10
  • Spring Boot 성능 최적화 9
  • 11/7
  • 11/6
twodc
twodc
매일의 배움과 고민, 그리고 작은 성장을 담은 개발자의 성장일지입니다.
  • twodc
    The Answer is 42
    twodc
  • 전체
    오늘
    어제
    • 분류 전체보기 (122)
      • TIL (122)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
twodc
Spring Boot 성능 최적화 8
상단으로

티스토리툴바