RestTemplate

배세훈·2022년 1월 20일
0

Spring

목록 보기
33/38

1. RestTemplate 이란?

  • spring 3.0부터 지원
  • 기계적이고 반복적인 코드를 최대한 줄여줌
  • Restful 형식에 맞춤
  • json, xml 를 쉽게 응답 받을 수 있음

2. HTTP 서버와의 다양한 통신방법

2-1. URLConnection

  • jdk 1.2부터 내장되어 있으며 java.net 패키지에 있다. URL의 내용을 읽어오거나 URL 주소에 GET, POST로 데이터를 전달 할 때 사용

ex)
1. new URL("http:// ...")
2. openConnection()
3. URLConnection
4. getInputStream, getOutputStream
5. InputStream, OutputStream 처리

문제점

  • 응답코드가 4xx 거나 5xx 면 IOException 처리된다.
  • 타임아웃을 설정할 수 없다.
  • 쿠키 제어 불가능

2-2 HttpClient

ex)
1. CloseableHttpClient httpclient = HttpClients.createDefault();
2. 메소드에 따라 new HttpGet("http:// ...");
3. CloseableHttpResponse response = httpclient.execute(httpget);
4. HttpEntity entity = response.getEntity();
5. Stream 으로 entity.getContent() 처리 등

URLConnection 와 비교하였을 때 장점

  • 모든 응답코드를 읽을 수 있다. (httpResponse.getStatusLine().getStatusCode())
  • 타임아웃 설정 가능
  • 쿠키 제어 가능

문제점

  • URLConnection 을 이용한 방식보다 코드가 간결하지만 여전히 반복적이고 코드가 길다.
  • 스트림 처리 로직을 별도로 짜야한다.
  • 응답의 컨텐츠타입에 따라 별도 로직이 필요하다.

3. RestTemplate 동작원리

  • org.springframework.http.client 패키지에 있다. HttpClient는 HTTP를 사용하여 통신하는 범용 라이브러리이고, RestTemplate은 HttpClient를 추상화(HttpEntity의 json, xml 등)해서 제공해준다. 따라서 내부 통신(HTTP 커넥션)에 있어서는 Apache HttpComponents를 사용한다. 만약 RestTemplate가 없었다면, 직접 json, xml 라이브러리를 사용해서 변환해야 한다.

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

4. RestTemplate 사용

설정 생성

HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(5000); // 읽기 최대 시간 지정, ms
factory.setConnectTimeout(3000); // 연결 최대 시간 지정, ms
HttpClient httpClient = httpClientBuilder.create()
	.setMaxConnTotal(100) // connection pool 적용
    .setMaxConnPerRoute(5) // connection pool 적용
    .build();
factory.setHttpClient(httpClient); // 동기 실행에 사용될 HttpClient 세팋ㅇ
RestTemplate restTemplate = new RestTemplate(factory);
  • RestTemplate은 기본적으로 connection pool을 사용하지 않는다.
    따라서 연결할 때마다 로컬 포트를 열고 tcp connection을 맺는다.
    이때 문제는 close() 이후에 사용된 소켓은 TIME_WAIT 상태가 되는데 요청량이 많다면 이런 소켓들을 재사용하지 못하고 소켓이 모두 소진되어 응답이 지연될 것이다.
    이런 경우 connection pool을 사용해서 해결할 수 있는데 DBCP마냥 소켓의 갯수를 정해서 재사용하는 것이다.
    RestTemplate 에서 connection pool을 적용하려면 위와 같이 HttpClient를 만들고 setHttpClient()를 해야한다.
  • setMaxConnPerRoute: IP, Port 1쌍에 대해 수행 할 연결 수를 제한한다.
  • setMaxConnTotal: 최대 오픈되는 커넥션 수를 제한한다.

예제

public void getForObject(){
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api")
                .queryParam("name", "shbae")
                .queryParam("age", "29")
                .encode()
                .build()
                .toUri();

        log.info("uri: {}", uri);

        RestTemplate restTemplate = new RestTemplate();
        User user = restTemplate.getForObject(uri, User.class);
        log.info("user: {}", user);
    }

    public void getForEntity(){
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/{path}")
                .queryParam("name", "shbae")
                .queryParam("age", 29)
                .encode()
                .build()
                .expand("user")
                .toUri();

        log.info("uri: {}", uri);

        RestTemplate restTemplate = new RestTemplate();

        ResponseEntity<User> response = restTemplate.getForEntity(uri, User.class);
        log.info("{}", response.getStatusCode());
        log.info("{}", response.getHeaders());
        log.info("{}", response.getBody());
    }

    public void postForObject(){
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/{path}")
                .encode()
                .build()
                .expand("user")
                .toUri();
        log.info("uri: {}", uri);

        RestTemplate restTemplate = new RestTemplate();

        User user = new User();
        user.setName("shbae");
        user.setAge(29);
        User response = restTemplate.postForObject(uri, user, User.class);
        log.info("response: {}", response);
    }

    public void postForEntity(){
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/{path}")
                .encode()
                .build()
                .expand("user")
                .toUri();
        log.info("uri: {}", uri);

        User user = new User();
        user.setName("shbae");
        user.setAge(29);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<User> response = restTemplate.postForEntity(uri, user, User.class);
        log.info("{}", response.getStatusCode());
        log.info("{}", response.getHeaders());
        log.info("{}", response.getBody());
    }

    public void exchange(){

        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/{path}/header")
                .encode()
                .build()
                .expand("user")
                .toUri();
        log.info("uri: {}", uri);

        User user = new User();
        user.setName("shbae");
        user.setAge(29);

        RequestEntity<User> req = RequestEntity
                .post(uri)
                .contentType(MediaType.APPLICATION_JSON)
                .header("x-authorization", "my-header")
                .body(user);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<User> response = restTemplate.exchange(req, new ParameterizedTypeReference<User>() {});
        log.info("{}", response.getStatusCode());
        log.info("{}", response.getHeaders());
        log.info("{}", response.getBody());

    }
profile
성장형 인간

0개의 댓글