ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] RestTemplate를 활용한 HTTP 요청
    Spring Boot/기타 2024. 11. 10. 21:46
    반응형

     

    목차

       

      RestTemplate란?

      RestTemplate은 Spring Framework의 HTTP 클라이언트로, RESTful 웹 서비스와 상호작용하여 쉽게 HTTP 요청을 보낼 수 있도록 도와줍니다.

       

      주요 클래스와 메서드

      • RestTemplate : HTTP 클라이언트를 생성하고 설정합니다.
        • getForObject() : GET 요청을 URL에 보내고, 결과를 객체로 반환합니다.
        • getForEntity() : GET 요청을 URL에 보내고, 결과를 ResponseEntity로 반환합니다.
        • postForObject() : POST 요청을 URL에 보내고, 결과를 객체로 반환합니다.
        • postForEntity() : POST 요청을 URL에 보내고, 결과를 ResponseEntity로 반환합니다.
        • put() : PUT 요청을 URL에 보냅니다.
        • delete() : DELETE 요청을 URL에 보냅니다.
        • exchange() : 특정 HTTP 메서드 요청을 URL에 보냅니다.
        • headForHeaders() : HEAD 요청을 URL에 보내고, 결과로 헤더 정보를 반환합니다.

       

      RestTemplate 사용 예제

      1. RestTemplate 설정

      Spring Boot를 사용하면, 별도로 Gradle 빌드 파일에 RestTemplate 의존성을 추가할 필요 없이 HTTP 요청을 보낼 수 있습니다. 이는 Spring Boot가 자동 구성(auto-config) 기능을 가지고 있어, 필요한 라이브러리를 자동으로 추가하고 구성해주기 때문입니다. 다만, 구체적인 설정(예: 타임아웃 값 조정 등)가 필요한 경우에는 직접 설정을 추가해야 합니다.

      아래와 같이 RestTemplateBuilder를 사용하여 RestTemplate 객체를 구성하며, 연결 타임아웃(setConnectTimeout)과 읽기 타임아웃(setReadTimeout)을 각각 5초로 설정한 후 스프링 빈으로 등록할 수 있습니다.

      import java.time.Duration;
      import org.springframework.boot.web.client.RestTemplateBuilder;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.client.RestTemplate;
      
      @Configuration
      public class RestTemplateConfig {
      
        /**
         * RestTemplate 빈을 생성
         *
         * @return RestTemplate 객체
         */
        @Bean
        public RestTemplate restTemplate() {
          return new RestTemplateBuilder()
                  .setConnectTimeout(Duration.ofSeconds(5)) // 연결 타임아웃을 5초로 설정
                  .setReadTimeout(Duration.ofSeconds(5)) // 읽기 타임아웃을 5초로 설정
                  .build();
        }
      }

       

      2. HTTP 요청을 위한 유틸리티 클래스

      HttpUtil 클래스는 RestTemplate을 활용하여 다양한 HTTP 메서드(GET, POST, PUT, DELETE)를 사용하여 HTTP 요청을 보내고, 서버로부터 응답을 받아 이를 DTO 객체로 반환하는 유틸리티 클래스입니다.

      import java.util.Map;
      import lombok.AllArgsConstructor;
      import org.springframework.http.HttpEntity;
      import org.springframework.http.HttpHeaders;
      import org.springframework.http.HttpMethod;
      import org.springframework.http.ResponseEntity;
      import org.springframework.stereotype.Component;
      import org.springframework.web.client.RestTemplate;
      
      /**
       * RestTemplate를 사용한 HTTP 요청(GET, POST, PUT, DELETE)을 위한 유틸리티 클래스
       */
      @AllArgsConstructor
      @Component
      public class HttpUtil {
      
        private final RestTemplate restTemplate;
      
        /**
         * HTTP 요청을 위한 HttpEntity를 생성
         *
         * @param headers 요청 헤더 정보(키-값 쌍)
         * @param body    요청 본문 객체
         * @return HttpEntity 객체
         */
        private <T> HttpEntity<T> createHttpEntity(Map<String, String> headers, T body) {
          HttpHeaders httpHeaders = new HttpHeaders();
          if (headers != null) {
            headers.forEach(httpHeaders::set);
          }
          return new HttpEntity<>(body, httpHeaders);
        }
      
        /**
         * GET 요청을 보내고 응답을 객체로 반환
         *
         * @param targetUrl    요청을 보낼 URL
         * @param headers      요청 헤더 정보
         * @param responseType 응답을 매핑할 클래스 타입
         * @return 응답 객체
         */
        public <T> ResponseEntity<T> sendGet(String targetUrl, Map<String, String> headers, Class<T> responseType) {
          HttpEntity<Void> entity = createHttpEntity(headers, null);
          return restTemplate.exchange(targetUrl, HttpMethod.GET, entity, responseType);
        }
      
        /**
         * POST 요청을 보내고 응답을 객체로 반환
         *
         * @param targetUrl    요청을 보낼 URL
         * @param body         요청 본문 객체
         * @param headers      요청 헤더 정보
         * @param responseType 응답을 매핑할 클래스 타입
         * @return 응답 객체
         */
        public <T, R> ResponseEntity<R> sendPost(String targetUrl, T body, Map<String, String> headers, Class<R> responseType) {
          HttpEntity<T> entity = createHttpEntity(headers, body);
          return restTemplate.exchange(targetUrl, HttpMethod.POST, entity, responseType);
        }
      
        /**
         * PUT 요청을 보내고 응답을 객체로 반환
         *
         * @param targetUrl    요청을 보낼 URL
         * @param body         요청 본문 객체
         * @param headers      요청 헤더 정보
         * @param responseType 응답을 매핑할 클래스 타입
         * @return 응답 객체
         */
        public <T, R> ResponseEntity<R> sendPut(String targetUrl, T body, Map<String, String> headers, Class<R> responseType) {
          HttpEntity<T> entity = createHttpEntity(headers, body);
          return restTemplate.exchange(targetUrl, HttpMethod.PUT, entity, responseType);
        }
      
        /**
         * DELETE 요청을 보내고 응답 객체를 반환
         *
         * @param targetUrl    요청을 보낼 URL
         * @param headers      요청 헤더 정보
         * @param responseType 응답을 매핑할 클래스 타입
         * @return 응답 객체
         */
        public <T> ResponseEntity<T> sendDelete(String targetUrl, Map<String, String> headers, Class<T> responseType) {
          HttpEntity<Void> entity = createHttpEntity(headers, null);
          return restTemplate.exchange(targetUrl, HttpMethod.DELETE, entity, responseType);
        }
      }

       

      3. 단위 테스트 작성

      HttpUtil 클래스를 테스트하여 HTTP 요청(GET, POST, PUT, DELETE)이 정상적으로 동작하는지 확인합니다.

       

      3_1. GET 요청 테스트

      GET 요청을 보내고, 응답의 ID가 요청한 ID와 같은지 확인합니다.

      @DisplayName("GET 요청: ID를 기준으로 포스트 조회 후 응답 ID 확인")
      @Test
      void testGetRequest() throws Exception {
      
        // Given
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
      
        // When
        ResponseEntity<PostDTO> response = httpUtil.sendGet(TEST_GET_URL, headers, PostDTO.class);
        log.debug("response: {}", objectMapper.writeValueAsString(response.getBody()));
      
        // Then
        assertAll(
                () -> assertNotNull(response),
                () -> assertNotNull(response.getBody()),
                () -> assertEquals(1, response.getBody().getId())
        );
      }

       

      3_2. POST 요청 테스트

      POST 요청을 보내고, 응답의 title과 body가 요청한 값과 같은지 확인합니다.

      @DisplayName("POST 요청: 포스트 저장 후 응답의 title과 body 확인")
      @Test
      void testPostRequest() throws Exception {
      
        // Given
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
      
        PostDTO post = PostDTO.builder()
                .title("foo")
                .body("bar")
                .userId(1)
                .build();
      
        // When
        ResponseEntity<PostDTO> response = httpUtil.sendPost(TEST_POST_URL, post, headers,
                PostDTO.class);
        log.debug("response: {}", objectMapper.writeValueAsString(response.getBody()));
      
        // Then
        assertAll(
                () -> assertNotNull(response),
                () -> assertNotNull(response.getBody()),
                () -> assertEquals("foo", response.getBody().getTitle()),
                () -> assertEquals("bar", response.getBody().getBody())
        );
      }

       

      3_3. PUT 요청 테스트

      PUT 요청을 보내고, 응답의 title과 body가 요청한 값과 같은지 확인합니다.

      @DisplayName("PUT 요청: 포스트 수정 후 응답의 title과 body 확인")
      @Test
      void testPutRequest() throws Exception {
      
        // Given
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
      
        PostDTO putData = PostDTO.builder()
                .id(1)
                .title("foo")
                .body("bar")
                .userId(1)
                .build();
      
        // When
        ResponseEntity<PostDTO> response = httpUtil.sendPut(TEST_PUT_URL, putData, headers,
                PostDTO.class);
        log.debug("response: {}", objectMapper.writeValueAsString(response.getBody()));
      
        // Then
        assertAll(
                () -> assertNotNull(response),
                () -> assertNotNull(response.getBody()),
                () -> assertEquals("foo", response.getBody().getTitle()),
                () -> assertEquals("bar", response.getBody().getBody())
        );
      }

       

      3_4. DELETE 요청 테스트

      DELETE 요청을 보내고, 응답이 빈 값인지 확인합니다.

      @DisplayName("DELETE 요청: 포스트 삭제 후 응답이 빈 값인지 확인")
      @Test
      void testDeleteRequest() throws Exception {
      
        // Given
        Map<String, String> headers = new HashMap<>();
        headers.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
      
        // When
        ResponseEntity<PostDTO> response = httpUtil.sendDelete(TEST_DELETE_URL, headers, PostDTO.class);
        log.debug("response: {}", objectMapper.writeValueAsString(response.getBody()));
      
        // Then
        assertAll(
                () -> assertNotNull(response),
                () -> assertNotNull(response.getBody()),
                () -> assertEquals(null, response.getBody().getTitle()),
                () -> assertEquals(null, response.getBody().getBody())
        );
      }

       

      참고

       

      자세한 소스 코드는 Github Repository를 참조하세요.

       

      관련 글

      [Spring Boot] RestClient를 활용한 HTTP 요청

      [Spring Boot] RestClient, HttpInterface를 활용한 HTTP 요청

      [Spring Boot] Apache HttpClient 5 기반 RestClient 구성하기

       

      반응형

      댓글

    Designed by Tistory.