SpringBoot Server(client) to Server 통신방법- UriComponentsBuilder, RestTemplate 사용하기

devdo·2021년 12월 31일
0

SpringBoot

목록 보기
11/33
post-thumbnail

RestTemplate이란?

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이 포함되어 있어 같이 의존성이 포함된다.


RestTemplate 메서드 정리

메서드Http설명
getForObjectGET주어진 URL 주소로 HTTP GET 메서드로 객체로 결과를 반환받는다
getForEntityGET주어진 URL 주소로 HTTP GET 메서드로 결과는 ResponseEntity로 반환받는다
postForLocationPOSTPOST 요청을 보내고 결과로 헤더에 저장된 URI를 결과로 반환받는다
postForObjectPOSTPOST 요청을 보내고 결과로 헤더에 저장된 URI를 결과로 반환받는다
postForEntityPOSTPOST 요청을 보내고 결과로 ResponseEntity로 반환받는다
deleteDELETE주어진 URL 주소로 HTTP DELETE 메서드를 실행한다
headForHeadersHEADER헤더의 모든 정보를 얻을 수 있으면 HTTP HEAD 메서드를 사용한다
putPUT주어진 URL 주소로 HTTP PUT 메서드를 실행한다
patchForObjectPATCH주어진 URL 주소로 HTTP PATCH 메서드를 실행한다
optionsForAllowOPTIONS주어진 URL 주소에서 지원하는 HTTP 메서드를 조회한다
exchangeanyHTTP 헤더를 새로 만들 수 있고 어떤 HTTP 메서드도 사용가능하다
executeanyRequest/Response 콜백을 수정할 수 있다

GET 방식 구현 예제

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;
    }

}

Post방식 구현 예제

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 방식

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/ 요 사이트에서 인코딩해서 보내야 한다.

나온다!



참고

소스출처

패스트캠퍼스 스프링 예성국 강의

profile
배운 것을 기록합니다.

0개의 댓글