[SPRING] RestTemplate

야부엉·2023년 11월 14일
0

1. RestTemplate

1. RestTemplate이란?

  • Rest 방식의 API를 호출할 수 있는 Spring 내장 클래스
  • Spring 3.0부터 지원하는 Spring의 HTTP 통신 템플릿이다.

2. 사용이유

  • 예를 들어서 우리가 만든 서비스의 회원가입에 사용자의 주소를 입력받아야 할 때가 있다. 이러한 상황에 우리가 가진 서버를가지고 기능을 직접 구현하면 시간과 비용이 많이 들 수 있다. 이 때, 카카오에서 만든 주소검색 API를 사용한다면 간단하게 구현 할 수 있다.
  • 우리의 서버가 Client의 입장이 되어 KaKao 서버에 요청을 진행할 때, 즉 우리 서버에서 다른 서버로 간편하게 요청할 수 있도록 Spring은 RestTemplate 기능을 제공하고 있다.

2. RestTemplate의 Get(하나의 값에 대한 요청)

1. Client입장 Server (예시)

  1. RestTemplate을 주입 받는다.
private final RestTemplate restTemplate;

// RestTemplateBuilder의 build()를 사용하여 RestTemplate을 생성합니다.
public RestTemplateService(RestTemplateBuilder builder) {
    this.restTemplate = builder.build();
}
  1. Server 입장의 서버로 RestTemplate를 사용하여 요청한다.
  • 요청 받은 검색어를 Query String 방식으로 Server 입장의 Server로 요청
public ItemDto getCallObject(String query) {
    // 요청 URL 만들기
    URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:7070")
            .path("/api/server/get-call-obj")
            .queryParam("query", query)
            .encode()
            .build()
            .toUri();
    log.info("uri = " + uri);

    ResponseEntity<ItemDto> responseEntity = restTemplate.getForEntity(uri, ItemDto.class);

    log.info("statusCode = " + responseEntity.getStatusCode());

    return responseEntity.getBody();
}
  • Spring의 UriComponentsBuilder를 사용해서 URI를 손쉽게 만들 수 있다.
  • getforentity는 get 방식으로 해당 uri에 요청을 보낸다.
    - 두번째 파라미토로 요청을 해서 받아오는 데이터를 받아줄 클래스 타입을 입력한다.
    - 자동으로 역직렬화가 되서 객체 형태로 담긴다.
    - >즉, 요청의 결과값에 대해서 JSON TO Object를 구현할 필요없이 RestTemplate을 사용하면 자동으로 처리 해준다.
    • 따라서 response.getBody() 를 사용하여 두 번째 파라미터로 전달한 클래스 타입으로 자동 변환된 객체를 가져올 수 있다.

2. Server입장 Server(예시)

  1. 서버 입장의 서버에서 해당 요청에 대한 결과를 반환한다.
public Item getCallObject(String query) {
    for (Item item : itemList) {
        if(item.getTitle().equals(query)) {
            return item;
        }
    }
    return null;
}

3. RestTemplate의 Get 요청(여러개의 값에 대한 요청)

  • 요청 값이 여러개인 경우는 Json을 이용해 처리한다.
  • build.gradle Json Dependency 추가
// json
implementation 'org.json:json:20230227'

1. Client 입장 Server(예시)

  • 위의 코드와 유사하지만, 차이점은 restTemplate.getForEntity(uri, String.class); String class로 객체를 받는다.
public List<ItemDto> getCallList() {
    // 요청 URL 만들기
    URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:7070")
            .path("/api/server/get-call-list")
            .encode()
            .build()
            .toUri();
    log.info("uri = " + uri);

    ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);

    log.info("statusCode = " + responseEntity.getStatusCode());
    log.info("Body = " + responseEntity.getBody());

    return fromJSONtoItems(responseEntity.getBody());
}
  • String Class 객체로 받았을 시,아래와 같은 데이터 형태는 중첩 Json형태를 가지고 있는 String 값이다.
    {
        "items":[
        {"title":"Mac","price":3888000},
        {"title":"iPad","price":1230000},
        {"title":"iPhone","price":1550000},
        {"title":"Watch","price":450000},
        {"title":"AirPods","price":350000}
          ]
    }
  • Json 처리를 도와주는 라이브러리를 이용해서 받아온 Json 형태의 String을 처리한다.
  • getJSONArray에 있는 items는 배열 이름(Server에서 받은 ResponseDto의 배열 변수명)을 가진 값들을 가져온다.
