본문 바로가기
Spring

RestTemplate은 너무 낡았고, WebClient는 너무 귀찮아. 뭐? RestClient?

by irerin07 2024. 2. 21.
728x90

스프링 6.1 버전, 그리고 스프링 부트 3.2.0-M1 버전부터 새롭게 RestClient가 등장했습니다.

이미 스프링에는 RestTemplate과 WebClient가 있는데 왜 굳이 새로운 Http Client를 추가시킨 걸까요?

RestClient는 무엇이고, 기존 RestTemplate나 WebClient와는 무엇이 다른 걸까요?

 

RestTemplate

RestClient를 알아보기 전에 우선 RestTemplate부터 간단하게 알아보도록 합시다.

RestTemplate의 첫 등장은 2009년, 스프링 3.0 버전과 함께였습니다. 

첫 등장 당시에는 그렇게 크지 않은 크기의 클래스였습니다. 여러 오버로드 메서드들을 사용해서 다른 서비스의 API를 호출할 수 있는 여러 방법을 제공했습니다.

하지만 시간이 지남에 따라 요구사항이 늘어나면서 점점 몸집이 불어나게 되었습니다. 결국 수많은 추가 메서드들과 수많은 오버로드 메서드들이 생겨나면서 헷갈리고, 사용하기가 좀 복잡해지게 되었습니다.

으악 이게 다 뭐야!

 

 

AsyncRestTemplate

AsyncRestTemplate은 스프링 4 버전에서 처음 등장하게 되었습니다. 기존 Blocking 방식으로 동작하던 RestTemplate을 보완하고자 등장했죠.

RestTemplate를 사용해 HTTP 요청을 보내면 해당 요청에 대한 응답이 올 때까지 클라이언트 스레드는 블로킹됩니다. 그렇게 되면 다른 작업을 수행할 수 없게 되고, 이는 대규모 시스템에서는 굉장히 비효율적인 방식이었죠. 

 

이를 해결하고자 등장한 AsyncRestTemplate.

 

그러나...

있었는데요
없었습니다.

AsyncRestTemplate은 스프링 5 버전에서 조용히 퇴장하게 됩니다. 왜죠? 분명히 RestTemplate의 단점을 보완해서 나왔는데 사라지다니...

 

그 이유는 바로 WebClient의 등장 때문이죠.

WebClient

스프링 5 에는 리액티브 웹 개발을 위한 모듈인 Spring WebFlux라는 새로운 웹 프레임워크가 등장하는데, 이를 지원하기 위한 HTTP client가 바로 WebClient입니다. 즉, WebFlux는 웹 서버를 개발하기 위한 모듈이라면, WebClient는 웹 서버와 통신하기 위한 클라이언트 모듈입니다.

 

WebClient는 싱글 스레드를 사용하며 비동기 방식으로 동작하기 때문에 요청을 보내고 응답을 기다릴 필요가 없어지고, 리액티브 스트림을 처리할 수 있습니다.

 

무엇보다 RestTemplate과 같은 방식이 아닌 Fluent API 방식을 사용하여 사용성을 대폭 증가시켰습니다.

쉽다! 편하다! 즐겁다?

 

하지만 이 녀석도 단점이 있었으니..

 

바로 WebClient를 사용하기 위해 의존성을 추가해야 한다는 것입니다.

사실 의존성 추가 자체는 어려운 일이 아니지만 이 기능 하자 사용하자고 굳이 굳이 의존성을 추가하자니 좀 귀찮긴 하죠.

 

거기에 WebClient는 WebFlux에 소속된 녀석이다 보니 사용 방법이 기존 RestTemplate과는 너무 달라서 의도치 않은 불편함을 초래하기도 합니다. 우리가 자주 사용하는 MVC는 전통적인 요청-응답 모델을 따르기 때문에 WebClient의 리액티브 한 처리 방식과는 조금 맞지 않을 수 있기 때문입니다.

HTTP Interface

스프링이 6.0으로 업데이트되면서 또 새로운 HTTP Interface라는 것을 세상에 내놓습니다. 

RestTemplate나 WebClient와는 조금 다른데, 실제로 이 녀석을 사용하기보다는 Java의 인터페이스를 이용해서 선언적으로 HTTP 서비스를 정의하는 용도입니다.

스프링의 HTTP Interface는 HTTP 요청을 위한 서비스를 자바 인터페이스와 어노테이션으로 정의할 수 있도록 도와줍니다. 그리고 해당 서비스를 구현하는 프록시 객체를 생성하면 이를 통해 손쉽게 HTTP 요청을 보낼 수 있게 되는 것이죠.

RestClient

스프링 6.1로 업데이트되면서 나온 RestClient입니다.

방금 설명드린 HTTP Interface를 기반으로 만들어졌으며 여러 특징을 가지고 있습니다.

  • 동기식 HTTP Client
  • WebClient와 같은 Fluent API방식
  • RestTemplate와 같은 인프라스트럭처(동일한 기술 기반)

간단히 설명하자면 RestTemplate이 너무 많은 오버로드 메서드와 HTTP 기능을 노출하는 것에 대한 부담이 컸고, 이를 보안하기 위해 WebClient의 Fluent API 방식을 채용한 것입니다.

 

