ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • feign client ErrorDecoder 분석
    Spring Framework 2023. 8. 25. 00:01

    개요

    feignClient를 활용하다 보면 에러 핸들링을 해줘야 합니다.

    만약 이러한 부분을 신경쓰지 않는다면 Controller에서 500 에러를 반환하게 되고 장애로 인지할 수 있습니다.

    feignClient는 어떻게 에러를 헨들링할까요?

     

    @ExceptionHandler로 처리

    feign.FeignException$BadRequest: [400 Bad Request]

    에러상황을 처리하다 보면 다음과 같은 메시지를 볼 수 있습니다.

    FeignException이 발생했고, 여기에서 400 Bad request가 발생했습니다.

     

    그러면Controller 쪽에서 해당 예외를 잡아서 처리하면 되지 않을까요?

    모든 예외를 FeignException으로 처리한다면 400과 500 에러의 판단을 Controller에서 진행하게 됩니다.

    외부를 호출하는 Client로직과 세부사항이 Controller까지 침범하게 되어 나중에 FeignClient 이외에 다른 기술을 적용하는 경우 같이 변경이 일어납니다.

     

    Feign Client ErrorDecoder 분석

    public interface ErrorDecoder {
    
        @Override
        public Exception decode(String methodKey, Response response) {
          FeignException exception = errorStatus(methodKey, response);
          Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));
          if (retryAfter != null) {
            return new RetryableException(
                response.status(),
                exception.getMessage(),
                response.request().httpMethod(),
                exception,
                retryAfter,
                response.request());
          }
          return exception;
        }
    
    }

    ErrorDecoder 인터페이스에서 decode 함수를 재정의하고 있습니다.

    여기서 errorStatus함수가 FeignException을 반환합니다.

     

    ErrorStatus 함수 분석

    private static FeignException errorStatus(int status,
                                                String message,
                                                Request request,
                                                byte[] body,
                                                Map<String, Collection<String>> headers) {
        if (isClientError(status)) {
          return clientErrorStatus(status, message, request, body, headers);
        }
        if (isServerError(status)) {
          return serverErrorStatus(status, message, request, body, headers);
        }
        return new FeignException(status, message, request, body, headers);
      }

    클라이언트에러인지, 서버에러인지 판단하고 그 외인 경우에는 FeignException을 반환합니다.

    isClientError함수는 HTTP 상태코드가 400~499인지 확인합니다.

    isServerError함수는 HTTP 상태코드가 500~599인지 확인합니다.

     

    ClientErrorStatus 함수 분석

    private static FeignClientException clientErrorStatus(int status,
                                                            String message,
                                                            Request request,
                                                            byte[] body,
                                                            Map<String, Collection<String>> headers) {
        switch (status) {
          case 400:
            return new BadRequest(message, request, body, headers);
          case 401:
            return new Unauthorized(message, request, body, headers);
          case 403:
            return new Forbidden(message, request, body, headers);
          case 404:
            return new NotFound(message, request, body, headers);
          case 405:
            return new MethodNotAllowed(message, request, body, headers);
          case 406:
            return new NotAcceptable(message, request, body, headers);
          case 409:
            return new Conflict(message, request, body, headers);
          case 410:
            return new Gone(message, request, body, headers);
          case 415:
            return new UnsupportedMediaType(message, request, body, headers);
          case 429:
            return new TooManyRequests(message, request, body, headers);
          case 422:
            return new UnprocessableEntity(message, request, body, headers);
          default:
            return new FeignClientException(status, message, request, body, headers);
        }
      }

    상태코드에 따라 swtich case 구문으로 다른 예외를 반환합니다.

    이때의 예외는 모두 FeignClientException을 상속받고 있는데 BadRequest를 하나의 예지로 살펴보고자 합니다.

     

     

    BadRequest Class 분석

    public static class BadRequest extends FeignClientException {
        public BadRequest(String message, Request request, byte[] body,
            Map<String, Collection<String>> headers) {
          super(400, message, request, body, headers);
        }
      }

    BadRequest 클래스로 들어가 보면 FeignClientException을 상속받고 있습니다.

    즉, 모든 4xx 상태코드들은 FeignClientException을 던집니다.

    비슷하게 모든 5xx 상태코드들은 FeignServerException을 던집니다.

     

     

    Feign을 처리하는 곳에서 재정의한 예외를 던지기

    try {
        callFeignClient
        //ClientException이 발생하면 잡아서 다른 예외를 던진다.
        //만약 그외의 에러가 발생하는 경우는 그냥 Exception을 던져서 우리가 인지할 수 있도록 500예외를 만든다.
    } catch (e: FeignClientException) {
        logger().warn { e.localizedMessage }
        throw CustomException("client를 호출하다가 4xx에러가 발생했습니다. 올바른 요청을 보냈는지 확인해보세요")
    }

     

    이후에 CustomException을 Controller에서 처리하면 될 것 같습니다.

    하지만 모든 feign에 try-catch 로직이 공통적으로 등장하는 것 같다면 ErrorDecoder를 상속받아 구현하는 방법을 사용할 수 있습니다.

     

     

    ErrorDecoder 상속받아 구현

      class IllegalArgumentExceptionOn404Decoder implements ErrorDecoder {
     
        @Override
        public Exception decode(String methodKey, Response response) {
          if (response.status() == 400)
            throw new IllegalArgumentException("bad zone name");
          return new ErrorDecoder.Default().decode(methodKey, response);
        }
     
      }

    Javadocs에서 설명하는 방식입니다.

    다음과 같은 방식으로 ErrorDecoder를 재정의할 수 있습니다.

    댓글

Designed by Tistory.