Back-end/SpringBoot

asynchronous? non-blocking? - WebClient

backend 개발자 지망생 2025. 5. 1. 18:44

https://tecoble.techcourse.co.kr/post/2021-07-25-resttemplate-webclient/

 

RestTemplate과 WebClient

이 글은 자바에서 HTTP 요청을 써봤거나 써보려고 하는 독자를 대상을 작성하였습니다. 스프링 어플리케이션에서 HTTP 요청할 때 사용하는 방법으로 RestTemplate과 WebClient가 있다. 스프링 5.0 이전까

tecoble.techcourse.co.kr

WebClient를 적용하기 위해서 읽어보았는데, 내 안에 흔들리는 개념들을 발견했다.

 

후에 궁금할 때 볼 수 있도록 포스팅한다..

 


 

우선 WebClient란? HTTP 요청할 때 사용하는 방법 중 하나이다.

다른 것은 RestTemplate인데, WebClient가 상대적으로 성능적인 이점이 있어 신규 도입 시에는 WebClient를 도입하는 것이 좋아보인다.

 

WebClient의 특징은 다음과 같다.

- 싱글 스레드 방식 사용 -> *정정 Netty 기반 이벤트 루프를 사용하여 적은 수의 스레드로 많은 요청 처리 가능

- Non-Blocking 방식을 사용

- 비동기화 가능

 

여기서 Non-Blocking 방식과 비동기화 방식에 대한 차이점에서 헷갈렸다.

 

우선적으로 핵심 차이에 대해 알아보자.

Non-blocking 요청을 보낸 뒤, 응답이 올 때까지 스레드를 점유하지 않는다. 즉, 응답을 기다리지 않고 다른 일을 할 수 있다.
Asynchronous 요청의 결과를 콜백이나 Future/Mono 등으로 나중에 처리한다. 흐름이 순차적이지 않고, 나중에 응답이 왔을 때 작업을 이어간다.

 

- Non-Blocking은 스레드를 점유하지 않는 것에 집중하는 것이다.

따라서, 응답을 기다리는 동안에도 스레드가 자유롭게 다른 작업이 가능하다.

요청 -> 바로 Mono 객체 리턴

 

- Asynchronous 방식은 콜백을 등록해두고, 응답이 오면 실행되는 방식이다.

대표적으로는 mono/flux, completablefuture 방식이 있다. -> 여기서는 mono 방식으로...

 

WebClient는 응답이 나중에 오기 때문에, 응답이 왔을 때 실행할 "콜백 코드"를 등록한다.

webClient.get()
    .uri("/api")
    .retrieve()
    .bodyToMono(String.class)
    .subscribe(result -> {
        // 응답이 오면 이 코드가 실행됨 (콜백)
        System.out.println("서버 응답: " + result);
    });

 

여기서 구성요소들을 살펴보자

 

1. Mono

Mono는 0개 또는 1개의 데이터를 비동기로 감싸는 컨테이너다. 리스트 형태의 값이면 Flux로 받게 될 것이다.

 

2. subscribe()

Mono는 실제로 subscribe()를 호출하기 전까지는 아무일도 하지 않는다.(-> 지연 실행: Lazy Evaluation)

추가적으로 error, complete를 넣을 수 있다.

mono.subscribe(
    data -> System.out.println("성공: " + data),
    error -> System.err.println("에러: " + error),
    () -> System.out.println("완료")
);

 

3. flatMap()

flatMap()은 Mono 안의 값을 다시 Mono로 변환할 때 사용한다. -> 다시 체이닝 할 때 사용하겠지?

Mono<String> result = webClient.get()
    .uri("/api1")
    .retrieve()
    .bodyToMono(String.class)
    .flatMap(dataFromApi1 -> 
        webClient.get()
                 .uri("/api2?query=" + dataFromApi1)
                 .retrieve()
                 .bodyToMono(String.class)
    );

 

추가적으로 WebClient + @Async + CompletableFuture를 이용해서 일부 API만 비동기 호출할 수 있다.