본문 바로가기

Back-End/Spring Boot

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);
}

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