티스토리 뷰

Spring

[Spring] RestTemplate 알아보기

woo'^'chang 2023. 4. 22. 16:46

Spring 프레임워크에서는 외부 API와 통신을 위해 RestTemplate을 구현해 두었습니다. RestTemplate이 무엇이고 어떤 동작을 수행하는지 정리해보고자 합니다.

RestTemplate

RestTemplate는 스프링 3.0부터 지원하는 HTTP 요청을 수행하는 동기식 클라이언트이다. HTTP 통신을 단순화하고 RESTful 원칙을 지킨 HTTP 메서드(GET, POST, DELETE, PUT)에 적합한 여러 메서드들을 제공하고 있기 때문에 HTTP 통신에 있어 유용하게 사용할 수 있다.

 

RestTemplate는 Blocking과 동기 방식이라는 특징이 존재한다. Blocking과 동기, Non-blocking과 비동기를 같은 개념으로 헷갈리는 경우가 많다. 하지만 두 개념은 구분 짓는 기준이 전혀 다르다. 이에 대해 먼저 알아보고자 한다.

Blocking/Synchronous

블로킹/논블로킹은 한 작업이 처리되는 동안 다른 작업이 처리될 수 있는가에 대한 구분이다.

 

[ Blocking ]

블로킹은 다른 함수를 호출할 때, 제어권도 넘겨주고 해당 작업이 끝난 후에 돌려받는 방식이다. 예를 들어 현재 제어권을 가지고 있는 A 함수가 B 함수를 호출하게 되면 제어권이 B 함수로 넘어가게 된다. B 함수는 자신의 작업이 종료되기 전까지 A 함수에게 제어권을 돌려주지 않기에 다른 작업을 처리할 수 없음을 의미한다.

 

[ Non-blocking ]

논블로킹은 다른 함수를 호출할 때, 제어권을 바로 돌려받는 방식이다. 예를 들어 현재 제어권을 가지고 있는 A 함수가 B 함수를 호출하게 되면 B 함수로 넘어간 제어권을 A 함수로 다시 넘겨주어 다른 작업을 처리할 수 있게 된다.

 

동기/비동기는 작업이 시간을 맞추는가에 대한 구분이다.

 

[ Synchronous ]

동기는 작업이 시간을 맞추는 방식이다. 시간을 맞추는 방법에는 시작 시간을 맞추거나 종료 시간을 맞추거나 차례로 실행하거나 하는 방법이 존재한다. 호출한 함수가 호출된 함수의 결과값을 지속적으로 물어보고 있기 때문에 시간을 맞추는 방법이 가능하다.

 

[ Asynchronous ]

비동기는 작업이 동시에 실행되는 방식이다. 연관관계가 있는 상황에서 호출한 함수에서 지속적으로 물어보는 것이 아닌 호출된 함수가 결과값을 알아서 반환하도록 하는 것을 의미한다.

 

블로킹/논블로킹과 동기/비동기에 대한 조합 상황을 표로 정리하면 아래와 같다.

RestTemplate은 블로킹/동기 방식의 특징을 가지고 있기에 메서드 호출 시 제어권이 전달되고 해당 작업이 완료될 때까지 기다렸다가 제어권과 결과값을 동시에 받아 다음 작업이 진행된다는 것을 이해할 수 있다.

Blocking으로 만든 이유

Blocking은 호출하는 즉시 해당 호출이 완료될 때까지 스레드를 차단한다. 이 방식은 코드를 간결하게 유지할 수 있고, 외부 API를 호출하는 중요한 로직에서 문제를 최소화할 수 있다. 그러나 이러한 방식은 대기 시간이 긴 API 요청의 경우 서비스의 전반적인 성능 저하를 초래할 수 있다. 따라서 RestTemplate의 Blocking 방식을 사용하는 경우 서비스의 비동기식 통신 기능이 중요하지 않고, 단일 스레드 상태로 작동하는 것이 좋다.

Asynchronous로 만든 이유

이전 컴파일 언어들과 같은 프로그래밍 모델에서 동기 호출 방식은 일반적인 방식이었다. 또한 단순하고 직관적인 API 호출 패턴을 가지고 있기에 안정성, 확장성 및 신뢰성을 보장하기 위해 처리 및 응답 시간을 예측할 수 있도록 하기 위함이다. 또한 네트워크 오버헤드와 같은 통신 비용과 비동기 방식의 구현에 필요한 추가 코드 및 노력을 최소화한다.

메서드

HTTP 요청을 보내기 위한 구현체로 HTTP와 관련된 여러 메서드가 존재한다. 공식 문서를 확인하면 모든 메서드를 확인할 수 있는데 그중 몇 가지 메서드에 대한 설명은 아래와 같다.

 