즉 우리는 RestClient를 사용해 WebClient처럼 Fluent  API 방식으로 RestTemplate과 같은 기능을 사용할 수 있게 되는 것입니다.

서블릿 버전의 WebClient라고 생각할 수 있을 것 같습니다.

거기에 RestClient 역시 HTTP Interface를 지원하기 때문에 서블릿 기반 프로젝트에서 굳이 WebFlux 의존성을 추가할 필요도 없어지는 것이죠.

 

RestClient 생성

public void setRestTemplate(Integer number) {
    RestClient restClient = RestClient.create();
    RestClient restClient2 = RestClient.create("baseUrl");
    RestClient restClient3 = RestClient.builder().build();
    RestClient restClient4 = RestClient.create(restTemplate);
}

RestClient는 위와 같은 방식으로 생성할 수 있습니다.

create() 메서드에 restTemplate을 파라미터로 넘겨주면 기존 RestTemplate의 구성으로 RestClient를 초기화할 수 있습니다.

 

RestClient로 GET요청

단순 Get 요청은 다음과 같이 작성할 수 있습니다.

public void get() {
    RestClient restClient = RestClient.create();

    String result = restClient.get()
            .uri("https://example.com")
            .retrieve()
            .body(String.class);
    System.out.println(result);
}

 

만약 응답 상태 코드와 헤더를 가져오고자 하는 경우, toEntity를 사용하여 응답 엔티티를 가져올 수 있습니다:

public void get() {
    RestClient restClient = RestClient.create();

    ResponseEntity<String> result = restClient.get()
            .uri("https://example.com")
            .retrieve()
            .toEntity(String.class);

    System.out.println("Response status: " + result.getStatusCode());
    System.out.println("Response headers: " + result.getHeaders());
    System.out.println("Contents: " + result.getBody());
}

 

RestClient는 내부적으로 Jackson을 사용하여 JSON을 객체로 변환할 수도 있습니다.

실제로 동일한 message converter를 사용하기 때문에 RestTemplate이 지원하는 모든 타입을 변환할 수 있습니다.

uri 변수의 사용법과 Accept 헤더가 JSON으로 설정되어 있다는 점에 유의하세요.

public void get() {
    RestClient restClient = RestClient.create();

    int id = ...
    Pet pet = restClient.get()
            .uri("https://petclinic.example.com/pets/{id}", id)
            .accept(APPLICATION_JSON)
            .retrieve()
            .body(Pet.class);
}

 

RestClient로 POST요청

POST 요청 역시 간단하게 작성할 수 있습니다.

public void set() {
    RestClient restClient = RestClient.create();

    Pet pet = ...
    ResponseEntity<Void> response = restClient.post()
            .uri("https://petclinic.example.com/pets/new")
            .contentType(APPLICATION_JSON)
            .body(pet)
            .retrieve()
            .toBodilessEntity();
}

 

RestClient Error Handling

public void set() {
    RestClient restClient = RestClient.create();

    String result = restClient.get()
            .uri("https://example.com/this-url-does-not-exist")
            .retrieve()
            .onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
                throw new MyCustomRuntimeException();
            })
            .body(String.class);
}

에러 핸들링 역시 간단하게 할 수 있습니다.

기본적으로 RestClient는 4xx이나 5xx대 상태 코드를 받으면 RestClientException의 하위 예외클래스를 던집니다.

하지만 이는 status handler를 사용해 위 코드처럼 MyCustomRuntimeException()등의 커스텀한 예외등으로 오버라이드 할 수 있습니다.

 

RestClient Exchange

public void get() {
    RestClient restClient = RestClient.create();

    Pet result = restClient.get()
            .uri("https://petclinic.example.com/pets/{id}", id)
            .accept(APPLICATION_JSON)
            .exchange((request, response) -> {
                if (response.getStatusCode().is4xxClientError()) {
                    throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
                } else {
                    Pet pet = convertResponse(response);
                    return pet;
                }
            });
}

RestClient의  exchange 메서드는 기본 HTTP 요청 및 응답에 대한 액세스를 제공합니다.

다만, exchange 메서드를 사용하면 Error Handling에서 언급한 Status Handler는 적용되지 않는데, 이는 exchange 메서드가 전체 response에 대한 접근을 지원하고, 이를 이용해 필요한 오류 처리를 할 수 있기 때문입니다.

 

 

마무리

이렇게 해서 간단하게 RestClient가 무엇인지, 그리고 어떻게 사용할 수 있는지 알아보았습니다.

확실히 RestTemplate보다 좀 더 편하게 코드를 작성할 수 있는 느낌입니다.

아직 나온 지 얼마 되지 않아서 조금 더 사용해 봐야 확실히 알 수 있겠지만 저는 확실히 RestTemplate보다는 앞으로 더 자주 사용할 것 같아 보이네요.

 

긴 글 읽어주셔서 감사합니다.

 

출처 : 

https://spring.io/blog/2023/07/13/new-in-spring-6-1-restclient

https://mangkyu.tistory.com/303

https://velog.io/@doxxx93/is-resttemplate-deprecated

https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-restclient

https://www.youtube.com/watch?v=Kv6JEn5HL-M

 

728x90