본문 바로가기
개발

Spring RestClient 사용하기

by 상5c 2024. 11. 10.

RestClient는 동기식 HTTP Client이다. Spring 6.1, Spring Boot 3.2 버전에서 추가되었다.

생성하기

생성은 RestClient.create() 혹은 RestClient.builder().build()를 호출하여 생성할 수 있다.

RestClient defaultClient = RestClient.create(); // RestClient.builder().build()와 같다.

좀 더 세밀한 제어를 하고싶다면, 메서드 체이닝을 통해 설정 값들을 추가할 수 있다.

RestClient customClient = RestClient.builder()
  .requestFactory(new HttpComponentsClientHttpRequestFactory())
  .messageConverters(converters -> converters.add(new MyCustomMessageConverter()))
  .baseUrl("https://example.com")
  .defaultUriVariables(Map.of("variable", "foo"))
  .defaultHeader("My-Header", "Foo")
  .requestInterceptor(myCustomInterceptor)
  .requestInitializer(myCustomInitializer)
  .build();
  • requestFactory - timeout 설정 제어
  • messageConverters - 메시지 컨버터
  • baseUrl - 요청 보낼 base 주소. restClient를 사용하여 요청 전송시 prefix로 붙게 된다.
  • defaultUriVariables - 주소 내 path variable이 존재하는 경우 기본 값을 설정할 수 있다.
  • defaultHeader - 인증 토큰같은 기본적으로 필요한 헤더를 세팅한다.

timeout 추가하기

다른 서비스와 통신할 때 타임아웃 설정은 필수이다. RestClient 생성 시점에 SimpleClientHttpRequestFactory 를 통해 타임아웃 설정을 전달한다.

SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(Duration.ofSeconds(1));
requestFactory.setReadTimeout(Duration.ofSeconds(2));

RestClient restClient = RestClient.builder()
  .requestFactory(requestFactory)
  .build();
  • connection timeout을 1초, read timeout을 2초로 설정한 requestFactory를 전달한다.

에러 핸들링

defaultStatusHandler() 메서드를 사용한다.

첫 번째 인자로 어떤 경우에 핸들러를 적용할지, 두 번째 인자로 정의한 핸들러를 전달한다.

RestClient restClient = RestClient.builder()
    .defaultStatusHandler(
            statusCode -> statusCode.is4xxClientError() || statusCode.is5xxServerError(),
            (request, response) -> {
                if (response.getStatusCode().is4xxClientError()) {
                    throw new RuntimeException("Client exception");
                }
                if (response.getStatusCode().is5xxServerError()) {
                    throw new RuntimeException("Server exception");
                }
                throw new RestClientException("Unexpected response status: " + response.getStatusCode());
            }
    )
    .build();

Spring Bean으로 등록하기

RestClient 객체는 생성되고 나면 멀티 스레드 환경에서 안전하게 사용할 수 있다.

멀티 스레드에서 안전하게 사용 가능하기에, Spring Bean으로 등록해 사용해도 된다.

timeout 설정, base url, 에러 핸들링 등을 추가한 형태는 이렇다.

@Configuration
public class JavaRestClientConfig {

    private static final int CONNECTION_TIMEOUT_SECONDS = 1;
    private static final int READ_TIMEOUT_SECONDS = 5;

    private static final Logger log = LoggerFactory.getLogger(JavaRestClientConfig.class);

    @Bean
    public RestClient javaRestClient(RestClient.Builder restClientBuilder) {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(Duration.ofSeconds(CONNECTION_TIMEOUT_SECONDS));
        requestFactory.setReadTimeout(Duration.ofSeconds(READ_TIMEOUT_SECONDS));

        return restClientBuilder
                .requestFactory(requestFactory)
                .defaultStatusHandler(
                        statusCode -> statusCode.is4xxClientError() || statusCode.is5xxServerError(),
                        (request, response) -> {
                            log.error("HTTP request failed.");
                            log.error("Request: {} {}", request.getMethod(), request.getURI());
                            log.error("Response: {} {}", response.getStatusCode(), response.getStatusText());

                            if (response.getStatusCode().is4xxClientError()) {
                                throw new RuntimeException("Client exception");
                            }
                            if (response.getStatusCode().is5xxServerError()) {
                                throw new RuntimeException("Server exception");
                            }
                            throw new RestClientException("Unexpected response status: " + response.getStatusCode());
                        }
                )
                .build();
    }
}

RestClient 사용하기

HTTP 요청 보내기

HTTP method를 선택해서 요청을 보낼 수 있다.

아래는 POST 요청 예시이다.

String response = restClient.post()
    .uri("https://sang5c.tistory.com/posts")
    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    .accept(MediaType.APPLICATION_JSON)
    .body(new Foo("tester", "25"))
    .retrieve() // retrieve 이전까지 요청에 대한 값 제어
    .body(String.class); // 응답 Body 변환

아래는 GET 요청에 대한 예시이다. 경로 변수를 두고 값을 전달할 수 있고, toEntity()를 사용하면 응답을 ResponseEntity로 받아 header나 status code에 대한 접근도 가능하다.

ResponseEntity<String> responseEntity = restClient.get()
    .uri("https://sang5c.tistory.com/posts/{id}", 100)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .toEntity(String.class);

에러 핸들링

사용 시점에서도 제어 가능하다. onStatus()를 사용한다.

String responsBody = client.get()
    .uri("https://sang5c.tistory.com/posts/{id}", 100)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
        throw new RuntimeException("Client exception. " + response.getStatusCode());
    })
    .body(String.class);

참고