public List<ItemDto> fromJSONtoItems(String responseEntity) {
    JSONObject jsonObject = new JSONObject(responseEntity);
    JSONArray items  = jsonObject.getJSONArray("items");
    List<ItemDto> itemDtoList = new ArrayList<>();

    for (Object item : items) {
        ItemDto itemDto = new ItemDto((JSONObject) item);
        itemDtoList.add(itemDto);
    }

    return itemDtoList;
}

2. 서버 입장 Server(예시)

  • ItemList를 ItemResponseDto에 담아 반환한다.
public ItemResponseDto getCallList() {
    ItemResponseDto responseDto = new ItemResponseDto();
    for (Item item : itemList) {
        responseDto.setItems(item);
    }
    return responseDto;
}

4. RestTemplate의 Post 요청

  • Post 요청도 위에서 설명한 get 요청과 작동 흐름을 같다.

1. Client 입장 Server(예시)

  1. 요청 받은 검색어를 Query String 방식으로 Server 입장의 Server로 요청
public ItemDto postCall(String query) {
    // 요청 URL 만들기
    URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:7070")
            .path("/api/server/post-call/{query}")
            .encode()
            .build()
            .expand(query)
            .toUri();
    log.info("uri = " + uri);

    User user = new User("Robbie", "1234");

    ResponseEntity<ItemDto> responseEntity = restTemplate.postForEntity(uri, user, ItemDto.class);

    log.info("statusCode = " + responseEntity.getStatusCode());

    return responseEntity.getBody();
}
  • 이 코드는 pathVariable 방식을 이용하는 것을 볼 수 있다.
  • .expand(query) :expand를 사용해 {query}안의 값을 동적으로 처리 할 수 있다.
  • restTemplate.postForEntity(uri, user, ItemDto.class); 이 코드를 확인하면 Post방식으로 해당 URI의 서버에 요청을 진행하는 것을 볼 수 있다.
    - 첫 번째 파라미터에는 URI, 두 번째 파라미터에는 HTTP Body에 넣어줄 데이터를 넣는다는 차이점이 있다.
    • 두 번째 파라미터는 자동으로 Json 형태로 변환이 되고, 세 번째 파라미터는 get과 같은 매핑하여 인스턴스화할 클래스 타입을 넣어주면 된다.

2. Server 입장 Server(예시)

  • 요청에 대한 결과를 반환하면 된다.
public Item postCall(String query, UserRequestDto userRequestDto) {
	...
}

5. RestTemplate의 Exchange

  • RestTemplate으로 요청을 보낼 때 Header에 특정 정보를 같이 전달 하고 싶을때 사용한다.
  • 예시는 Authorization : token값을 헤더에 보내는 경우이다.

1. Client의 Server(예시)

public List<ItemDto> exchangeCall(String token) {
    // 요청 URL 만들기
    URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:7070")
            .path("/api/server/exchange-call")
            .encode()
            .build()
            .toUri();
    log.info("uri = " + uri);

    User user = new User("Robbie", "1234");

    RequestEntity<User> requestEntity = RequestEntity
            .post(uri)
            .header("X-Authorization", token)
            .body(user);

    ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class);

    return fromJSONtoItems(responseEntity.getBody());
}
  • exchange 메서드의 첫 번째 파라미터에 RequestEntity 객체를 만들어 전달해주면 uri, header, body의 정보를 한번에 전달할 수 있다.

2. Server의 Server(예시)

  • 이 때 Controller에서 아래와 같이 client 입장 서버의 header 이름을 어노테이션으로 맞춰줘야한다.
    @PostMapping("/exchange-call")
    public ItemResponseDto exchangeCall(@RequestHeader("X-Authorization") String token, @RequestBody UserRequestDto requestDto) {
        return itemService.exchangeCall(token, requestDto);
    }
  • 그래야지 아래와 같이 token 값을 이용 할 수 있다.
public ItemResponseDto exchangeCall(String token, UserRequestDto requestDto) {
    System.out.println("token = " + token);
    System.out.println("requestDto.getUsername() = " + requestDto.getUsername());
    System.out.println("requestDto.getPassword() = " + requestDto.getPassword());
    return getCallList();
}

출처

내일배움캠프 Spring Master

profile
밤낮없는개발자

0개의 댓글