프레임워크(Framework)/Spring

[Spring] 스프링 부트 3.2 RestClient 살펴보기

잇트루 2024. 5. 30. 00:05
반응형

RestClient

RestClient는 Spring framework 6.1(Spring boot 3.2)에 새로 추가된 동기식 HTTP Client로 Spring 애플리케이션에서 REST API 호출을 위한 HTTP 요청을 보낼 수 있다.

RestClient의 등장으로 같은 동기식 HTTP Client인 RestTemplate을 대체하여 사용할 수 있으며, fluent API를 제공하여 현대적인 방식으로 코드를 작성할 수 있게 되었다.

 

RestClient의 등장 이유

기존의 RestTemplate는 2009년 Spring 3.0에 추가된 상당히 오래된 동기식 HTTP Client이다. 그로 인해 몇 가지 단점들이 존재한다.

  • RestTemplate는 수많은 메서드가 오버로딩되어 제공하기 때문에 기능을 사용하는데 혼란을 줄 수 있다.
  • 고전적인 방식인 Template method 패턴을 활용한 클래스로 현대적인 방식과는 거리가 멀다.
  • Non-Blocking 환경에서는 적절하지 않다.

이후 등장한 WebClient는 동기식 처리와 비동기식 처리 모두 지원하며, fluent API를 제공한다. 하지만 Spring MVC 환경에서 사용하기 위해 WebFlux를 추가로 의존해야 한다는 단점이 있다.

따라서, Spring MVC 환경에서 현대적인 방식으로 HTTP Client 사용하기 위해 등장했다.

 

 

RestClienet 사용하기

RestClient 생성

RestTemplate와는 달리 static 메서드를 통해 new 생성자를 사용하지 않고 객체를 생성할 수 있다. create() 메서드를 통해 생성하거나 builder를 통해 여러 default 설정을 할 수 있다.

// create()
RestClient restClient1 = RestClient.create();

// builder()
RestClient restClient2 = RestClient.builder()
        .baseUrl("http://localhost:8080")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
        .build();

 

 

GET 요청

http://localhost:8080/{key1}/{key2}와 같이 path variable 경로에 요청을 보낸다고 가정하면 다음과 같이 요청을 보낼 수 있다.

String key1 = "key1";
Long key2 = 1L;
ResponseEntity<Map> response = restClient.get()
        .uri("http://localhost:8080/{key1}/{key2}", key1, key2)
        .retrieve()
        .toEntity(Map.class);

 

query parameter에 값을 담아 요청할 경우에는 uriBuilder를 사용하여 쉽게 작성할 수 있다.

String name = "홍길동";
int age = 20;
ResponseEntity<Map> response = restClient.get()
        .uri(uriBuilder -> uriBuilder.path("/example")
                .queryParam("name", str)
                .queryParam("age", age)
                .build())
        .retrieve()
        .toEntity(Map.class);

 

path variable과 query parameter를 동시에 사용하면 다음과 같이 작성하여 요청할 수 있다.

Long id = 1L;
String name = "홍길동";
int age = 20;
ResponseEntity<Map> response = restClient.get()
        .uri(uriBuilder -> uriBuilder.path("/example/{id}")
                .queryParam("name", str)
                .queryParam("age", age)
                .build(id))
        .retrieve()
        .toEntity(Map.class);

 

ResponseEntity 형태로 받고 싶지 않을 경우에는 body를 통해 객체 형태로 응답받을 수 있다.

Long id = 1L;
String name = "홍길동";
int age = 20;
Map response = restClient.get()
        .uri(uriBuilder -> uriBuilder.path("/example/{id}")
                .queryParam("name", str)
                .queryParam("age", age)
                .build(id))
        .retrieve()
        .body(Map.class);

 

POST 요청

이름과 나이를 가진 객체를 body에 담아 post 요청을 한다고 가정하면 다음과 같이 작성할 수 있다.

Person person = new Person("홍길동", 20);
Map response = restClient.post()
        .uri("http://localhost:8080/example")
        .contentType(MediaType.APPLICATION_JSON)
        .body(person)
        .retrieve()
        .body(Map.class);

 

post 요청도 마찬가지로 ResponseEntity 형태로 응답받기 위해서는 다음과 같이 작성할 수 있다.

Person person = new Person("홍길동", 20);
ResponseEntity<Map> response = restClient.post()
        .uri("http://localhost:8080/example")
        .contentType(MediaType.APPLICATION_JSON)
        .body(person)
        .retrieve()
        .toEntity(Map.class);

System.out.println("body = " + response.getBody());
System.out.println("StatusCode = " + response.getStatusCode());
System.out.println("Headers = " + response.getHeaders());

 

PUT

PUT 요청은 다음과 같이 작성할 수 있다.

Long id = 1L;
Person person = new Person("홍길동", 20);
Map response = restClient.put()
        .uri("http://localhost:8080/example/{id}", id)
        .contentType(MediaType.APPLICATION_JSON)
        .body(person)
        .retrieve()
        .body(Map.class);

 

DELETE

DELETE 요청 다음과 같이 작성할 수 있다.

Long id = 1L;
ResponseEntity<Void> bodilessEntity = restClient.delete()
        .uri("http://localhost:8080/example/{id}", id)
        .retrieve()
        .toBodilessEntity();

toBodilessEntity()는 responseBody는 없으나, Http StatusCode와 Headers를 가진 ResponseEntity다.

 

예외처리

RestClient의 예외처리 방법은 크게 2가지다.

  • builder()를 통해 RestClient를 생성할 때 defaultStatusHandler()를 통한 예외처리
  • 요청 후 onStatus()를 통해 응답의 상태코드에 따른 예외처리
RestClient restClient = RestClient.builder()
        .baseUrl("http://localhost:8080")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
        .defaultStatusHandler(HttpStatusCode::is4xxClientError, (req, res) -> {
            throw new RestClientException("Client error: " + res.getStatusCode());
        })
        .build();

ResponseEntity<Map> response = restClient.get()
        .uri(uriBuilder -> uriBuilder.path("/example/{id}")
                .queryParam("str", str)
                .build(id))
        .retrieve()
        .onStatus(HttpStatusCode::is5xxServerError, (req, res) -> {
            throw new RestClientException("Server error: " + res.getStatusCode());
        })
        .toEntity(Map.class);

 

반응형