[Spring Boot] Rest Template 이란?

Monday·2024년 4월 8일
0

Spring

목록 보기
2/6
post-thumbnail

MSA 환경에서는 기능에 따라 분리된 여러 컴포넌트가 있다.
애플리케이션이 제공하는 서비스가 복잡하다면 하나 이상의 컴포넌트를 통합하여
서비스를 제공한다.

MSA 아키텍처에서 데이터를 통합하려고 가장 많이 사용하는 방법은 원격 API 호출이다.
쉽게 개발할 수 있고 기능을 확장하기 쉬워 보편적으로 REST-API를 사용한다.
REST-API를 제공하는 컴포넌트를 서버, REST-API를 호출하는 컴포넌트를 클라이언트라고 한다.

스프링 프레임워크에서는 REST-API를 쉽게 호출하는 클래스를 제공한다.
대표적으로 RestTemplateWebClient 다.
RestTemplate 은 블로킹 동기식 방식을 사용한 대표적인 REST-API 클라이언트 클래스다.

RestTemplate 클래스

RestTemplate 클래스는 REST-API를 실행할 수 있는 메서드를 제공한다.
다른 서버의 REST-API를 호출할 수 있도록 URL를 설정하고,
GET, POST 같은 HTTP 메서드를 사용할 수 있는 메서드를 제공한다.

RestTemplate의 특징

  • 자바 객체를 HTTP 요청 메시지의 바디로 변환하고,
    HTTP 응답 메시지 바디를 자바 객체로 변환하는 기능을 제공한다. 이때 메시지의 콘텐츠 타입에 따라 적절한 컨버터를 찾아 동작한다. RestTemplate 은 이 컨버터를 쉽게
    확장할 수 있는 구조를 갖고 있다.
    JSON뿐만 아니라 XML 같은 메시지를 자바 객체로 쉽게 변환할 수 있다.
  • RestTemplate 은 HTTP 프로토콜을 직접 사용하여 서버의 REST-API를 호출하는
    일련의 네트워크 작업을 추상화한 메서드들을 제공한다.
    대표적으로 getForEntity(), postForEntity(), delete(), put() 메서드다.
    이 메서드들은 HTTP 메서드 이름과 같으므로 쉽게 사용할 수 있다.
    또한 RestTemplate의 메서드를 사용하면 네트워크 작업을 위한 반복적인 코드를
    생략할 수 있다.
  • RestTemplate 은 네트워킹 기능을 별도의 클래스에 위임하고 있다.
    그래서 커넥션 관리나 메시지를 주고받는 저수준 네트워킹 작업은 구현체에 따라
    다르게 동작한다.
    개발자가 원하는 구현체를 사용할 수 있으며,
    RestTemplate 의 메서드는 이 구현체에 상관없이 일관된 기능을 제공한다.
  • RestTemplate 은 인터셉터 기능을 제공하고 있어 REST-API를 실행할 때 인터셉터를 실행 할 수 있다.
    그러므로 메시지를 주고받을 때 기능을 확장할 수 있다.
  • RestTemplate 은 멀티 스레드 환경에 안전한 (thread-safe) 클래스이므로
    스프링 빈으로 객체를 생성하고 필요한 곳에 주입해서 사용해도 된다.

RestTemplate 구조

HttpMessageConverter

  • HTTP의 바디 메시지를 자바 객체로 변환
  • 스프링 MVC 프레임워크에서 콘텐츠 타입에 따라 메시지를 변환하는 역할
  • Content-type 헤더에 정의된 값에 따라 적절한 HttpMessageConverter가 동작

사용자가 필요한 HttpMessageConverter 객체들을 새로 구성하려면
setMessageConverters(List<HttpMessageConverter<?>> messageConverters)
HttpMessageConverter 리스트를 인자로 받는 생성자를 사용하면 된다.

ResponseErrorHandler