메서드 시그니처 설명
<T> ResponseEntity<T> getForEntity(
    String url,
    Class<T> responseType,
    Object... uriVariables
)
URI 주소로 HTTP GET 요청을 통해 리소스를 반환한다.
<T> T getForObject(
    String url,
    Class<T> responseType,
    Object... uriVariables
)
URI 주소로 HTTP GET 요청을 통해 객체를 반환한다.
<T> ResponseEntity<T> postForEntity(
    String url,
    Object request,
    Class responseType,
    Object... uriVariables
)
URI 주소로 HTTP POST 요청을 통해 리소스를 생성하고 응답에 있는 리소스를 반환한다.
<T> T postForObject(
    String url,
    Object request,
    Class<T> responseType,
    Object... uriVariables
)
URI 주소로 HTTP POST 요청을 통해 리소스를 생성하고 응답에 있는 객체를 반환한다.
URI postForLocation(
    String url,
    Object request,
    Object... uriVariables
)
URI 주소로 HTTP POST 요청을 통해 리소스를 생성하고 위치 헤더값을 반환한다.
void put(
    String url,
    Object request,
    Object... uriVariables
)
URI 주소로 HTTP PUT 요청을 통해 리소스를 생성하거나 업데이트한다.
<T> T patchForObject(
    String url,
    Object request,
    Class<T> responseType,
    Object... uriVariables
)
URI 주소로 HTTP PATCH 요청을 통해 리소스를 업데이트하고 응답에 있는 객체를 반환한다.
void delete(String url, Object... uriVariable) URI 주소로 HTTP DELETE 요청을 통해 리소스를 삭제한다.
<T> ResponseEntity<T> exchange(
    String url,
    HttpMethod method,
    HttpEntity<?> requestEntity,
    Class<T> responseType,
    Object... uriVariables
)
지정된 URI 템플릿에 HTTP 메서드를 실행하여 지정된 요청 엔티티를 요청에 쓰고 응답을 ResponseEntity로 반환한다.

즉, HTTP 요청 설정을 커스텀하여 원하는 타입의 결과를 ResponseEntity로 받을 수 있음을 의미한다.
<T> T execute(
    String uriTemplate,
    HttpMethod method,
    RequestCallback requestCallback,
    ResponseExtractor<T> responseExtractor,
    Object... uriVariables
)
지정된 URI 템플릿에 HTTP 메서드를 실행하여 RequestCallback으로 요청을 준비하고 ResponseExtractor로 응답을 읽습니다.

즉, HTTP 요청 시 요청과 응답에 따른 Callback을 지정할 수 있음을 의미한다.

동작 원리

위의 사진을 바탕으로 RestTemplate 메서드를 직접 따라가 보며 동작 원리를 알아보고자 한다.

클라이언트에서 애플리케이션에 요청 데이터를 전송하면 HttpMessageConverter가 요청 데이터를 컨트롤러에 필요한 객체로 생성한다. 애플리케이션에서는 생성된 RestTemplate을 통해 외부 API에게 요청을 보내게 되는데 URI, HTTP, 요청 정보를 매개변수로 전달하여 메서드를 호출한다.

먼저 RequestCallback을 생성하는데 요청 전에 수행해야 할 동작을 지정한다. 최종적으로 ClientHttpReqeust를 통해 요청이 실행되는데 요청 정보로 만든 RequestEntity의 정보를 ClientHttpRequest에 설정하는 역할을 수행한다.

 

ResponseExtractor는 HTTP 응답이 도착했을 때 설정한 타입을 담은 ResponseEntity를 생성해 주는 역할을 수행한다. 생성한 RequestCallback, ResponseExtractor와 함께 HTTP 요청을 실행하게 된다.

URI, HTTP method에 대한 null 검증 이후 ClientHttpRequest를 생성하고 이전에 생성한 RequestCallback을 통해 세부 정보를 설정한다. 설정이 완료된 ClientHttpRequest를 실행함으로 HTTP 프로토콜을 통해 외부 API와 통신하게 되는 것이다. 통신 도중 예외가 발생한다면 ResponseErrorHandlerClientHttpResponse에서 응답 데이터를 가져와 예외를 처리하게 된다.

 

최종적으로 ResponseExtractor에서 ClientHttpResponse 응답 데이터를 바탕으로 ResponseEntity를 생성하여 반환하게 되는 것이다.

실습

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

RestTemplate은 org.springframework.web.client에 속한다. 위의 의존성을 통해 해당 패키지를 가져와 사용할 수 있다.

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
    	return new RestTemplate();
    }
}

RestTemplate은 빈으로 등록되어 있지 않기 때문에 빈으로 등록하여 사용하기 위해서는 다음과 같이 직접 등록이 필요하다.

@RequestMapping("/rest-template")
@RestController
public class RestTemplateController {

	private final ApplicationContext applicationContext;

    public RestTemplateController(ApplicationContext applicationContext) {
    	this.applicationContext = applicationContext;
    }

    @GetMapping("/hello")
    public ResponseEntity<HelloResponse> hello() {
        final RestTemplate restTemplate = applicationContext.getBean(RestTemplate.class);
        final String url = "http://localhost:8081/hello";
        return restTemplate.getForEntity(url, HelloResponse.class);
    }
}
@RestController
public class HelloController {

    @GetMapping("/hello")
    public ResponseEntity<HelloResponse> hello() {
    	return ResponseEntity.ok(new HelloResponse("hello", 200));
    }
}

8080 포트와 8081 포트에 2개의 서버를 띄워 실습하였다. 간단하게 RestTemplate을 통해 요청을 보내는 코드와 요청이 전달될 진입점에 대한 코드를 작성하였다.

 

실제 결과를 확인하면 다음과 같은 결과를 확인할 수 있다.

 

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Total
Today
Yesterday