웹 프로그래밍/[ Spring Boot ]

[ SpringBoot ] 07. RestTemplate을 알아보자

kim.svadoz 2021. 10. 12. 17:00
728x90
반응형

RestTemplate

  • Spring 3.0부터 지원하는 Spring의 HTTP 통신 템플릿 (server to servet)
  • RESTful의 원칙을 지킬 수 있으며 HTTP 메소드들에 적합한 여러 메소드가 제공됨
  • HTTP 요청 후 JSON, XML, string과 같이 응답을 받을 수 있는 템플릿 (그게 아니라면 직접 라이브러리로 파싱해야함)
  • Blocking I/O 기반의 동기방식을 사용(REST API 호출 후 응답을 받을 때까지 기다린다)
  • Header와 Content-Type을 설정해서 외부 API 호출 가능
  • 단순 메소드 호출 만으로 복잡한 작업을 쉽게 처리 가능

HttpClient : HTTP를 사용해 통신하는 범용 라이브러리로 RestTemplate은 HttpClient를 추상화 해서 제공한다.

다른 HTTP 통신방법으로는 URLConnecton이 있는데 이는 타임아웃을 설정할 수 없고 쿠키를 제어할 수 없다는 단점이 있다. 또 응답 코드가 4xx, 5xx가 되면 IOException이 발생한다.

또 다른 방식으로는 HttpClient가 있는데 모든 응답코드에 대응이 가능하고 타임아웃 설정과 쿠키제어가 가능하지만 여전히 코드의 가독성이 좋지 않으며 스트림 처리를 별도 로직을 작성해야 하고 응답 컨텐츠 타입에 따라서 별도 로직이 필요하다는 단점이 있다.

그래서 이러한 단점들을 보완하는 RestTemplate이 현재 가장 많이 사용된다.

RestTemplate은 HttpClient를 추상화해서 제공이 되어 있어 내부 통신은 Apache HttpComponents를 사용한다.

> RestTemplate 메서드

