스프링 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
'Spring' 카테고리의 다른 글
면접관 : 스프링부트 auto-configuration의 동작방식을 설명해주세요. - 2 (0) | 2024.02.16 |
---|---|
면접관 : 스프링부트 auto-configuration의 동작방식을 설명해주세요. - 1 (0) | 2024.02.13 |
확실히 스프링 시큐리티에 컨트리뷰트를 하고 나서 내 인생이 달라졌다. (0) | 2024.02.02 |
Bean Scope - 스프링 공식문서가 말하는 빈 스코프 (0) | 2024.01.26 |
Lazy-initialized Beans (0) | 2024.01.24 |