에러를 처리할때 사용한다.
기본 설정으로 o.s.web.client.DefaultReponseErrorHandler 구현체를 사용하지만, 구현체를 설정할 수 있는 setErrorHandler() 메서드를 제공하고 있어 사용자의 의도에 따라 에러 처리 기능을 교체할 수 있다.

ReponseErrorHandler 인터페이스는 두 가지 추상 메서드를
포함하고 있다.
에러여부를 판단하는 boolean has Error() 메서드와 예외를 처리하는 void handleError() 메서드가 정의되어 있다.
DefaultReponseErrorHandler 구현체는 HTTP 상태 코드 세 자리 중
첫 번째 자리에 따라 예외를 던지도록 구현되어 있다.
DefaultResponseErrorHandler가 던지는 예외들은 RuntimeException 클래스를 상속받아 RuntimeException을 기본 처리하는 스프링 프레임워크의 예외 전략을 사용할 수 있다.

public interface ResponseErrorHandler {
	
    boolean hasError(ClientHttpResponse response) throws IOException;
    
    void handleError(ClientHttpResponse response) throws IOException;
    
    default void handleError(URI url, HTtpMethod method, ClientHttpResponse response) throws IOException {
    	handleError(response);
    }
}

ClientHttpRequestFactory

  • REST-API 서버와 커넥션을 맺고 요청을 전달하고 응답 메시지를
    받아 올 때까지 일련의 네트워킹 과정을 처리
  • HTTP 프로토콜을 사용하여 클라이언트 요청과 서버 응답을 처리한다.
  • 클라이언트 요청은 ClientHttpRequest 클래스로,
    서버 응답은 ClentHttpReponse 클래스로 추상되어 처리된다.
  • ClientRequestexecute() 메서드를 실행하면 ClientHttpResponse 객체를 받을 수 있다. 이때 서버와 클라이언트 사이에 커넥션을 사용하여 요청 전송 및 응답 수신 과정이 진행된다.

ClientHttpRequestFactory 인터페이스를 구현한 구현체

  • OkHttp3ClentHttpRequestFactory 안드로이드에서 많이 사용하는 OkHttp 로 작성한 구현체다.
    동기식/비동기식 프로그래밍이 모두 가능한 장점이 있다.
  • Netty4ClientHttpRequestFactory 비동기 논블로킹 프레임워크인 Netty를 사용하여 작성된 구현체다.
    비동기 프로그래밍을 계획한다면 가장 먼저 고려하는 것이 좋다.
  • SimpleClientHttpRequestFactory 동기식으로 동작하며,
    JDK에서 제공하는 라이브러리를 사용하여 작성된 구현체다

RestTemplate의 기본 생성자 내부에는 기본 설정으로 HttpMessageConverter 객체들을 생성하고 내부 변수 messageConverters에 할당한다.

ClientHttpRequestInterceptor

ClientHttpRequestInterceptor 인터페이스가 HttpRequestFactory에 포함되어 있다.
ClientHttpRequestInterceptor 인터페이스는 클라이언트 요청을
서버로 전송하기 전에 실행되는 intercept() 메서드를 제공하고 있어
REST-API를 호출하고 응답받는 과정에 기능을 추가할 수 있다.
스프링 WebMVC의 인터셉터와 비슷한 역할이다.
여러개의 인터셉터를 설정할 수 있으며 체인 패턴 방식으로 구성된다.
그래서 입터셉터들을 순차적으로 실행한 후 REST-API를 호출한다.

@FunctionalInterface
public interface ClientHttpRequestInterceptor {
	ClentHttpResponse intercpet(HttpRequest request, byte[] body,
    								ClientHttpRequestExecution execution) throws IOException;
}

intercpet() 메서드는 REST-API의 사용자 요청 메시지를 가로채고, 서버의 응답 메시지를 리턴한다.

RestTemplate 스프링 빈 설정

@Configuration
public class RestTemplateConfig {
	
    @Bean
    public ClientHttpRequestFactory clientHTtpRequestFactry() {
    	SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        
        factory.setConnectTimeout(3000);
        factory.setReadTmeout(1000);
        factory.setBufferRequestBody(false);
        
        return factory;
    }
    
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
    	RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
        