image-20210924155919175

  • getForObject()

    Product product = restTemplate.getForObject(BASE_URL + "/{id}", Prodcut.class);

    Prodcut로의 매핑은 기본적으로 jackson-databind가 담당한다.

  • getForEntity()

    • 응답을 ResponseEntity 객체로 받는다.
    • getForObject()와 달리 HTTP 응답에 대한 추가 정보를 담고 있어 GET 요청에 대한 응답 코드, 실제 데이터를 확인할 수 있다.
    • 또, ResponseEntity 제네릭 타입으로 응답을 String이나 Object 객체로 유연하게 받는 것이 가능하다.
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(BASE_URL + "/{id}", String.class, 25);
    log.info("statusCode: {}", responseEntity.getStatusCode());
    log.info("getBody: {}", responseEntity.getBody());
    • getForEntity()에 여러 값을 담을 params를 같이 넘겨줄 수 있다. LinkedMultiValueMap 객체에 담아서 parmas로 넘겨줄 수 있다.
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("name", "book");
    params.add("description", "best-seller");
    
    ResponseEntity<Product> responseEntity = restTemplate.getForEntity(BASE_URL + "/{name}/{description}", Product.class, params);
    log.info("statusCode: {}", responseEntity.getStatusCode());
    log.info("getBody: {}", responseEntity.getBody());
  • get 요청에 header가 필요한 경우

    • get Method에서는 header를 추가할 수가 없으므로 exchange method를 사용해야 한다.
    HttpHeaders headers = new HttpHeaders();
    headers.set("header", header);
    headers.set("header2", header2);
    
    HttpEntity request = new HttpEntity(headers);
    
    ResponseEntity<String> response = restTemplate.exchange(
        URL_PATH,
        HttpMethod.GET,
        request,
        String.class
    );
  • get 요청에 header 값과 쿼리 스트링(query String, param)이 필요한 경우

    • post처럼 HttpEntity에 넣어서 요청할 수가 없다.
    HttpHeaders headers = new HttpHeaders();
    headers.set("header", header);
    headers.set("header2", header2);
    
    HttpEntity request = new HttpEntity(headers);
    
    UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(URL_PATH).queryParam("keywords", "11");
    
    ResopnseEntity<String> response = resTemplate.exchange(
        uriBuilder.toUriString(),
        HttpMethod.GET,
        request,
        String.class
    );
    • 이렇게 UriBuilder를 사용해서 넣는 수 밖에 없다.
    • post방식과 달리 httpEntity에 같이 넣거나 exchange의 parameter로 넘길 수 가 없다.
    • 굳이 uriBuilder를 쓰지 않고 map에 파라미터를 추가하고 map을 parameter 문자열로 변환해주는 메소드를 만들어서 사용하는 것이 편할 수 있다.
    HttpHeaders headers = new HttpHeaders();
    headers.set("header", header); 
    headers.set("header2", header2);
    
    HttpEntity request = new HttpEntity(headers);
    
    Map<String, String> params = new HashMap<String, String>();
    params.put("query1", "test1");
    params.put("query2", "test2");
    
    ResponseEntity<String> response = restTemplate.exchange(
        URL_PATH + "?" + this.mapToUriParam(params),
        HttpMethod.GET,
        request,
        String.class
    );
    
    ...
    
    private static String mapToUriParam(Map<String, Object> params) {
        StringBuffer paramData = new StringBuffer();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            if (paramData.length() != 0) {
                paramData.append("&");
            }
            paramData.append(param.getKey());
            paramData.append("=");
            paramData.append(String.valueOf(param.getValue()));
        }
        return paramData.toString();
    }
    
  • postForObject() Method에 header 값이 없는 경우

    Product newProduct = Product.builder()
        .name("book")
        .description("best-seller").build();
    
    Product product = restTemplate.postForObject(BASE_URL + "/product", newProduct, Prodcut.class);
  • postForObject() Method에 header 포함해서 보내기

    Prodcut newProduct = Product.builder()
        .name("book")
        .description("best-seller").build();
    
    HttpHeaders headers = new HttpHeaders();
    headers.set("headerTest", "headerValue");
    
    HttpEntity<Product> request = new HttpEntity<newProduct, headers);
    
    Product product = restTemplate.postForObject(BASE_URL + "/product", request, Product.class);
  • post에 form data를 사용

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    
    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
    
    ResponseEntity<String> response = restTemplate.postForEntity(BASE_URL + "/form", request, String.class);
  • TimeOut 설정하기

    • timeOut을 설정하려면 ClientHttpRequestFactory와 같은 팩토리 메소드를 만들고 RestTemplate의 생성자에 추가해야 한다.
    RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
    
    private ClientHttpRequestFactory getClientHttpRequestFactory() {
        int timeout = 5000;
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setConnetTimeout(timeout);
    
        return clientHttpRequestFactory;
    }
    // timeout = 0 : 무제한 설정
  • Execute()

    • Execute()는 콜백을 통해 요청 준비와 응답 추출을 완벽하게 제어하여 요청을 수행하는 가장 일반적인 메서드를 ResTemplate에서 제공한다.
    • getForObejct()나 postForObject() 등은 execute()를 내부적으로 호출한다.

> RestTemplate의 동작원리

image-20210924160054392

  1. 어플리케이션이 RestTemplate을 생성하고, URI, HTTP 메소드 등의 헤더를 담아 요청한다.
  2. RestTemplate은 HttpMessageConverter를 사용해 requestEntity를 요청 메세지로 변환한다.
  3. RestTemplate은 ClientHttpRequestFactory로부터 ClientHttpRequest를 가져와서 요청을 보낸다.
  4. ClientHttpRequest는 요청 메시지를 만들어서 HTTP 프로토콜을 통해 서버와 통신한다.
  5. restTemplate은 ResponseErrorHandler로 오류를 확인하고 있다면 처리로직을 태운다.
  6. ResponseErrorHandler는 오류가 있다면 ClientHttpResponse 에서 응답데이터를 가져와서 처리한다.
  7. RestTemplate은 HttpMessageConverter를 이용해 응답메세지를 Java Object(Class ResponseType)로 변환한다.
  8. 어플리케이션에 반환한다.

참조

https://velog.io/@soosungp33/%EC%8A%A4%ED%94%84%EB%A7%81-RestTemplate-%EC%A0%95%EB%A6%AC%EC%9A%94%EC%B2%AD-%ED%95%A8

https://juntcom.tistory.com/141

728x90
반응형