RestTemplate이란 무엇일까?
•
지금까지는 Client 즉, 브라우저로부터 요청을 받는 서버의 입장에서 개발을 진행해왔다.
•
서비스 개발을 진행하다보면 라이브러리 사용만으로는 구현이 힘든 기능들이 무수히 많이 존재한다.
•
예를 들어 우리의 서비스에서 회원가입을 진행할 때 사용자의 주소를 받아야 한다면? GPS를 통한 위치 자료가 필요하다면? 등등
◦
주소, GPS를 검색할 수 있는 기능을 구현해야하는데 직접 구현을 하게되면 많은 시간과 비용이 들어간다.
◦
그 데이터를 어떻게 받는지 원리부터 파악하는데 연구 시간, 개발 시간이 너무 오래걸릴 수 있다.
◦
가장 중요한 것은 개발자가 접근하기 어려운 이런 부분들을 API 요청에 대한 응답데이터로 다양하게 제공해주고 있는 기업 또는 공공기관이 있다.
이럴 때, 내가 개발하는 서버는 Client의 입장이 되어 예로 Kakao 서버에 요청을 진행해서 받아야 한다.
•
Spring에서는 서버에서 다른 서버로 간편하게 요청할 수 있도록 RestTemplate 기능을 제공
결론적으론 내 포지션 또한 클라이언트 입장이 되어서 어떠한 데이터를 받아와야 한다.
•
웹 페이지(프론트엔드, 클라이언트)
(서버)나의 백엔드 서버
◦
현재까지 진행한 과정들은 대부분 이러한 관계를 가지고 있고, 뒤로 가더라도 항상 이 관계는 변하지 않는다.
◦
하지만 내가 원하는 데이터의 출처가 외부 다른 서비스에 있다면?
•
나의 백엔드 서버(클라이언트)
(서버)외부 백엔드 서버
◦
위와 같은 관계가 되어야 한다.
◦
따라서 전체적인 프로젝트는
•
웹 페이지(프론트엔드, 클라이언트)
(서버)나의 백엔드 서버(클라이언트)
(서버)외부 백엔드 서버
◦
이와 같이 중간의 위치에서 두가지 역할을 모두 수행하는 포지션을 가지게 된다.
이번 실습을 통해서 우측 부분인 나의 백엔드 서버(클라이언트)
(서버)외부 백엔드 서버 부분을 다뤄본다
이 부분이 해결 되면 컨트롤러에서 기존 웹 페이지와의 API 설계를 섞기만 하면 최종적인 형태를 구현할 수 있게 된다.
RestTemplate는 기존 API 요청과 비슷하게 GET 또는 POST 등으로 요청을 진행 할 수 있다. 각 요청 방식마다 어떻게 쿼리문으로 요청하고 어떠한 데이터 타입을 요청할 수 있는지 실습을 통해 살펴보았다.
IDE에 Client입장의 나의 서버 또 다른 IDE에 외부 기관 등의 입장인 데이터 서버를 동시에 켜고 그 요청에 대한 처리들을 살펴보았다.
Spring에서는 이렇게 백엔드 서버가 외부 서버로부터 요청을 하는 것을 보다 간편하게, 직관적으로 구분 할 수 있도록 RestTemplate에서 getCall, postCall 등 메소드를 제공해주고 있다. 실제로 Service의 getForEntity, postForEntity에서 서버로의 요청, 응답을 받아오게 된다.
1. 요청을 보내는 (나-Client) Controller는 다음과 같다.
@RestController
@RequestMapping("/api/client")
public class RestTemplateController {
private final RestTemplateService restTemplateService;
public RestTemplateController(RestTemplateService restTemplateService) {
this.restTemplateService = restTemplateService;
}
//[1] 데이터 1개 GET 요청
@GetMapping("/get-call-obj")
public ItemDto getCallObject(String query) {
return restTemplateService.getCallObject(query);
}
//[2] 데이터 전체 리스트 GET 요청
@GetMapping("/get-call-list")
public List<ItemDto> getCallList() {
return restTemplateService.getCallList();
}
//[3] 어떠한 유저 정보와 필요 데이터 타입을 담아서 1개 POST 요청
@GetMapping("/post-call")
public ItemDto postCall(String query) {
return restTemplateService.postCall(query);
}
//[4] 어떠한 유저 정보와 필요 데이터 타입을 담는데 헤더에 인증(Authorization)정보와 함께 전체 리스트
//(ex: 카카오 회원의 정보라던지, 그 회원의 캐릭터 정보라던지 등 일 수 있음)
@GetMapping("/exchange-call")
public List<ItemDto> exchangeCall(@RequestHeader("Authorization") String token) {
return restTemplateService.exchangeCall(token);
}
}
Java
복사
[1]Service클래스의 ItemDto 타입으로 데이터 1개를 가져오는 GET요청인 getCallObject() 메소드
[2]Service클래스의 List<ItemDto> 타입으로 데이터 리스트를 가져오는 GET요청인 getCallList() 메소드
[3]Service클래스의 RequestEntity<User> 타입으로 유저 정보만을 담아 데이터 1개를 가져오는 POST요청인 postCall() 메소드
[4]Service클래스의 RequestEntity<User> 타입으로 유저 정보와 함께 헤더에 인증 토큰을 담아 데이터 리스트를 가져오는 POST요청인 postCall() 메소드
2번, 4번 요청에서 변환 과정 JSON / → String → JSONObject → ItemDto→ List<ItemDto>
Client 입장의 전체 소스코드(Github)
2. 요청을 받고 응답을 보내는 (가상-Server) Controller는 다음과 같다.
•
위 클라이언트 Service부분에서 “여기서 서버로 요청하고 담아옴”라는 주석이 있는 restTemplate 실행 부분에서 이곳 (가상-Server) 으로 요청하고 여기에서 Service 클래스에서 처리 한 뒤, 응답을 보내준다.
@RestController
@RequestMapping("/api/server")
public class ItemController {
private final ItemService itemService;
public ItemController(ItemService itemService) {
this.itemService = itemService;
}
// [1]서버 - 하나 받고 다시 주기
@GetMapping("/get-call-obj") // Client측 restTemplate.getForEntity(uri, ItemDto.class)로부터 해당 경로로 쿼리문으로 변환된 상태로 요청이 들어옴
public Item getCallObject(@RequestParam String query) {
return itemService.getCallObject(query);
}
// [2]서버 - String 받고 하나씩 객체 배열만들어서 모두 응답해주기
@GetMapping("/get-call-list")
public ItemResponseDto getCallList() {
return itemService.getCallList();
}
// [3]서버 - PathVariable 방식 받기
@PostMapping("/post-call/{query}") //PathVariable 방식으로 받고 있음
public Item postCall(@PathVariable String query, @RequestBody UserRequestDto requestDto) {
return itemService.postCall(query, requestDto);
}
// [4]서버 - 헤더의 토큰, 바디의 객체 받을 것 Dto 객체로 매개변수로 받음
@PostMapping("/exchange-call")
public ItemResponseDto exchangeCall(@RequestHeader("X-Authorization") String token, @RequestBody UserRequestDto requestDto) {
return itemService.exchangeCall(token, requestDto);
}
}
Java
복사
[1]Client로부터 Query param으로 요청했기 때문에, 매개변수로 @RequestParam으로 Query String을 받는다. Service클래스의 Item 타입으로 데이터 1개를 응답해주는 GET요청인 getCallObject() 메소드가 실행 된다. Server 입장의 서버에서 itemList를 조회하여 요청받은 검색어에 맞는 Item을 반환
[2]Client로부터 String으로 요청이 오기 때문에, 매개변수로 @RequestParam으로 Query String을 받는다. Service클래스의 ItemResponseDto 타입으로 데이터 리스트를 응답해주는 GET요청인 getCallList() 메소드가 실행된다. Server 입장의 서버에서 itemList를 ItemResponseDto에 담아 반환
[3]Client로부터 PathVariable 방식으로 요청이 오기 때문에, 매개변수로 @PathVariable String query, @RequestBody UserRequestDto requestDto을 받는다. Service클래스의 Item 타입으로 PathVariable의 요청 query와 유저정보가 담긴 UserRequestDto 받아 데이터 1개를 응답해주는 POST요청인 postCall() 메소드 실행, Server 입장의 서버에서 itemList를 조회하여 요청받은 검색어에 맞는 Item을 반환
[4]Client로부터 Header에 특정 정보를 같이 전달 방식으로 요청이 오기 때문에, 매개변수로 @RequestHeader("Authorization") String token을 받는다. Service클래스의 ItemResponseDto 타입으로 유저 정보와 함께 헤더에 인증 토큰을 담아 데이터 리스트를 응답해주는 POST요청인 exchangeCall() 메소드가 실행, 전달받은 header와 body의 정보를 확인, 2번과 같은 방식으로 Server 입장의 서버에서 itemList를 ItemResponseDto에 담아 반환
Server 입장의 전체 소스코드(Github)