[Spring] FeignClient 에러 리스폰스 버블링
❗️ ISSUE
Spring Feign Client
를 사용하면 일반적으로 다음과 같은 코드를 작성합니다.
@FeignClient(name = "user-service", path = "/api/user")
public interface UserFeignClient {
@PostMapping(value = "/login")
ResponseEntity<LoginResponseDto> login(@RequestBody LoginRequestDto loginRequest);
@PutMapping(value = "/access")
ResponseEntity<Void> updateUserAccessAt(@RequestBody UpdateAccessAtRequestDto updateAccessAtRequest);
}
요청이 성공적으로 처리된 경우에는 크게 문제가 되지 않지만, Feign Server에서 처리가 실패된 경우에는 문제가 발생하게 됩니다.
우리 서비스에서는 에러 발생 시 exception advice를 통해 에러 타입에 맞는 failure response entity를 반환하는 구조입니다.
때문에 Feign Server에서 exception이 발생하면 Feign Client에서 해당 exception을 응답해주지 못하는 이슈가 발생했습니다.
Feign Client의 경우 리스폰스 타입을 특정 DTO로 지정해두었고, failure response는 data가 null이기 때문에 아래와 같은 에러가 발생하는 것입니다.
또한 상위 메소드에서 getter을 쓰기 때문에 와일드카드를 쓸 수도 없는 상황이었습니다.
✅ SOLUTION
이를 해결하기 위해 FeignErrorDecoder
를 정의하여 Feign Server에서 실패 응답이 전달되면 Feign Client에서 실패 데이터를 파싱하여 다시 exception을 발생시켜 실패 응답을 내려주도록 처리했습니다.
FeignClientConfig
를 생성해FeignErrorDecoder
을 bean 등록하여 사용
// FeignClientConfig.java
public class FeignClientConfig {
@Bean
public ErrorDecoder errorDecoder() {
return new FeignErrorDecoder();
}
}
// FeignErrorDecoder.java
public class FeignErrorDecoder implements ErrorDecoder {
private static final String LOGIN_PATH = "/api/user/login";
@SneakyThrows
@Override
public Exception decode(String methodKey, Response response) {
Reader reader = response.body().asReader(StandardCharsets.UTF_8);
String errorResult = IOUtils.toString(reader);
String path = response.request().url();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
ResponseEntity errorResponse = objectMapper.readValue(errorResult, ResponseEntity.class);
int status = response.status();
if (StringUtils.endsWithIgnoreCase(path, LOGIN_PATH) && isLoginError(errorResponse.getResponseCode())) {
status = HttpStatus.OK.value();
}
return new FeignClientException(status, errorResponse.getResponseCode(), errorResponse.getMessage());
}
// User Server에서 유효한 failure가 발생한 경우 (존재하지 않는 계정, 비밀번호 오류 등)
private boolean isLoginError(String responseCode) {
String code = responseCode.substring(2);
String emailErrorCode = ResponseType.USER_NOT_EXIST_EMAIL.getCode();
String accountNameErrorCode = ResponseType.USER_NOT_EXIST_ACCOUNT_NAME.getCode();
String passwordErrorCode = ResponseType.USER_NOT_MATCH_PASSWORD.getCode();
return emailErrorCode.equals(code) || accountNameErrorCode.equals(code) || passwordErrorCode.equals(code);
}
}
- FeignClient 통신에서 발생하는 에러를 처리하기 위한 exception 클래서 정의 및 핸들링
// ExceptionControllerAdvice.java
...
@ExceptionHandler(FeignClientException.class)
public org.springframework.http.ResponseEntity
<ResponseEntity<Void>> handleFeignClientException(FeignClientException exception) {
printLog(exception.getClass().getName(), exception.getMessage());
return org.springframework.http.ResponseEntity
.status(exception.getStatus())
.body(ResponseEntity.failureResponse(exception.getResponseCode(), exception.getMessage()));
}
...
- FeignClient에 configuration 추가
@FeignClient(name = "user-service", path = "/api/user", configuration = FeignClientConfig.class)
public interface UserFeignClient {
...
}
Author And Source
이 문제에 관하여([Spring] FeignClient 에러 리스폰스 버블링), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@_koiil/FeignClient-에러-리스폰스-버블링저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)