스프링부트

RestTemplate를 RestClient로 ?!

Choony 2024. 7. 1. 22:21

현재 프로젝트에서는 GPT와의 통신이 필수인만큼 외부 API를 꼭 사용해야 한다.

 

그렇기에 이전에도 잘 써왔던 RestTemplate를 적용해서 프로젝트를 진행하고 있었다.

전혀 아무 문제 없이 외부 API를 호출했고 원하는 대로 기능완성도 되었다.

 

평범한 일상을 보내던 중 자극적인 문장 하나를 보게 되었는데...

"RestTemplate은 곧 deprecated 된다" 이걸 보자마자 머리가 띵했다. (나 잘 쓰고 있는데 왜?)

 

그렇게 급한 마음에 좀 찾아보니 현재 WebClient라는 기술이 잘 사용되고 있고 RestTemplate은 유지보수 단계에 들어갔다고 한다.

나도 최신기술을 도입해보고자 하는 마음에 WebClient 기술을 간단하게나마 공부해 봤다.


Webclient는 Spring5.0에서 WebFlux와 함께 내놓은 인터페이스이며 특징으로는 싱글 스레드 방식을 사용하고 Non-Blocking 방식을 사용한다는 것이다. 추가로 Mono와 Flux라는 타입을 사용한다.

 

사용하기 위해서는 아래의 의존성을 추가해 주면 된다.

implementation 'org.springframework.boot:spring-boot-starter-webflux'

 

 

추가를 해주면 되는데..... 난 너무 마음에 안 들었다.

내 프로젝트의 경우 GPT, 카카오, 자체 개발한 평가서버(FastAPI) 총 3번의 외부 API 호출이 있다. (아주 간단한 호출)

 

아주 간단한 호출인데 말로만 들었던 WebFlux 산하인 Webclient를 사용해야 한다?

이 또한 부담스럽지만, 일단 RestTemplate과 사용방식이 너무 달랐다. (WebFlux를 공부하기엔 시간적으로 쫓겼다.. 졸작.....)

 

그렇게 하염없이 뭐 좋은 거 없나 찾아보다가 아주 마음에 드는 녀석을 찾게 되었다.

오늘의 주인공인 RestClient이다.


RestClient

RestClient는 Spring6.1에서 내놓은 아주 따끈따끈하다고 볼 수 있는 녀석이다.

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

 

Webclient와 마찬가지로 Fluent API를 제공해서 코드를 더 간결하고 직관적으로 만들 수 있다.

예시로 Get 코드를 봐보자.

RestClient restClient = RestClient.create();

String result = restClient.get()
  .uri("https://example.com")
  .retrieve()
  .body(String.class);
System.out.println(result);

 

아주 맘에 든다. 거기다 더 마음에 드는 건 아래 두 가지다.

  • 새로운 의존성을 추가할 필요가 없다.
  • 기존 RestTemplate의 인프라와 함께 사용이 가능하다.

👍👍👍

내가 Webclient를 쓰기 싫어하던 이유 두 가지를 바로 해결해 주었다. 

 

번외로 Webclient에서 지원하는 Non-blocking을 RestClient에서는 못할 줄 알고 찾아봤는데 역시나 당연히 되더라.. Non-blocking이 필요하더라도 RestClient를 쓰면 될 거 같다.


RestTemplate  To  RestClient

코드 예시로 GPT와 통신하는 코드를 보여주겠다!

 

[ 기존 ]

// GPTRestTempConfig.java
@Bean
@Qualifier("gptRestTemplate")
public RestTemplate gptRestTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getInterceptors().add((request, body, execution) -> {
        request.getHeaders().add("Authorization", "Bearer " + secretKey);
        return execution.execute(request, body);
    });
    return restTemplate;
}
// GPTServiceImpl.java

... 
@Qualifier("gptRestTemplate")
@Autowired
private final RestTemplate gptRestTemplate;

@Override
public String getNewQuestion(ChatRequest chatRequest) {
    ChatResponse gptResponse = gptRestTemplate.postForObject(apiUrl, chatRequest, ChatResponse.class);
    ChatResponse gptResponse = gptComponent.getGptResponse(chatRequest);
    checkValidGptResponse(gptResponse);

    return gptResponse.getChoices().get(0).getMessage().getContent();
}
...

 

기존에 사용하던 방식이다. 사용했던 사람들은 코드만 봐도 익숙할 것이다. 핵심은 postForObject!

 

이제 이를 바꿔보자.

 

[ RestClient 적용 ]

@Configuration
public class RestClientConfig {
    @Value("${openai.url}")
    private String gptUrl;

    @Value("${openai.secret-key}")
    private String gptSecret;

    @Bean
    public GPTComponent gptComponent() {
        RestClient restClient = RestClient.builder()
                .baseUrl(gptUrl)
                .defaultHeader("Authorization", "Bearer " + gptSecret)
                .build();

        RestClientAdapter adapter = RestClientAdapter.create(restClient);
        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();

        return factory.createClient(GPTComponent.class);
    }

}
@Component
@HttpExchange
public interface GPTComponent {

    @PostExchange()
    ChatResponse getGptResponse(@RequestBody ChatRequest request);
}
// GPTServiceImpl.java
private final GPTComponent gptComponent;

@Override
public String getNewQuestion(ChatRequest chatRequest) {
    // 위의 코드에서
    ChatResponse gptResponse = gptRestTemplate.postForObject(apiUrl, chatRequest, ChatResponse.class);
    // 아래로 바뀌었다. 서비스상에서는 큰 차이가 보이지 않는다.
    ChatResponse gptResponse = gptComponent.getGptResponse(chatRequest);
    ...
}

 

GPTComponent 파일을 보면 아주 대박이다. 기존 RestTemplate를 사용할 때는 postForObject 메서드를 직접 이용해서 post 요청을 보냈지만 RestClient에서는 단순히 인터페이스를 정의해서 동일한 역할을 수행하게 된다. 

이 방식은 FeignClient가 떠오르기도 한다.

 

 

앞으로는 RestTemplate보다 RestClient를 사용할 것 같다. WebClient의 편한 방식을 어쩜 이리 잘 가져왔는지....

무거운 WebClient 대신 RestClient를 사용해 보는 게 어떨까?!

 

일단 이 유튜브 영상을 보고 생각해도 늦지 않다. 너무 좋은 영상이다. (토비 님 짱짱)

https://youtu.be/S4W3cJOuLrU