본문 바로가기
Baeldung번역&공부/Spring-Reactive

Spring WebClient vs RestTemplate

by ms727 2025. 3. 2.

원본 글: https://www.baeldung.com/spring-webclient-resttemplate

 

 

이 글에서는Spring5 버전에서 나온 WebClient와 RestTemplate을 비교합니다.

 

1. Blocking vs Non-Blocking Client

1.1 RestTemplate Blocking Client

RestTemplate는 Thread-per-request(요청당 스레드1개)기반의 Java Servlet API를 사용합니다. 

이 말은 Thread는 webClient가 응답을 받을때까지 block된 상태르 유지하고 있습니다. block된 상태로 CPU나 메모리를 점유하고 있기에 쓸데없는 리소스를 잡아먹는 Thread-per-request모델은 단점이 될 수 있습니다. 

한 번에 엄청 많은 요청이 들어왔을 경우 응답이 끝날때까지 Thread가 메모리 및 CPU를 점유하므로 이는 서비스 성능 저하로 이어질 수 있습니다. 왜냐하면 요청이 들어올때마다 Thread를 계속 생성할텐데 응답이 끝나기 전까지는 계속해서 남아있을거기 때문입니다.

 

1.2 WebClient Non-Blocking Client

WebClient는 비동기, non-blocking 개념을 가지고 있는 Spring Reactive 프레임워크의 일부입니다.

RestTemplate과 달리 "task"같은 이벤트를 발행하고 tasks라는 queue에 넣어서 이를 실행이 가능할때 가져다가 실행하는 event driven 방식의 개념을 가지고 있습니다.

이러한 방식을 통해서 reactive는 상대적으로 적은 Thread를 사용게 됩니다.

 

2. Comparsion Example

두 방식을 비교하기 위해서 동시에 많은 요청이 올 경우에 대한 성능 테스트를 진행해봅니다.

 

RestTemplate방식은 요청이 많아질수록 성능이 저하되지만 WebClient방식은 성능이 크게 저하되지 않음을 확인할 수 있을겁니다.

요청을 제공할 서버가 필요합니다.

의존성을 추가해줍니다.

 

implementation 'org.springframework.boot:spring-boot-starter-webflux'

 

요청을 제공할 엔드포인트 및 서버를 작성합니다.

@RestController
public class TestController {

    @GetMapping("/slow-service-tweets")
    private List<Tweet> getAllTweets() throws InterruptedException {
        Thread.sleep(2000L); // delay
        return Arrays.asList(
                new Tweet("RestTemplate rules", "@user1"),
                new Tweet("WebClient is better", "@user2"),
                new Tweet("OK, both are useful", "@user1"));
    }

}
//
public record Tweet(String message, String user) { }

//application.proeperties
server.port=8082

 

2.1 Using RestTemplate to Call a Slow Service

Thread-per-request로 이루어진 클라이언트 서버를 작성합니다.

@Slf4j
@RestController
public class RestTemplateController {

    @GetMapping("/tweet-blocking")
    public List<Tweet> getTweet() {
        log.info("Starting Blocking Controller!");
        String uri = "http://localhost:8082/slow-service-tweets";

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<List<Tweet>> response = restTemplate.exchange(uri, HttpMethod.GET, null,
                new ParameterizedTypeReference<List<Tweet>>() {
                });


        List<Tweet> result = response.getBody();
        result.forEach(tweet -> log.info(tweet.toString()));
        log.info("Exiting BLOCKING Controller!");
        return result;
    }
}

//
public record Tweet(String message, String user) { }

//application.properties
server.port=8080

 

두 서버를 기동한 뒤, 'localhost:8080/tweet-blocking'로 요청을 보내면 응답을 받을때까지 block되어 기다리는것을 확인할 수 있습니다.

 

2025-03-01T15:41:14.205+09:00  INFO 80459 --- [SpringRestTemplateCompareWebClient-MVC] [nio-8080-exec-1] c.m.s.RestTemplateController             : Starting Blocking Controller!
2025-03-01T15:41:16.298+09:00  INFO 80459 --- [SpringRestTemplateCompareWebClient-MVC] [nio-8080-exec-1] c.m.s.RestTemplateController             : Tweet[message=RestTemplate rules, user=@user1]
2025-03-01T15:41:16.298+09:00  INFO 80459 --- [SpringRestTemplateCompareWebClient-MVC] [nio-8080-exec-1] c.m.s.RestTemplateController             : Tweet[message=WebClient is better, user=@user2]
2025-03-01T15:41:16.298+09:00  INFO 80459 --- [SpringRestTemplateCompareWebClient-MVC] [nio-8080-exec-1] c.m.s.RestTemplateController             : Tweet[message=OK, both are useful, user=@user1]
2025-03-01T15:41:16.298+09:00  INFO 80459 --- [SpringRestTemplateCompareWebClient-MVC] [nio-8080-exec-1] c.m.s.RestTemplateController             : Exiting BLOCKING Controller!

 

