Server - Server 끼리 통신하는 방법을 배워보자. 요즘에는 MSA방식으로 웹개발이 이루어지니, Server들이 많아지면서 그들끼리 data들을 주고 받는 일이 많아졌다.
그러면서 스프링은 객체로 통신하게끔 라이브러리를 지원하고 있는 것이 RestTemplate이다.
그 사용방식은 간단했다. 어렵지 않게 http method방식처럼 이루어지기 때문이다. get, post, delete, 등등의 메서드를 지원해준다.
RestTemplate은 Spring 3.0 부터 지원하는 동기 방식의 템플릿으로 Spring에서 HTTP 통신을 RESTful 형식에 맞게 손쉬운 사용을 제공해주는 템플릿이다. Rest API 서비스를 요청후 응답 받을 수 있도록 설계되었으며 HTTP 프로토콜의 메소드(ex. GET, POST, DELETE, PUT)들에 적합한 여러 메소드들을 제공한다. Java에서 사용되는 다른 템플릿(ex. JdbcTemplate)들 처럼 단순 메소드 호출 만으로 복잡한 작업을 쉽게 처리할 수 있는 것이 특징이다.
출처: https://minkwon4.tistory.com/178
RestTemplate은 spring-web 의존성에 포함된 클래스이지만, spring-webmvc 의존성에 spring-web이 포함되어 있어 같이 의존성이 포함된다.
메서드 | Http | 설명 |
---|---|---|
getForObject | GET | 주어진 URL 주소로 HTTP GET 메서드로 객체로 결과를 반환받는다 |
getForEntity | GET | 주어진 URL 주소로 HTTP GET 메서드로 결과는 ResponseEntity로 반환받는다 |
postForLocation | POST | POST 요청을 보내고 결과로 헤더에 저장된 URI를 결과로 반환받는다 |
postForObject | POST | POST 요청을 보내고 결과로 헤더에 저장된 URI를 결과로 반환받는다 |
postForEntity | POST | POST 요청을 보내고 결과로 ResponseEntity로 반환받는다 |
delete | DELETE | 주어진 URL 주소로 HTTP DELETE 메서드를 실행한다 |
headForHeaders | HEADER | 헤더의 모든 정보를 얻을 수 있으면 HTTP HEAD 메서드를 사용한다 |
put | PUT | 주어진 URL 주소로 HTTP PUT 메서드를 실행한다 |
patchForObject | PATCH | 주어진 URL 주소로 HTTP PATCH 메서드를 실행한다 |
optionsForAllow | OPTIONS | 주어진 URL 주소에서 지원하는 HTTP 메서드를 조회한다 |
exchange | any | HTTP 헤더를 새로 만들 수 있고 어떤 HTTP 메서드도 사용가능하다 |
execute | any | Request/Response 콜백을 수정할 수 있다 |
client 프로젝트, server 프로젝트를 각각 만든다.
1) client 프로젝트
ApiController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/client")
public class ApiController {
private final RestTemplateService restTemplateService;
@GetMapping("/hello")
public UserResponse getHello() {
return restTemplateService.hello();
}
}
RestTemplateService
@Service
public class RestTemplateService {
// http://localhost/api/server/hello
// response
public UserResponse hello() {
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/hello")
.queryParam("name", "steve")
.queryParam("age", 13)
.encode()
.build()
.toUri();
System.out.println(uri.toString());
RestTemplate restTemplate = new RestTemplate();
// getForObject vs getForEntity
// String result = restTemplate.getForObject(uri, String.class);
// json 형태로 받자!
ResponseEntity<UserResponse> result = restTemplate.getForEntity(uri, UserResponse.class);
// UserResponse result = restTemplate.getForObject(uri, UserResponse.class);
System.out.println(result.getStatusCode());
System.out.println(result.getBody());
return result.getBody();
}
UriComponentsBuilder
: Uri를 만들 때 사용하는 빌더객체
여기서 queryParam()
을 통해 파라미터를 던진다.
RestTemplate
: RestTemplater 객체
getForObject
vs getForEntity
getForObject
는 반환값이 Object이고, getForEntity
는 반환값이 Entity인 것이다.
restTemplate.getForObject(uri, String.class);
restTemplate.getForEntity(uri, UserResponse.class);
2) server 프로젝트
ServerApiController
@RestController
@RequestMapping("/api/server")
@Slf4j
public class ServerApiController {
@GetMapping("/hello")
public User get(@RequestParam String name, @RequestParam int age) {
log.info("hello server요청");
User user = new User();
user.setName(name);
user.setAge(age);
log.info("user: " + user);
return user;
}
}
1) client 프로젝트
ApiController
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/client")
public class ApiController {
private final RestTemplateService restTemplateService;
@GetMapping("/post")
public UserResponse postHello() {
return restTemplateService.post();
}
}
RestTemplateService
@Service
public class RestTemplateService {
public UserResponse post() {
// http://localhost:9090/api/server/user/{userId}/name/{userName}
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userId}/name/{userName}") // post시 expand()로 param을 던진다.
.encode()
.build()
.expand(11, "steve")
.toUri();
System.out.println("uri :" + uri);
// http body -> object -> object mapper -> json -> rest template -> http body json
UserRequest req = new UserRequest();
RestTemplate restTemplate = new RestTemplate();
// String.class 로 해서 제대로 들어오는지 확인용도로 쓸 수 있다!
ResponseEntity<UserResponse> response = restTemplate.postForEntity(uri, req, UserResponse.class);
System.out.println(response.getStatusCode());
System.out.println(response.getHeaders());
System.out.println(response.getBody());
return response.getBody();
}
}
post방식에선 UriComponentsBuilder
에서 expand()
에서 pathVariable을 채운다.
2) server 프로젝트
ServerApiController
@RestController
@RequestMapping("/api/server")
@Slf4j
public class ServerApiController {
@PostMapping("/user/{userId}/name/{userName}")
public User post(@RequestBody User user, @PathVariable int userId, @PathVariable String userName) {
log.info("userId : {}, userName", userId, userName);
user.setName("dsg");
user.setAge(10);
log.info("client req : {}", user);
return user;
}
}
post방식일 때 결과이다. server 프로젝트에서 User setter를 사용 값을 변경한 것이 반영된다.
exchange 방식은 어떤 Http 메서드 방식으로 구현이 가능하며, Headers 헤더 값을 새로 넣을 수 있다.
.header("x-authorization","my-header")
이런식으로 새로 교정할 수 있다.
restTemplate.exchange(request, new ParameterizedTypeReference<>(){});
exchange 메서드는
ParameterizedTypeReference<>(){}
을 파라미터로 가진다.
RestTemplateService
public ResponseEntity exchange(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/{path}/header")
.encode()
.build()
.expand("user")
.toUri();
log.info("uri : {}", uri);
UserRequest req = new UserRequest();
req.setName("dsg");
req.setAge(27);
RequestEntity<UserRequest> request = RequestEntity
.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization","my-header")
.body(req);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<UserResponse> response = restTemplate.exchange(request, new ParameterizedTypeReference<>(){});
log.info("{}",response.getStatusCode());
log.info("{}",response.getHeaders());
log.info("{}",response.getBody());
return response;
}
또는
@Repository
@Slf4j
@RequiredArgsConstructor
public class ElasticSearchRepository {
private final ElasticSearchProps elasticSearchProps;
private final RestTemplate restTemplate;
public void indexProductList(List<ProductDocumentDto> productDocumentDtoList) {
String url = elasticSearchProps.getProductEngineUrl() + "/documents";
HttpEntity<List<ProductDocumentDto>> entity = new HttpEntity<>(productDocumentDtoList, getHttpHeaders());
restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
}
private HttpHeaders getHttpHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + elasticSearchProps.getProductEnginePrivateKey());
return headers;
}
}
public ResponseEntity naver(){
URI uri = UriComponentsBuilder
.fromUriString("https://openapi.naver.com")
.path("/v1/search/local.json")
.queryParam("query","eni 스터디룸")
.queryParam("display","10")
.queryParam("start","1")
.queryParam("sort","random")
.encode() // standard: UTF-8
.build()
.toUri();
log.info("uri : {}", uri);
RequestEntity<Void> req = RequestEntity
.get(uri)
.header("X-Naver-Client-Id","Zi3o1uQftp59zuIqEAz4")
.header("X-Naver-Client-Secret","iy6YKSWpLM")
.build();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(req, new ParameterizedTypeReference<>(){});
log.info("{}",response.getStatusCode());
log.info("{}",response.getHeaders());
log.info("{}",response.getBody());
return response;
}
naver-API 개발 가이드 참조,
application 등록 검색 api 로 선택!
Clinet ID
& Client Secret
정보 나오는 것을 exchange()
헤더 값에 새로 갱신해서 naver 지역 api 정보를 검색할 수 있는 것으로 활용할 수 있다.
헤더 (-H
) 새로 넣고,
query
param은 URL 인코딩(UTF-8) 방식으로
https://dencode.com/ 요 사이트에서 인코딩해서 보내야 한다.
나온다!
소스출처
패스트캠퍼스 스프링 예성국 강의