Spring 의 RestTemplate
Spring 의 REST Client 인 RestTemplate
에 대해 알아보자
RestTemplate 을 사용하여 HTTP request 가 가능하며,
GET, POST, PUT, DELETE, HEAD 등의 method 를 사용할 수 있다.
본 글에서는 이 중에서도 GET, POST, HEAD 메소드의 사용 예를 알아본다.
또, RestTemplate 에서 Exception Handling 을 하는 방식에 대해서도 알아본다.
기본적인 사용법
GET 메소드
JSON 받아오기
getForEntity 메소드를 사용하여 JSON을 받아올 수 있다.
RestTemplate restTemplate = new RestTemplate(); |
JsonNode 클래스는 Jackson 이 제공하는 JSON 노드 구조 클래스다.
JSON 대신 POJO 받아오기
Response 를 곧바로 DTO 에 매핑할 수 있다.
public class Foo implements Serializable { |
이런 POJO가 있을 때 getForObject 메소드를 쓸 수 있다.
Foo foo = restTemplate |
HEAD 메소드
headForHeaders 메소드로 간단하게 헤더만 가져올 수 있다.
HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl); |
POST 메소드
postForLocation(), postForObject() 또는 postForEntity() 를 사용할 수 있다.
postForLocation() 는 생성된 리소스의 URI 를 반환하는 반면,
postForObject() 와 postForEntity() 는 리소스 자체를 반환한다.
postForObject()
다음과 같이 사용한다.
RestTemplate restTemplate = new RestTemplate(); |
postForLocation()
다음과 같이 사용한다.
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar")); |
exchange()
좀 더 범용적인 exchange() 메소드를 사용할 수도 있다.
RestTemplate restTemplate = new RestTemplate(); |
폼 데이터 전송하기
서버가 ‘&’로 연결된 여러개의 ‘키=밸류’ 쌍을 받을 수 있도록 ‘Content-Type’ 을 application/x-www-form-urlencoded 로 변경해줘야한다.
HttpHeaders headers = new HttpHeaders(); |
그리고 LinkedMultiValueMap 으로 폼 데이터를 감싼다.
MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); |
다음은, HttpEntity 로 request를 빌드한다.
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); |
마지막으로 postForEntity() 를 호출한다.
ResponseEntity<String> response = restTemplate.postForEntity( |
Exception Handling
RestTemplate 은 내부적으로 errorHandler 라는 필드를 가지고 있다.
이 errorHandler 라는 녀석이 바로 HTTP 에러가 발생했을 때, 이를 처리하는 로직이
들어있는 객체이며, DefaultResponseErrorHandler 라는 클래스의 인스턴스로 초기화 되어있다.
DefaultResponseErrorHandler 는 기본적으로 HTTP 에러가 발생했을 때
다음 3가지 중 하나의 Exception 을 던진다.
- HttpClientErrorException - 4xx HTTP 상태 코드가 응답 됐을 때
- HttpServerErrorException - 5xx HTTP 상태 코드강 응답 됐을 때
- UnknownHttpStatusCodeException - 알 수 없는 HTTP 상태 코드가 응답 됐을 때
위의 예외들은 모두 RestClientResponseException의 서브 클래스다.
물론 가장 단순한 방법은 try/catch 문으로 모든 HTTP 메소드 호출부를 감싸는 것이다.
하지만 이 방법은 API 의 종류와 호출부가 많아지면 코드의 품질이 좋지 않아질 것이다.
다른 좋은 방법은 없을까? 다행히도 스프링이 좋은 방법을 제공한다.
ResponseErrorHandler 인터페이스
ResponseErrorHandler 를 구현하는 클래스를 작성하여
RestTemplate 의 setErrorHandler() 로 앞서 작성한 에러 핸들러의 인스턴스를 세팅해줄 수 있다.
일반적으로 에러 핸들러에서는 다음 둘 중 한가지 동작을 수행하게 된다.
- HTTP 응답에 따라서 우리의 앱에 맞는, 의미있는 Exception 을 던져준다.
- Exception 을 던지지 않고 HTTP 응답을 무시한 채 프로그램이 계속 실행 되도록 한다.
참고로, ResponseErrorHandler 인터페이스를 구현하는 대신
이미 ResponseErrorHandler 를 구현하고 있는 DefaultResponseErrorHandler 클래스를 상속받아도 된다.
이러면 이미 DefaultResponseErrorHandler 에서 hasError() 메소드가 4xx/5xx 상태 코드를 받는 경우
true 를 반환하도록 구현되어 있기 때문에 직접 구현해줄 필요가 없어서 편하다.
다음의 예제 코드를 보자.
public class MyErrorHandler implements ResponseErrorHandler { |
이렇게 작성한 에러 핸들러를 다음과 같이 RestTemplate 에 셋팅해 줄 수 있다.
|
TestRestTemplate
TestRestTemplate 은 RestTemplate 의 유용한 대체제로서 Integration Test 시 유용하다.
내장 서버와 함께 @SpringBootTest 어노테이션을 사용중이라면, Test 클래스 내에서 @Autowired 되어 곧바로 사용할 수 있다.
커스터마이징이 필요하다면 RestTemplateBuilder @Bean 을 사용하면 된다.
RestTemplate과 다른점은, TestRestTemplate 은 RestTemplate 을 내장(composition)하며
다음과 같은 추가 기능을 제공한다는 것이다.
Authentication
- 템플릿 생성 시 입력
생성자 인자로 credentials 를 줄 수 있다.
TestRestTemplate testRestTemplate |
- 템플릿 생성 후 입력
이미 템플릿을 생성한 후에도 가능하다.
TestRestTemplate testRestTemplate = new TestRestTemplate(); |
HttpClientOption
TestRestTemplate 내 ENUM 형태로 존재하는 HttpClientOption 을 통해 내부의 HTTP Client 를 커스터마이징할 수 있다.
TestRestTemplate testRestTemplate = new TestRestTemplate("user", |
만약 인증이 필요하지 않다면 단순히 이렇게 할 수도 있다.
TestRestTemplate(TestRestTemplate.HttpClientOption.ENABLE_COOKIES) |
RestTemplate 의 Wrapper
몇가지 생성자와 메소드를 통한 추가적인 기능 뿐만 아니라, TestRestTemplate 은 RestTemplate 의 Wrapper 의 역할도 할 수 있다.
이는 레거시 코드로 인해 꼭 RestTemplate 을 써야만 하는 경우에 유용하다.
WebClient vs RestTemplate
WebClient 는 Spring5 에서 새롭게 등장한 클래스다.
Spring3 부터 지원되는 RestTemplate 가 블로킹 방식으로만 동작하는 반면,
WebClient 는 블로킹 뿐만 아니라 논블로킹으로도 동작하여 RestTemplate 의 대체제로 사용되고있다.
RestTemplate
RestTemplate 은 블로킹 방식이며, 내부적으로 Java Servlet API 를 사용하여 하나의 request 당 하나의 스레드를 점유하기 때문에
요청에 대한 응답이 늦어지는 경우 불필요하게 CPU 와 메모리 자원이 낭비될 수가 있다.
또한, 잦은 컨텍스트 스위칭으로 성능에도 좋지않은 영향을 미칠 수도 있고
스레드 풀의 스레드가 고갈될 수도 있다.
WebClient
반면, WebClient 는 논블로킹 방식이기 때문에, 응답이 늦어지더라도 큰 문제가 없다.
WebClient 는 Spring WebFlux 라이브러리에 포함 되어있다.
내부적으로, WebClient 가 만든 task 를 Reactive framework 가 큐에 넣고, 응답이 왔을 때만 꺼내서 실행한다.
Reactive framework 는 Java 9의 Reactive Streams API를 통해 비동기 로직을 조립할 수 있게 해준다.
참고1: https://www.baeldung.com/rest-template
참고2: https://www.baeldung.com/spring-webclient-resttemplate
참고3: https://www.baeldung.com/spring-rest-template-error-handling
참고4: https://www.baeldung.com/spring-boot-testresttemplate