        restTemplate.getInterceptors().add(new IdentityHeaderInterceptor());
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
        
        return restTemplate;
    }
}

RestTemplateClientHttpRequestFactory 구현체로 SimpleClientHttpRequestFactory를 사용한다.

SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();

클라이언트와 서버 사이에 커넥션 객체를 생성하는 데 소요되는 최대 시간(밀리초)을 설정한다

factory.setConnectTimeout(3000);

클라이언트가 서버에 데이터 처리를 요청하고 응답받기까지 소요되는 최대 시간(밀리초)을 설정한다.
factory.setReadTmeout(1000);

SimpleClientHttpRequestFactory는 요청 메시지의 바디를 버퍼링하는 기능을 제공한다.
기본 설정(true)은 요청 메시지의 바디를 버퍼링한다.
큰 파일이 버퍼에 저장되면 애플리케이션의 메모리에 문제가 발생할 수 있으므로, 요청 메시지의 바디 크기가 크다면 반드시 false로 설정하여 버퍼링 기능을 사용하지 않는게 좋다.

factory.setBufferRequestBody(false);

ClentHttpRequestFactory 인자를 받는 생성자를 사용하여 RestTemplate 객체를 생성한다.

RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

RestTemplate 객체에 새로운 인터셉터 객체를 추가한다.
getInterceptors()RestTemplate 객체에 설정된 인터셉터 리스트 객체를 리턴한다.
그러므로 리스트 객체의 add() 메서드를 사용하여 새로운 Interceptor 객체를 추가한다.

restTemplate.getInterceptors().add(new IdentityHeaderInterceptor());

RestTemplate 객체에 새로운 ResponseErrorHandler를 설정한다.

restTemplate.setErrorHandler(new DefaultResponseErrorHandler());

Connection Timeout과 ReadTimeOut 설정

SimpleClientHttpRequestFacotry를 설정할 때 가장 중요한 것은
Connection Tmeout 값과 Read TimeOut 값을 설정하는 것이다.

MSA 환경에서는 컴포넌트의 데이터를 다른 컴포넌트로 전파하거나 참조할때
네트워크 매체를 주로 사용한다.
네트워크는 편리하지만 언제 어디서든 장애가 발생할 수 있다.

장애 상황으로 REST-API 클라이언트는 서버에 커넥션을 맺거나 요청 후
응답을 오래 기다려야 할 수도 있다.
심지어 커넥션을 맺지 못하거나 응답을 받지 못할수도 있다.
장애 상황이 지속되면 사용자가 여러번 클릭을 하여 요청이 더 많아질 수 있다.
그러면 결국 REST-API 클라이언트도 리소스가 부족해지거나 과부하 상태가 될 수 있다.
이렇게 하나의 시스템 장애가 다른 시스템 장애로 번질 수 있다.

그러므로 MSA 환경에서 자신의 시스템을 보호하는 여러 가지 방법을 강구해 두어야 한다.
그 중 가장 간단한 방법이 Connection TimeOut과 Read TimeOut을 설정하는 것이다.

적절한 Connection TimeOut과 Read TimeOut을 찾는 것은 어렵다.
서버 관점이 아닌 서비스 관점에서 생각해야 한다.

사용자가 요청한 API를 5초 이내에 응답하는 것이 목표라면
5초를 Connection TimeOut과 ReadTimeOut로 잘 분배해야 한다.
여기에는 정답은 없다.
적절한 TimeOut 값을 설정하고 스카우터나 핀 포인트 같은 APM 툴을 사용하여
모니터링을 하는 것이 중요하다.

Connection TimeOut이나 Read TimeOut으로 인한 예외가 발생하면
적절한 폴백(fallback) 처리를 해주어야 한다.

출처
스프링 부트로 개발하는 MSA 컴포넌트

profile
차근차근 꾸준히

0개의 댓글