Spring Boot - RestTemplate를 사용한 Server To Server 연결(3)
이 전 포스트들에서는 RestTemplate로 GET요청을 다양하게 하는 법과 DTO로 리팩토링해 유연하게 대응하는 법을 알아보았다. 이번에는 GET이 아닌 다른 요청들을 알아보겠다.
먼저 지금까지와는 다르게 patch메서드를 사용하기 위해서는
// Patch 메서드를 사용하기 위한 의존성 / spring boot 3.x
implementation 'org.apache.httpcomponents.client5:httpclient5'
위의 의존성을 추가해주어야한다.
postForEntity(uri, requestObject, responseType.class); // post Entity 요청
postForObject(uri, requestObject, responseType.class); // post Object 요청
patchForObject(uri, requestObject, responseType.class); // patch Object 요청 : entity는 없다
delete(uri); // delete 요청
기본적으로 RestTemplate의 get요청을 보내 듯이 보내되, request Object를 담아서 보내주기만 하면 된다.
post요청으로 예시를 보여주자면 아래와 같이 할 수 있다.
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:10000") // baseUrl
.path("/api/server/hello") // 경로
.build()
.toUri();
// request data
UserRequest data = UserRequest.builder()
.name("Uheejoon")
.age(25)
.build();
// header 생성
HttpHeaders header = new HttpHeaders();
header.add("x-authorization", "ffff");
// 요청 객체 생성
HttpEntity<UserRequest> entity = new HttpEntity<>(data, header);
// build restTemplate
RestTemplate restTemplate = new RestTemplateBuilder()
.additionalInterceptors(new CInterceptor())
.build();
ResponseEntity<UserResponse> result = restTemplate.postForEntity(uri, entity, UserResponse.class);
이러한 함수 외에도 exchange() 또한 지금까지와 동일하다.
restTemplate.exchange(uri, HttpMethod.POST, requestEntity, UserResponse.class);
restTemplate.exchange(uri, HttpMethod.PATCH, requestEntity, UserResponse.class);
restTemplate.exchange(uri, HttpMethod.PUT, requestEntity, UserResponse.class);
restTemplate.exchange(uri, HttpMethod.DELETE, null, Void.class);
근데 지금 코드들을 보면 굳이굳이 exchange함수 안에 uri, 메서드, 요청객체, 리턴 타입을 다 열거해야만 할까? 사람에 따라 다르겠지만 나한테는 너무 번거롭고 불편하다.
exchange의 overloading된 함수들을 보면

requestEntity로 위의 responseType빼고 모두 대체할 수 있다. 지금까지 사용한 코드들을 requestEntity로 바꾸어보자.
// 요청 보낼 uri 생성
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:10000") // baseUrl
.path("/api/server/hello") // 경로
.build()
.toUri();
// request data
UserRequest data = UserRequest.builder()
.name("Uheejoon")
.age(25)
.build();
// request entity
RequestEntity<UserRequest> request = RequestEntity
.post(uri) // method
.headers((header) -> {
header.set("x-authorization", "ffff");
header.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
header.setContentType(MediaType.APPLICATION_JSON);
}) // header Consumer 등록 가능
.body(data);
// build restTemplate
RestTemplate restTemplate = new RestTemplateBuilder()
.additionalInterceptors(new CInterceptor())
.build();
ResponseEntity<UserResponse> result = restTemplate.exchange(request, UserResponse.class);
return result;

예상한대로 잘 나오는 것을 볼 수 있다.
Generic의 사용
지금까지는 일반 class 객체를 주고 받았고, entity와 .class로 리턴 받을 타입을 담았다. 근데 만약 받으려고 하는 데이터의 타입이 Generic이라면 어떻게 해야할까? Generic에는 .class를 붙일 수가 없다. 이 부분을 집고 넘어가야한다.
먼저 지금까지 사용한 exchange함수에 해당 내용이 있기를 바라며 문서를 살펴보자.

exchange의 Overloading된 함수들 중 ParameterizedTypeReference 라는 것이 눈에 띈다. 제네릭이 들어가 있으면서 이름 부터가 "Reference의 파라미터화" 이다. 자세하게 확인해보자.

정말로 이 클래스의 목적은 generic 타입이 캡처링이 가능하고 통과할 수 있게 해주기 위해서 라고 한다. 다시 말해서 generic을 사용할 수 있게 해주는 것 같다. 그렇다면 이제 generic을 선언해서 사용해보자.
간단하게 Generic record를 선언해보자.
@Builder
public record Api<T>(
T body
) { }
그리고 받으려는 타입을 아래와 같이 선언한다.
ParameterizedTypeReference<Api<UserResponse>> response = new ParameterizedTypeReference<>() {};
이를 활용해서 데이터를 주고 받아보자.
<client>
// 요청 보낼 uri 생성
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:10000") // baseUrl
.path("/api/server/hello") // 경로
.build()
.toUri();
// request data
UserRequest data = UserRequest.builder()
.name("Uheejoon")
.age(25)
.build();
// request entity
RequestEntity<UserRequest> request = RequestEntity
.post(uri) // method
.headers((header) -> {
header.set("x-authorization", "ffff");
header.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
header.setContentType(MediaType.APPLICATION_JSON);
}) // header Consumer 등록 가능
.body(data);
// build restTemplate
RestTemplate restTemplate = new RestTemplateBuilder()
.additionalInterceptors(new CInterceptor())
.build();
// 받을 타입 정의
ParameterizedTypeReference<Api<UserResponse>> resType = new ParameterizedTypeReference<>() {};
ResponseEntity<Api<UserResponse>> result = restTemplate.exchange(request, resType);
return result;
<server>
@PostMapping("/hello")
public ResponseEntity<Api<UserResponse>> helloPost(
@RequestHeader("x-authorization") String authorization,
@RequestBody UserRequest request
) {
// convert
UserResponse data = UserResponse.builder()
.name(request.name())
.age(request.age())
.build();
Api<UserResponse> res = Api.<UserResponse>builder().body(data).build();
return ResponseEntity.ok().body(res);
}

원하는 결과가 잘 나오는것을 볼 수 있다.