새로운 HTTP 클라이언트 RestClient
Spring Framework 6.1 M2에서 새로운 동기 HTTP 클라이언트인 RestClient
가 출시되었습니다! 그런데 그게 뭔데요? 원래 WebClient
에서도 동기로 HTTP 쓸 수 있었잖아요?
Spring MVC에서 WebClient
하나 쓰겠다며 울며 겨자 먹기로 추가하던 spring-boot-starter-webflux
를 지워버리셔도 된다는 말입니다.
따라서 이제 저희는 RestClient
를 사용해서 RestTemplate
의 기능들을 좀 더 가볍고 유연하게 사용할 수 있습니다.
💡RestClient 생성
RestClient
클래스의 static 메서드인 create()
를 이용해서 생성할 수 있습니다.
RestClient restClient = RestClient.create();
아래과 같이 기존 RestTemplate
의 구성으로 RestClient
를 초기화할 수 있습니다.
RestClient restClient = RestClient.create(restTemplate);
또한 빌더 패턴을 사용해서 기본 URL, 헤더 설정 등등 추가 옵션들을 사용할 수도 있습니다.
RestClient restClient = RestClient.builder()
.baseUrl("https://www.naver.com")
.defaultHeaders(HttpHeaders::clearContentHeaders)
.build();
💡GET & POST 요청
아래의 코드는 RestClient로 GET 요청을 하고 사이트 내용을 문자열로 검색합니다.
RestClient restClient = RestClient.create();
String result = restClient.get()
.uri("https://example.com")
.retrieve()
.body(String.class);
System.out.println(result);
HTTP의 컨텐츠 뿐만 아니라 상태 코드나 헤더를 받고 싶다면 ResponseEntity
를 쓸 수 있습니다.
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());
Jackson을 사용하여 JSON을 바로 객체로 변환할 수도 있습니다. 내부에서 RestTemplate
과 똑같은 메시지 컨버터를 사용합니다.
int id = ...
Pet pet = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.retrieve()
.body(Pet.class);
POST 요청 또한 간단하게 다음과 같이 쓸 수 있습니다.
Pet pet = ...
ResponseEntity<Void> response = restClient.post()
.uri("https://petclinic.example.com/pets/new")
.contentType(APPLICATION_JSON)
.body(pet)
.retrieve()
.toBodilessEntity();
💡에러 핸들링
기본적으로 RestClient는 4xx 또는 5xx 상태 코드를 수신할 때 RestClientException
하위 클래스를 던집니다. 이 때 다음과 같은 상태 핸들러를 사용하여 재정의할 수 있습니다.
String result = restClient.get()
.uri("https://example.com/this-url-does-not-exist")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders())
})
.body(String.class);
💡Exchange
RestClient
는 기본 HTTP 요청 및 응답에 대한 액세스를 제공하므로 보다 고급 시나리오에 대한 exchange()
를 제공합니다.
앞서 언급한 상태 핸들러는 exchange 기능이 이미 전체 응답에 대한 액세스를 제공하므로 필요한 오류 처리를 수행할 수 있으므로 exchange()
를 사용할 때 적용되지 않습니다.
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;
}
});
대체적으로 WebClient랑 사용법이 똑같다는 느낌이 듭니다. 근데 중요한 것은 저희가 쓸 데 없이 무거운 spring-boot-starter-webflux
를 추가하지 않았다는 것이죠!
현재 진행 중인 개인 토이 프로젝트의 버전을 올려서 RestTemplate
대신 좀 더 현대적인 RestClient
를 사용해야겠습니다.
🎯정리
RestTemplate
는Template
과 같은 클래스를 통해 모든 HTTP의 기능을 노출하는 것이 부담을 줄 수 있었습니다. 그래서 저희는WebClient
를 썼었습니다.WebClient
는spring-boot-starter-webflux
의존성을 추가해야 하는 부담이 있었습니다.Spring 6.1 M2
,Spring Boot 3.2.0-M1
부터RestClient
를 사용할 수 있습니다. WebFlux 의존성을 추가하지 않고도WebClient
처럼 너무 많은 메서드를 오버라이딩 하지 않으면서 동기적으로 HTTP 요청을 할 수 있습니다.
🔖참고
Subscribe to my newsletter
Read articles from Ji, Seunghyeon directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by