Post

feign client, feign configuration

목적

연동된 구간별 API http 응답 코드 별로 예외 메시지 처리를 하기 위함.

ex) 주문 접수 할 때, 연동하는 API가 5가지 일 때, 5가지 마다 각각 다른 예외 처리르 하기 위함 (화면에 메시지 안내 포함)

주말에 한번 Feign Client로 예외 처리 하는 방식을 고민해보았는데요,

(오픈 Feign 뉴비라서 제가 잘 몰라요)

http status 코드 파싱해서 각 구간별 예외 메시지를 처리할까 고민중입니다.

예상 되는 조합 FallbackFactory + ControllerAdvice 조합

혹시 이 방법 보다 더 나은 방법이 있으시면 공유 부탁드립니다.

​

예제코드

​

public class CouponErrorDecoder implements ErrorDecoder {

  private final ObjectMapper om = new ObjectMapper();

  /**
   * 발생된 예외를 catch 해서 custom하게 예외를 처리합니다.
   *
   * @param methodKey 호출 메서드
   * @param response  응답 데이터
   * @return custom Exception
   */
  @SneakyThrows
  @Override
  public Exception decode(
      final String methodKey,
      final Response response) {
    final Request request = response.request();

    log.info("{} 요청이 성공하지 못했습니다. status : {}, body : {}", methodKey, response.status(),
        response.body());

    FileBeatLogger.info(new HashMap<>() {
      {
        put("action", "coupon-request-fail");
        put("url", request.url());
        put("http status code", response.status());
        put("error message", response.body()
            .toString());
      }
    });

    if (Request.HttpMethod.GET == request.httpMethod() && request.url()
        .contains("restore")) {
      cancelError(response);
    }

    final var error = om.readValue(response.body()
        .toString(), CouponErrorMessage.class);

    log.info("{}", error);
    switch (response.status()) {
      case 400:
        return new CouponUseException(5250, error.getMessage());
      case 422:
      case 404:
        return new CouponUseException(5251, error.getMessage());
      default:
        return new CouponUseException(5252, "쿠폰 API 호출 중 오류가 발생되었습니다.");
    }
  }

  /**
   * 오류 메시지 파싱 Class.
   */
  @ToString
  @Data
  static class CouponErrorMessage {

    private String message;
    private boolean success;

  }

  /**
   * 쿠폰 사용취소 exception을 핸들링합니다.
   */
  private Exception cancelError(@NonNull final Response response) {
    final Request request = response.request();
    final int status = response.status();

    if (status == 404) {
      final String message =
          String.format(":alert: :alert: :alert: 사용되지 않은 쿠폰은 복원할 수 없습니다. url: [%s]",
              request.url());
      log.error(message);
      SlackNotifier.notify(message);
      return new CouponCancelException(5253, "사용되지 않은 쿠폰은 취소 할 수 없습니다.");
    }

    final String message =
        String.format(":alert: :alert: :alert: 쿠폰취소 요청 feign 요청이 실패하였습니다. url: [%s]",
            request.url());
    log.error(message);
    SlackNotifier.notify(message);

    return FeignException.errorStatus(request.httpMethod()
        .name(), response);
  }

}
/**
 * 쿠폰 Feign Configuration.
 */
public class CouponFeignConfiguration {

  /**
   * 쿠폰 ErrorDecoder Bean을 등록합니다.
   *
   * @return ErrorDecoder
   */
  @Bean
  public ErrorDecoder decoder() {
    return new CouponErrorDecoder();
  }

  /**
   * 재시도 횟수 설정.
   *

   * 최소 기간 0.1초, 최대 0.5초, 재시도 횟수 3회
   *

   */
  @Bean
  Retryer retryer() {
    return new Retryer.Default(100, 500, 3);
  }

}
/**
 * 쿠폰 feign Client.
 */
@FeignClient(name = "coupon-api-service", fallback = CouponFeignClientFallback.class,
    qualifier = "CouponFeignClient", configuration = CouponFeignConfiguration.class)
public interface CouponFeignClient {

  /**
   * 쿠폰 사용 처리 요청입니다.
   *
   * @param memberNo   회원번호
   * @param couponCode 쿠폰코드
   * @param orderNo    주문번호
   * @return 쿠폰 사용 처리 응답
   */
  @GetMapping(path = "/v2/order/coupon/{memberNo}/use/{couponCode}/to/{orderNo}")
  ApiResponseModel use(
      @PathVariable("memberNo") Long memberNo,
      @PathVariable("couponCode") Long couponCode,
      @PathVariable("orderNo") Long orderNo);

  /**
   * 쿠폰 사용을 취소 합니다.
   *
   * @param memberNo   회원번호
   * @param couponCode 쿠폰번호
   * @return 쿠폰 사용 취소 응답
   */
  @GetMapping("/v1/crm/{memberNo}/restore/{couponCode}")
  ApiResponseModel cancel(
      @PathVariable("memberNo") Long memberNo,
      @PathVariable("couponCode") Long couponCode);

}

​

This article is licensed under CC BY 4.0 by the author.