REST Template (3)

REST Template의 exchange메소드를 활용해보자.

이전 시간에는 Request를 보낼 때 dto와 objectMapper의 기능을 이용해 JSON만 보냈지만 이번 시간에는 RequestEntity를 사용하여 Header을 추가하는 방법에 대해 알아본다.

restTemplateService 클래스안에 exchange()메소드를 만든다.

public UserResponse exchange(){
    //http://localhost:9091/api/server/user/{userId}/name/{userName}
    URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:9091")
            .path("/api/server/user/{userId}/name/{userName}")
            .encode()
            .buildAndExpand(100, "dboo") //userId, userName
            // .build().expand() 따로 해줘도 무방
            .toUri();
    log.info(uri.toString());

    // http body -> object -> object mapper -> JSON -> rest template -> http body json

    UserRequest req = new UserRequest();
    req.setName("dboo");
    req.setAge(31);

    RequestEntity<UserRequest> requestEntity = RequestEntity
            .post(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .header("x-authorization", "abcd")
            .header("custom-header", "fffff")
            .body(req);


    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<UserResponse> response = restTemplate.exchange(requestEntity, UserResponse.class);
    return response.getBody();
}

그리고 클라이언트 컨트롤러에서 exchange()메소드를 타도록 해준다.

@PostMapping("/exchange")
public UserResponse exchangeHello(){
    return restTemplateService.exchange();
}

요청에 들어온 헤더를 서버측에서 꺼내 읽어보자.

@PostMapping("/user/{userId}/name/{userName}")
public User post(@RequestBody User user,
                 @PathVariable int userId,
                 @PathVariable String userName,
                 @RequestHeader("x-authorization") String authorization,
                 @RequestHeader("custom-header") String customHeader){
    log.info("Header => auth : {} , custom : {}", authorization, customHeader);
    log.info("PathVariable => userId : {}, userName : {}", userId, userName);
    log.info("client req => user객체 : {}", user);
    return user;
}

현업에서의 JSON

현업에서 사용하는 JSON의 구조를 상정해보자.

{
  "header" : {

  },
  "body" : {

  }
}

위와 같은 형태로 내려오는데 매번 header와 body의 내용이 변한다고 생각해보자.

{
  "header" : {
    "response_code" : "OK"
  },
  "body" : {
    "name" : "dboo",
    "age" : 31,
    "book" : "spring boot",
    "page" : 1024
  }
}

body에는 name,age 가 들어올 수도 있고 book, page가 들어올 수도 있다고 생각해보고

이러한 JSON을 받기 위한 구조를 디자인 해보자.

일단, 클라이언트에서 JSON을 보내기위한 dto를 만든다.

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Req<T> {

    private Header header;
    private T body; //제네릭 타입, 매번 내용이 바뀌기 때문에.

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Header{
        private String responseCode;
    }

}

body에는 어떤내용이 들어올지 모르기때문에 제네릭 타입으로 선언해준다.

서버측에도 동일한 dto를 만들어둔다.

이제 이 JSON을 서버로 보내기 위한 컨트롤러단과 서비스단을 수정해준다.

@RestController
@RequestMapping("/api/client")
@RequiredArgsConstructor
public class ApiController {

    private final RestTemplateService restTemplateService;

    @GetMapping("")
    public UserResponse getHello(){
        return restTemplateService.hello();
    }

    @PostMapping("")
    public UserResponse postHello(){
        return restTemplateService.post();
    }

    @PostMapping("/exchange")
    public UserResponse exchangeHello(){
        return restTemplateService.exchange();
    }

    @PostMapping("/genericExchange")
    public Req<UserResponse> genericExchangeHello(){
        return restTemplateService.genericExchange();
    }
}

일단 genericExchange라는 메소드를 통해 동작하게하고, 서비스단에 genericExchange메소드를 생성한다.

public Req<UserResponse> genericExchange(){
    //http://localhost:9091/api/server/user/{userId}/name/{userName}
    URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:9091")
            .path("/api/server/user/{userId}/name/{userName}")
            .encode()
            .buildAndExpand(100, "dboo") //userId, userName
            // .build().expand() 따로 해줘도 무방
            .toUri();
    log.info(uri.toString());

    // http body -> object -> object mapper -> JSON -> rest template -> http body json


    UserRequest userRequest = new UserRequest();
    userRequest.setName("dboo");
    userRequest.setAge(31);

    Req<UserRequest> req = new Req<UserRequest>();
    req.setHeader(new Req.Header());
    req.setBody(userRequest);

    RequestEntity<Req<UserRequest>> requestEntity = RequestEntity
            .post(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .body(req);

    RestTemplate restTemplate = new RestTemplate();


    ResponseEntity<Req<UserResponse>> response
            = restTemplate.exchange(requestEntity,
    // Generic타입의 경우 타입클래스.class로 클래스타입지정할수 없기 때문에 ParameterizedTypeReference를 사용한다.
    //new ParameterizedTypeReference<Req<UserResponse>>(){});
            new ParameterizedTypeReference<>(){}); // returnType이 지정되어있기 때문에 생략가능
    //return response.getBody().getBody();
    // response.getBody() => Req , response.getBody().getBody() => Req안의 body인 UserResponse
    return response.getBody();
}

이제 서버에서 generic타입 멤버변수를 포함하는 객체로 요청을 받아서 처리하는 컨트롤러를 추가해보자.

public Req<User> post(
                 // HttpEntity<String> entity,
                 @RequestBody Req<User> user,
                 @PathVariable int userId,
                 @PathVariable String userName,
                 @RequestHeader("x-authorization") String authorization,
                 @RequestHeader("custom-header") String customHeader){
    // log.info("entity : {}", entity.getBody());
    log.info("Header => auth : {} , custom : {}", authorization, customHeader);
    log.info("PathVariable => userId : {}, userName : {}", userId, userName);
    log.info("client req => user객체 : {}", user);

    Req<User> response = new Req<>();
    response.setHeader(new Req.Header());
    response.setBody(user.getBody());
    return response;
}

return을 할때에 Req 타입으로 객체를 리턴하도록 하고, Req객체 안에는 header, body를 넣어서 에코로 돌려보내준다.

HttpEntity 의 경우 순수한 http엔티티가 들어온다. 주의할 점은 HttpEntity를 찍어보려면 RequestBody 부분을 주석처리하고 해야한다.