TIL 20240622 RestClient?

2024. 6. 22. 17:02개발일지

사이드 프로젝트에서 소셜로그인을 구현하게 되었는데,,,

이에 대해 다시 공부하다가 궁금해졌다!!

 

예전에 튜터님이 restClient가 새로 나왔으니까 이거 공부? 경험? 해보는 것도 좋다고 써보라고 하셨기에 그간 써왔긴했는데,, 정확히 이에 대해 공부를 한 적이 없었기에 이번 기회에 찾아보았다.

 


우선적으로 새로 나왔다는 데,,, 왜 새롭게 나왔을까?

 

일단 스프링 프레임 워크는 RestTemplate, WebClient와 같은 HTTP 클라이언트를 지원하고 있다고 한다... 

하지만 이것들도 여러 불편함을 가지고 있다는 것이다...... 

어떤 불편함이 있을까? 

 

 

RestTemplate
RestTemplate의 경우, 2009년 Spring Framework 3.0 버전에 RestTemplate이 도입되었지만 Template과 유사한 클래스에 HTTP의 모든 기능을 노출하게 되면 너무 많은 메서드가 overloaded 된다는 문제가 있었다. 그래서 사실 Spring Framework 5에서는 RestTemplate 대신 리액티브 프로그래밍 기반의 WebClient를 사용하게 되었다고 한다.

 


WebClient
WebClient는 Spring MVC(spring-boot-starter-web)에서 기본적으로 제공하지 않기 때문에 Spring WebFlux(spring-boot-starter-webflux) 라이브러리를 별도로 추가해야 한다. 더불어 WebFlux는 리액티브 프로그래밍을 지원하고,  WebClient도 이에 맞춰 동작하도록 설계되었기 때문에, Spring MVC에서 사용하지 않는 많은 라이브러리가 함께 추가될 수 밖에 없는 상황이라고 한다.


따라서!  Spring Framework 6.1, Spring Boot 3.2.0 버전 부터는 webflux의 의존성 없이도 사용할 수 있는 새로운 동기식 HTTP 클라이언트인 RestClient가 도입되었고 이름에서 알 수 있듯이 RestClient는 RestTemplate의 인프라와 함께 WebClient의 능수능란한 API를 제공하기도 한다. 또한 RestClient를 사용하면 WebClient와 유사한 방식으로 사용 가능하며, 기존의 message converters, request factories, interceptors 및 RestTemplate의 기타 구성 요소들을 사용할 수 있다.

 

 

 

사용 예시 코드를 살펴보자면,

 

1. RestClient 객체 생성

 

java

RestClient restClient = RestClient.create();

 

kotlin

var restClient = RestClient.create()

 

 

2. GET을 사용하여 리소스를 검색

 

java

String result = restClient.get()
.uri(uriBase + "/articles")
.retrieve()
.body(String.class);

 

kotlin

var result: String = restClient.get()
    .uri(uriBase + "/articles")
    .retrieve()
    .body(String::class.java)

 

 

3. POST를 사용하여 리소스를 생성

 

java

Article article = new Article(1, "How to use RestClient");
ResponseEntity<Void> response = restClient.post()
.uri(uriBase + "/articles")
.contentType(APPLICATION_JSON)
.body(article)
.retrieve()
.toBodilessEntity();

 

 

kotlin

var article: Article = Article(1, "How to use RestClient")
var response: ResponseEntity<Void> = restClient.post()
    .uri(uriBase + "/articles")
    .contentType(APPLICATION_JSON)
    .body(article)
    .retrieve()
    .toBodilessEntity()

 

 

4. PUT을 사용하여 리소스를 업데이트

 

java

Article article = new Article(1, "How to use RestClient even better");
ResponseEntity<Void> response = restClient.put()
.uri(uriBase + "/articles/1")
.contentType(APPLICATION_JSON)
.body(article)
.retrieve()
.toBodilessEntity();

 

