🌱 Spring Cloud - MSA간 통신 (RestTemplate, Feign Client) 🌱

Kim Dae Hyun·2021년 6월 26일
0

Spring-Cloud

목록 보기
4/4
post-thumbnail

Github 소스코드

🔎 이런거 해볼 거에요.

  • 두 개 MSA User serviceOrder serviceEureka Service에 등록되어 있는 상태이고, Spring cloud gateway에 의해 라우팅된다.
  • User service에서 Order service로 주문정보를 얻어오기 위해 요청을 보내고자 한다.
  • RestTemplate를 이용하는 방법과 FeignClient를 이용하는 방법 두 가지를 해보자.

RestTemplate

1. 가장 먼저 RestTemplate를 사용하기 위해 해당 모듈을 Bean으로 등록해주어야 한다.

  • 간단한게 springboot의 main class에 Bean을 등록한다.
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

2. 호출하고자 하는 service의 API를 확인해보자.

  • 내부 동작이 아닌 주소 매핑정보와 리턴타입을 확인한다.
    @GetMapping("/orders/{userId}")
    public ResponseEntity<List<ResponseOrder>> getOrderByOrderId(@PathVariable("userId") String userId) {
        List<OrderDto> findOrders = orderService.getOrderByUserId(userId);
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        List<ResponseOrder> result = new ArrayList<>();
        findOrders.forEach( o -> {
            result.add(mapper.map(o, ResponseOrder.class));
        });
        return ResponseEntity.status(HttpStatus.OK).body(result);
    }
}
  • Order service가 API gateway service에 등록된 설정을 보자.
- id: order-service
  uri: lb://ORDER-SERVICE
  predicates:
    Path=/order-service/**

endpoint는 /orders/{userId} 이고 api gateway에 order-service로 등록되어 있으니 호출해야 하는 API의 uri는 http://127.0.0.1:8000/order-service/orders/{userId} 가 된다.

3. User service의 service계층에서 RestTemplate을 통해 호출해보자.

  • RestTemplate는 앞서 Bean으로 등록했으므로 주입받아 사용한다. (저는 @RequiredArgsConstructor + final 로 받았어요.)
    private final RestTemplate restTemplate;
  • RestTemplate의 exchange메서드르 이용한다.
    • exchange(url, HttpMethod, requestEntity, responseType)
    • body는 없으므로 requestEntity는 null로 하고 응답받을 타입은 ParameterizedTypeReference로 지정한다.
  • new ParameterizedTypeReference에 알맞은 DTO를 정의하여 받아주면 정상적으로 다른 MSA의 API를 호출할 수 있다.
String url = String.format("http://127.0.0.1:8000/order-service/orders/%s", userId);
ResponseEntity<List<ResponseOrder>> response = restTemplate.exchange(url, HttpMethod.GET, null,
                new ParameterizedTypeReference<List<ResponseOrder>>() {
                });
List<ResponseOrder> orders = response.getBody();

FeignClient

1. FeignClient를 위한 interface를 생성한다.

  • @FeignClient에는 호출하고자 하는 MSA이름을 지정한다.
  • 여기서 MSA의 이름은 Eureka server에 인스턴스로 등록시 사용된 이름이다.
  • 이렇게 FeignClient로 설정해주면 마치 자신의 API인 것처럼 정의가 가능하다.
  • @GetMapping 쪽을 보면 기존에 Controller에서 endpoint를 지정하는 것과 거의 동일한 것을 알 수 있다. 다만 세부 구현 내용은 필요로 하지 않는다.
@FeignClient(name = "order-service")
public interface OrderServiceFeignClient {

    @GetMapping("/order-service/orders/{userId}")
    List<ResponseOrder> getOrders(@PathVariable String userId);
    
}

2. Service계층에서 정의한 FeignClient로 Order-Service를 호출해보자.

  • 정의한 FeignClient을 주입받는다. 이전과 동일하게 @RequiredArgsConstructor + final로 주입받는
    다.
private final OrderServiceFeignClient orderServiceFeignClient; 
  • 주입받은 객체로 정의한 API를 호출한다.
  • FeignClient에서 제공하는 에러처리 기법이 있어 이후 사용해 볼 것이다. 일단 try-catch문으로 예외를 처리한다.
  • RestTemplate보다는 FeignClient가 코드도 분리되고 훨씬 더 간결하여 좋다는 느낌이 든다.
List<ResponseOrder> orders  = null;
try {
	orders = orderServiceFeignClient.getOrders(userId);
            
} catch (FeignException e) {
	log.error(e.getMessage());
}

3. FeignClient에서 ErrorDecoder를 이용한 예외처리

  • ErrorDecoder를 이용하여 예외를 모아 관리할 수 있다.
  • ErrorDecoder를 구현하는 클래스를 생성하고 아래와 같은 리턴값, 매개변수를 갖는 decode 메서드를 오버라이딩한다.
  • 첫번째 매개변수인 method에는 FeignClient를 통해 호출한 함수의 이름을 포함한다. 때문에 에러 케이스마다 호출한 함수의 명에 따라 구분하여 처리가 가능하다.
  • 리턴값은 임의대로 Exception객체를 적절하게 구성하여 리턴한다.
@Component
public class FeignClientErrorDecoder implements ErrorDecoder {

    @Override
    public Exception decode(String method, Response response) {

        switch (response.status()) {
            case 404: 
                if(method.contains("getOrders")) {
                    return new ResponseStatusException(HttpStatus.valueOf(response.status()), "User's order is empty.");
                }
                break;
            default:
                return new Exception(response.reason());
        }
        return null;
    }
}
  • ErrorDecoder를 구현하면 try-catch 없이 아래와 같이 깔끔하게 다른 MSA의 API를 호출할 수 있다.
List<ResponseOrder> orders = orderServiceFeignClient.getOrders(userId);
profile
좀 더 천천히 까먹기 위해 기록합니다. 🧐

1개의 댓글

comment-user-thumbnail
2022년 10월 3일

진짜 배울게 많네요.

답글 달기