1. RestTemplate
1. RestTemplate이란?
- Rest 방식의 API를 호출할 수 있는 Spring 내장 클래스
- Spring 3.0부터 지원하는 Spring의 HTTP 통신 템플릿이다.
2. 사용이유

- 예를 들어서 우리가 만든 서비스의 회원가입에 사용자의 주소를 입력받아야 할 때가 있다. 이러한 상황에 우리가 가진 서버를가지고 기능을 직접 구현하면 시간과 비용이 많이 들 수 있다. 이 때, 카카오에서 만든 주소검색 API를 사용한다면 간단하게 구현 할 수 있다.
- 우리의 서버가 Client의 입장이 되어 KaKao 서버에 요청을 진행할 때, 즉 우리 서버에서 다른 서버로 간편하게 요청할 수 있도록 Spring은 RestTemplate 기능을 제공하고 있다.
2. RestTemplate의 Get(하나의 값에 대한 요청)
1. Client입장 Server (예시)
- RestTemplate을 주입 받는다.
private final RestTemplate restTemplate;
public RestTemplateService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
- Server 입장의 서버로 RestTemplate를 사용하여 요청한다.
- 요청 받은 검색어를 Query String 방식으로 Server 입장의 Server로 요청
public ItemDto getCallObject(String query) {
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(예시)
- 서버 입장의 서버에서 해당 요청에 대한 결과를 반환한다.
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() {
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(예시)
- 요청 받은 검색어를 Query String 방식으로 Server 입장의 Server로 요청
public ItemDto postCall(String query) {
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) {
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