kotlin

var article: Article = Article(1, "How to use RestClient even better")
var response: ResponseEntity<Void> = restClient.put()
    .uri(uriBase + "/articles/1")
    .contentType(APPLICATION_JSON)
    .body(article)
    .retrieve()
    .toBodilessEntity()

 

 

5. DELETE 를 사용하여 리소스를 제거

 

java

ResponseEntity<Void> response = restClient.delete()
.uri(uriBase + "/articles/1")
.retrieve()
.toBodilessEntity();

 

kotlin

var response: ResponseEntity<Void> = restClient.delete()
    .uri(uriBase + "/articles/1")
    .retrieve()
    .toBodilessEntity()

 

 

6. Exchange를 사용한 응답 구문 분석

 

java

List<Article> article = restClient.get()
.uri(uriBase + "/articles")
.exchange((request, response) -> {
    if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) {
        throw new ArticleNotFoundException();
    } else if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
        return objectMapper.readValue(response.getBody(), new TypeReference<>() {});
    } else {
        throw new InvalidArticleResponseException();
    }
});

 

kotlin

var article: List<Article> = restClient.get()
    .uri(uriBase + "/articles")
    .exchange { request, response ->
        if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(204))) {
            throw ArticleNotFoundException()
        } else if (response.getStatusCode().isSameCodeAs(HttpStatusCode.valueOf(200))) {
            return@exchange objectMapper.readValue(response.getBody(), object : TypeReference() {})
        } else {
            throw InvalidArticleResponseException()
        }
    }

 

 

7. 오류 처리

 

java

Article article = restClient.get()
.uri(uriBase + "/articles/1234")
.retrieve()
.onStatus(status -> status.value() == 404, (request, response) -> {
    throw new ArticleNotFoundException(response)
})
.body(Article.class);

 

kotlin

val article: Article = restClient.get()
    .uri("$uriBase/articles/1234")
    .retrieve()
    .onStatus({ status -> status.value() == 404 }) { request, response ->
        throw ArticleNotFoundException(response)
    }
    .body(Article::class.java)

 

 

8. RestTemplate 구성을 사용하여  RestClient 인스턴스 생성

 

java

RestTemplate oldRestTemplate;
RestClient restClient = RestClient.create(oldRestTemplate);

 

kotlin

var oldRestTemplate: RestTemplate? = null
var restClient = RestClient.create(oldRestTemplate!!)

 

 

:) 대부분 자바로 되어있긴 했지만,,, 코틀린으로 변환해서 보기도 했당

 

 


레퍼런스 정리!!!

시간 남을때마다 틈틈이 읽어봐야겠당 ㅠㅠ

 

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2.0-M1-Release-Notes

 

Spring Boot 3.2.0 M1 Release Notes

Spring Boot. Contribute to spring-projects/spring-boot development by creating an account on GitHub.

github.com

 

https://spring.io/blog/2023/07/13/new-in-spring-6-1-restclient

 

New in Spring 6.1: RestClient

Spring Framework 6.1 M2 introduces the RestClient, a new synchronous HTTP client. As the name suggests, RestClient offers the fluent API of WebClient with the infrastructure of RestTemplate. Fourteen years ago, when RestTemplate was introduced in Spring Fr

spring.io

 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

 

RestTemplate (Spring Framework 6.1.10 API)

postForLocation Create a new resource by POSTing the given object to the URI template, and return the value of the Location header. This header typically indicates where the new resource is stored. URI Template variables are expanded using the given URI va

docs.spring.io

 

https://docs.spring.io/spring-framework/reference/integration/rest-clients.html

 

REST Clients :: Spring Framework

WebClient is a non-blocking, reactive client to perform HTTP requests. It was introduced in 5.0 and offers an alternative to the RestTemplate, with support for synchronous, asynchronous, and streaming scenarios. WebClient supports the following: Non-blocki

docs.spring.io