2.2 Using WebClient to Call a Slow Service

WebClient 클라이언트 서버도 간단하게 구축해봅니다.

 

@Slf4j
@RestController
public class ReactiveTestController {


    @GetMapping(value = "/tweets-non-blocking",
            produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Tweet> getTweetsNonBlocking() {
        log.info("Starting NON-BLOCKING Controller!");
        Flux<Tweet> tweetFlux = WebClient.create()
                .get()
                .uri("http://localhost:8082/slow-service-tweets")
                .retrieve()
                .bodyToFlux(Tweet.class);

        tweetFlux.subscribe(tweet -> log.info(tweet.toString()));
        log.info("Exiting NON-BLOCKING Controller!");
        return tweetFlux;
    }
}

//
public record Tweet(String message, String user) { }
//application.properties
server.port=8080

 

WebClient는 method실행이 완료되면 Flux publisher형태를 반환합니다. 결과가 나오면 publisher는 subscriber에게 메시지를 발행합니다.

 

실제 로그를 통해서 어떤 이야기인지 확인해봅니다.

 

2025-03-01T15:55:40.113+09:00  INFO 81144 --- [SpringRestTemplateCompareWebClient-Reactive] [ctor-http-nio-3] c.m.s.ReactiveTestController             : Starting NON-BLOCKING Controller!
2025-03-01T15:55:40.216+09:00  INFO 81144 --- [SpringRestTemplateCompareWebClient-Reactive] [ctor-http-nio-3] c.m.s.ReactiveTestController             : Exiting NON-BLOCKING Controller!
2025-03-01T15:55:42.352+09:00  INFO 81144 --- [SpringRestTemplateCompareWebClient-Reactive] [ctor-http-nio-3] c.m.s.ReactiveTestController             : Tweet[message=RestTemplate rules, user=@user1]
2025-03-01T15:55:42.352+09:00  INFO 81144 --- [SpringRestTemplateCompareWebClient-Reactive] [ctor-http-nio-3] c.m.s.ReactiveTestController             : Tweet[message=WebClient is better, user=@user2]
2025-03-01T15:55:42.352+09:00  INFO 81144 --- [SpringRestTemplateCompareWebClient-Reactive] [ctor-http-nio-3] c.m.s.ReactiveTestController             : Tweet[message=OK, both are useful, user=@user1]

 

1. tweets-non-blocking API가 호출되면 Starting NON-BLOCKING Controller! 로그가 찍힘.

2. WebClientgetSlowServiceUri()를 호출하여 HTTP GET 요청을 보냄.

3. 요청을 보낸 후, WebClient는 바로 Exiting NON-BLOCKING Controller! 로그를 출력하고 컨트롤러 실행을 종료.

4. API 응답이 도착하면 Flux가 데이터를 하나씩 emit하며 tweetFlux.subscribe()를 통해 로그를 출력.

 

3. 결론

 

이 글에서는 Spring이 제공하는 두 가지 다른 방식의 web Client를 살펴보았습니다.

 

RestTemplate는 동기, blocking방식으로 요청을 진행하며, WebClient는 비동기 및 non-blocking방식으로 요청을 제공합니다. 이 방식은 응답이 온 경우 구독자(subscriber)에게 알림을 보내 바로바로처리할 수 있게끔 합니다.

RestTemplate은 현재 많이 사용되고 있는데 어떤 요구사항에서는 non-blocking방식의 webClient를 사용하는것이 더 이점이 많을때가 있습니다.


 

다만 RestTemplate은 MVC를 꼭 의존해야하고, Webclient는 web-flux를 꼭 의존해야합니다.

 

이러한 단점때문에 Spring boot 3.2.0버전부터 RestClient라는 RestTemplate의 상위호환 기능이 MVC에서 제공하고 있으니 이에 대한 학습을 병행하는것도 좋은 방법 같습니다. 

 

예제코드: https://github.com/kkminseok/baeldung-test/tree/main/Spring-reactive/SpringRestTemplateCompareWebClient

 

baeldung-test/Spring-reactive/SpringRestTemplateCompareWebClient at main · kkminseok/baeldung-test

baeldung-test. Contribute to kkminseok/baeldung-test development by creating an account on GitHub.

github.com