[스프링 부트 핵심가이드] 서버 간 통신

FeelingXD·2023년 4월 22일
0

북스터디

목록 보기
10/13
post-thumbnail

스프링의 서버간 통신

서버간 통신의 필요성과 MSA

모놀리스 아키텍처(모든서비스가 한프로젝트로 통합되어있는형태)의 서비스는 규모가 커질수록 하나로 통합되어있는 특성때문에 sideEffect를 고려하지 않을수없다. 최근에는 MSA(마이크로서비스 아키텍처 : 작은 단위의 여러프로젝트등의 상호작용으로 서비스하는형태)등 으로 서비스마다 각각 다른서버를 사용하여 서비스 하는형태가 늘었다. 각 서버는 필요한 서버의 API를 활용하여 서비스하며 이과정에서 스프링에서는 Rest template 와 webclient 를 통하여 다른 서버와 요청을 주고받을수 있도록 도와준다.

Rest Template 와 WebClient

RestTemplate
pring에서 지원하는 객체로 간편하게 Rest 방식 API를 호출할 수 있는 Spring 내장 클래스이다. Spring 3.0부터 지원되었고, json, xml 응답을 모두 받을 수 있다.
Rest API 서비스를 요청 후 응답받을 수 있도록 설계되어 있으며 HTTP 프로토콜의 메소드(ex. GET, POST, DELETE, PUT)들에 적합한 여러 메소드를 제공한다.

  • HTTP 통신 기능을 손쉽게 사용하도록 설계된 템플릿이다. HTTP 서버와의 통신을 단순화한 이 템플릿을 이용하면 RESTful한 서비스를 편리하게 만들 수 있다.
  • 동기 방식으로 처리된다. (Blocking I/O)
  • 기존에 서비스하고있는 프로젝트가 사용하고 있을확률이 크다. 그러나 deprecated 되었기때문에 추후 변경 해야할수있다.(legacy)

WebClient
Spring Framework 5부터는 WebFlux 스택과 함께 Spring은 WebClient라는 새로운 HTTP 클라이언트를 도입하여 기존의 동기식 API를 제공할 뿐만 아니라 효율적인 비차단 및 비동기 접근 방식을 지원하여, Spring 5.0 이후부터는 RestTemplate는 deprecated 되었다.

  • 기본적으로 비동기 처리방식을 지원한다.
  • Reactive Streams의 Back Pressure를 지원한다.(서비스에대한 응답을 보장하는방법)

Example Code

//RestTemplate GET example
public String getName() {
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/v1/crud-api")
                .encode()
                .build()
                .toUri();

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

        return responseEntity.getBody();
    }

    public String getNameWithPathVariable() { //path variable 이 있는경우
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090") // 외부 api 주소
                .path("/api/v1/crud-api/{name}") // -> 외부 api의 endpoint
                .encode()
                .build()
                .expand("name") // 복수의 값을 넣어야할 경우 , 를 추가 위의경우 pathVariable 의 값이들어감 
                .toUri();

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class); //해당 uri 에대한 응답값을 String 로 받음

        return responseEntity.getBody();
    }
    
    public String getNameWithParameter(String name) { //파라미터를 넣는경우
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/v1/crud-api/param")
                .queryParam("name", name) //=> 외부 api 호출시 name이라는 parameter를 추가함 위의경우 
                //http://localhost:9090/api/v1/crud-api/param?name={name} 과 같이 호출됨
                .encode()
                .build()
                .toUri();

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

        return responseEntity.getBody();
    }
//Rest template post의 경우
public ResponseEntity<MemberDto> postWithParamAndBody() {
     URI uri = UriComponentsBuilder
             .fromUriString("http://localhost:9090")
             .path("/api/v1/crud-api")
             .encode()
             .build()
             .toUri();

     MemberDto memberDto = new MemberDto();
     memberDto.setName("flature!!");
     memberDto.setEmail("flature@gmail.com");
     memberDto.setOrganization("Around Hub Studio");

     RestTemplate restTemplate = new RestTemplate();

     return restTemplate.postForEntity(uri, memberDto,
             MemberDto.class); // 외부 api에 post 요청을 보내되 request Body로 memeberDto를 첨부하여 호출함 응답값은 MemberDto의 형태로받을거라는 명시
 }
//WebClient get
public String getNameWithPathVariable() {
     WebClient webClient = WebClient.create("http://localhost:9090"); -> 외부 api서버

     ResponseEntity<String> responseEntity = webClient.get()
         .uri("/api/v1/crud-api/{name}", "Flature") //-> 외부 api의 end포인트 와 pathVariable
         .retrieve()
         .toEntity(String.class)
         .block();

     return responseEntity.getBody();
 }
//webClient post
public ResponseEntity<MemberDto> postWithParamAndBody() {
        WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:9090")
            .defaultHeader(HttpHeaders.CONTENT_TYPE,  MediaType.APPLICATION_JSON_VALUE)// 기본 해더
            .build();

        MemberDto memberDTO = new MemberDto();
        memberDTO.setName("flature!!");
        memberDTO.setEmail("flature@gmail.com");
        memberDTO.setOrganization("Around Hub Studio");

        return webClient.post().uri(uriBuilder -> uriBuilder.path("/api/v1/crud-api").build())
            .bodyValue(memberDTO) // body를 통해 memberDto라는 파라미터를 넘김
            .retrieve()
            .toEntity(MemberDto.class)
            .block();
    }

정리

  • 스프링(스프링부트) 에서는 서버간 통신이 필요할때 스프링 자체에서 webClient와, RestTemplate를 제공하며 이를 통해 보다 편하게 서버간 통신을 구현할 수 있다.
  • RestTemplate는 depreacate 되었으나 기존 사용하던 프로젝트를 유지보수할 일이생길 수 있기에 어느정도 이해가 필요하다.
  • 위 글에서는 Blocking 한방법에 대해서 예제코드로 다루었다. webClient의 핵심인 비동기 방법과 RestTemplate 의 차이에 대해서는 다른글을통해 정리해야 할것같다. 😅
profile
tistory로 이사갑니다. :) https://feelingxd.tistory.com/

0개의 댓글