T_era
RestTemplate 직접 생성 vs 빌더 생성 차이와 동작 원리 본문
1. RestTemplate 직접 생성 방식
RestTemplate restTemplate = new RestTemplate();
- 순수한 RestTemplate 인스턴스만 생성됨.
- Spring Boot의 자동설정(메시지 컨버터, 커넥션 타임아웃, 커넥션 풀 등)이 적용되지 않음.
- JSON 변환 등 메시지 컨버터를 직접 추가해야 함.
- 커스텀
ClientHttpRequestFactory를 직접 지정해야 하며, 설정이 번거로움. - 장점:
- PATCH 등 일부 HTTP 메서드 지원을 위해 커스텀 팩토리 지정이 가능.
- 단점:
- 설정이 불편하고, 실수로 인한 오류 가능성 높음.
- Spring Boot의 편리한 자동설정 혜택을 못 받음.
PATCH 메서드 지원
- Java 기본 HttpURLConnection은 PATCH를 지원하지 않음.
- PATCH를 쓰려면
HttpComponentsClientHttpRequestFactory등 별도 팩토리 지정 필요.
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
2. RestTemplateBuilder를 통한 생성 방식
@Autowired
private RestTemplateBuilder restTemplateBuilder;
RestTemplate restTemplate = restTemplateBuilder.build();
- Spring Boot가 제공하는 자동설정(메시지 컨버터, 타임아웃, 커넥션 풀 등)이 모두 적용됨.
- JSON 변환, UTF-8 인코딩 등 대부분의 설정이 자동으로 적용되어 편리함.
- 메서드 체이닝으로 커스텀 설정도 쉽게 가능.
- 단점:
- 기본적으로 내장 HttpURLConnection을 사용 → PATCH 메서드 미지원.
- PATCH를 쓰려면 별도 팩토리 지정 필요(직접 생성 방식과 동일).
3. RestTemplate과 ClientHttpRequestFactory의 관계
- RestTemplate은 직접 네트워크 통신을 하지 않고,
내부적으로 ClientHttpRequestFactory라는 부품을 사용해 실제 HTTP 요청을 만듦. - 대표적인 팩토리 종류:
SimpleClientHttpRequestFactory(기본, HttpURLConnection 기반)HttpComponentsClientHttpRequestFactory(Apache HttpClient 기반, PATCH 등 지원)
- RestTemplate이 요청을 만들 때, 실제 네트워크 통신은 팩토리가 담당한다.
4. Spring Boot 자동설정 끄기
application.yml에서 아래처럼 자동설정을 끄면,
RestTemplateBuilder 등에서 제공하는 자동설정이 적용되지 않음.- 이럴 경우, 직접 생성 방식과 동일하게 모든 설정을 직접 해줘야 함.
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
- org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration
- org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration
5. 실무에서 주의할 점 & 추가로 알면 좋은 내용
- 대부분의 경우 RestTemplateBuilder를 사용하는 것이 편리하고, Spring Boot의 장점을 살릴 수 있음.
- PATCH 등 특수 HTTP 메서드가 필요하다면,
RestTemplateBuilder로 생성하더라도 팩토리를 명시적으로 지정해주면 됨. restTemplateBuilder .requestFactory(HttpComponentsClientHttpRequestFactory::new) .build();- RestTemplate는 Spring 5.0 이후로는 비동기/고성능이 필요한 경우 WebClient 사용을 권장함.
- RestTemplate은 스레드 세이프하므로, 빈으로 등록해서 재사용하는 것이 좋음.
6. 예시 코드와 설명
private List<SearchProductResponse> productSearch(List<Long> productIds) {
EventAddProductRequest eventAddProductRequest = new EventAddProductRequest(productIds);
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:8080")
.path("/api/product/search-product")
.encode()
.build()
.toUri();
ResponseEntity<List<SearchProductResponse>> response = restTemplate.exchange(
uri,
HttpMethod.GET,
new HttpEntity<>(eventAddProductRequest),
new ParameterizedTypeReference<List<SearchProductResponse>>() {}
);
return response.getBody();
}
코드 설명
1. 요청 객체 생성
EventAddProductRequest eventAddProductRequest = new EventAddProductRequest(productIds);
- API에 전달할 요청 객체를 생성.
- 이 객체는 productIds(상품 ID 리스트)를 담고 있음.
2. URI 생성
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:8080")
.path("/api/product/search-product")
.encode()
.build()
.toUri();
UriComponentsBuilder를 사용해 요청할 API의 URI를 만듦..encode()를 호출하면, URI에 특수문자가 포함되어 있을 때 자동으로 인코딩..build().toUri()로 최종 URI 객체를 생성.
3. RestTemplate의 exchange 메서드 사용
ResponseEntity<List<SearchProductResponse>> response = restTemplate.exchange(
uri,
HttpMethod.GET,
new HttpEntity<>(eventAddProductRequest),
new ParameterizedTypeReference<List<SearchProductResponse>>() {}
);
exchange메서드는 다양한 HTTP 메서드(GET, POST, PATCH 등)로 요청을 보낼 수 있음.- 첫 번째 인자: 요청할 URI
- 두 번째 인자: HTTP 메서드 (여기서는 GET)
- 세 번째 인자: 요청 본문과 헤더를 담는
HttpEntity객체 (여기서는 요청 객체만 전달) - 네 번째 인자: 응답 타입을 지정하는데,
제네릭 타입(예: List)을 받을 때는new ParameterizedTypeReference<List<SearchProductResponse>>() {}와 같이 사용해야
타입 정보가 올바르게 전달됨.
4. 응답 결과 반환
return response.getBody();
- 응답 객체에서 실제 데이터(List)만 꺼내서 반환.
추가 설명
단일 객체를 받을 때는 아래처럼 간단하게 사용할 수 있다.
restTemplate.getForObject(apiUrl, User.class);
- 요청 객체가 필요한 경우(POST 등)에는 아래처럼 사용.
restTemplate.postForObject(apiUrl, eventAddProductRequest, User.class);- exchange는 다양한 HTTP 메서드와 복잡한 요청/응답 타입을 지원할 때 유용.
아래는 요청하신 보상 트랜잭션(Compensating Transaction) 관련 내용을 요약하여,
RestTemplate 정리글에 자연스럽게 추가할 수 있도록 작성한 예시입니다.
7. RestTemplate와 트랜잭션의 한계, 그리고 보상 트랜잭션
- RestTemplate은 HTTP 통신을 통해 외부 시스템(마이크로서비스, 외부 API 등)과 데이터를 주고받는 용도로 사용된다.
- 하지만 RestTemplate을 사용한 외부 호출은 Spring의 트랜잭션(@Transactional)과 직접적으로 연동되지 않는다.
- 즉, 한 서비스 내에서 DB 트랜잭션이 롤백되어도, 이미 외부 시스템에 요청이 나간 경우 그 요청을 자동으로 취소할 수 없다.
- 반대로, 외부 시스템에서 실패가 발생해도, 이미 커밋된 로컬 트랜잭션을 자동으로 롤백할 수 없다.
- 이런 이유로, 분산 트랜잭션이 필요한 상황에서는 RestTemplate만으로는 트랜잭션의 원자성을 보장할 수 없다.
보상 트랜잭션(Compensating Transaction)이란?
- 보상 트랜잭션은 분산 환경에서 트랜잭션의 원자성을 보장하기 어려울 때,
실패 시 이미 수행된 작업을 "취소"하는 별도의 작업(보상 작업)을 추가로 수행하는 패턴. - 예를 들어, 서비스A에서 DB에 저장 후, 서비스B에 RestTemplate으로 요청을 보냈는데,
서비스B에서 실패가 발생하면, 서비스A에서 이미 저장한 데이터를 삭제(취소)하는 로직을 추가로 실행하는 방식. - Saga 패턴 등에서 자주 사용되며, 마이크로서비스 아키텍처에서 데이터 일관성을 맞추기 위해 필수적으로 고려해야 하는 개념.
정리
- RestTemplate을 사용하는 경우, 트랜잭션을 서비스 간에 직접 공유할 수 없으므로
데이터 정합성을 위해 보상 트랜잭션(혹은 Saga 패턴 등)을 반드시 고려해야 한다. - 단일 서비스 내 트랜잭션과는 다르게,
외부 시스템과의 연동에서는 "실패 시 어떻게 복구할 것인가"에 대한 설계가 필요.
결론
- RestTemplateBuilder를 사용하면 Spring Boot의 자동설정과 편리함을 누릴 수 있다.
- 직접 생성은 커스텀 설정이 필요할 때만 사용하고, 대부분의 경우 권장되지 않는다.
- PATCH 등 특수 메서드가 필요하면 팩토리를 명시적으로 지정해야 한다.
- 분산 트랜잭션이 필요한 상황에서는 문제가 발생할 수 있어서 복구 방법을 설계해야한다.
- 실무에서는 WebClient로의 전환도 고려해보자.
'Programing > Spring' 카테고리의 다른 글
| Chapter 1: WebSocket과 STOMP 프로토콜 (2) | 2025.07.11 |
|---|---|
| Spring WebSocket 알림 기능 구현 및 테스트 경험 정리 (1) | 2025.07.09 |
| 실시간 데이터 전송 방식 정리 (1) | 2025.07.01 |
| Spring Boot + AWS S3 프로필 이미지 API 구현 (1) | 2025.07.01 |
| Spring Boot EC2 배포 & 테스트 과정에서 문제 해결 (0) | 2025.06.30 |