Spring/SpringCloudGateway

[SpringCloudGateway] ExceptionHandler

joo_k 2025. 6. 2. 23:04
반응형

MVC에서는 @ControllerAdvice를 통해서 에러를 핸들링한다.

Reactive에서는 다른 방식의 에러 핸들링 방법을 제공하는데 이를 Spring Cloud Gateway에 어떻게 적용하는지 알아보자.

 

ErrorWebExceptionHandler

Spring cloud gateway에서 발생한 모든 예외를 처리할 수 있는 전역 핸들러 인터페이스
WebFlux의 최하단 필터 체인에서 동작하며, DispatcherHandler가 처리하지 못한 에러를 이 핸들러가 최종적으로 받는다.
에러 내역 로그를 남기거가, 에러 응답을 포맷팅 해서 주는 작업 등을 할 수 있다. 
ErrorWebExceptionHandler에서는 handle을 구현해야 하는데 
handle 메서드에서는 exchange에 response를 직접 핸들링해야 한다.

이러한 과정을 좀 더 간결하게 만들어준 추상클래스 AbstractErrorWebExceptionHandler가 존재
ErrorAttributes, ServerResponse 등을 활용하여 에러 내역 추출이나 응답 생성에 도움을 주는 기능을 제공 

 

예시-ErrorWebExceptionHandler 

exchange를 직접 핸들링 필요 

@Component
@Order(-2)
public class CustomGlobalExceptionHandler implements ErrorWebExceptionHandler {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();

        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        Map<String, Object> errorDetails = Map.of(
            "code", "500",
            "message", ex.getMessage()
        );

        byte[] errorBytes;
        try {
            errorBytes = objectMapper.writeValueAsBytes(errorDetails);
        } catch (JsonProcessingException e) {
            errorBytes = "{\"code\": \"500\", \"message\": \"JSON Error\"}".getBytes(StandardCharsets.UTF_8);
        }

        DataBuffer buffer = response.bufferFactory().wrap(errorBytes);
        return response.writeWith(Mono.just(buffer));
    }
}

 

예시-AbstractErrorWebExceptionHandler

ErrorAttributes를 통해서 에러내용 추출 편리
ServerResponse를 사용해 간편하게 응답 생성 가능 
getRoutingFunction() 메서드는 예외가 발생했을 때 어떻게 처리할지 라우터를 정의하는 곳

public class CustomExceptionHandler extends AbstractErrorWebExceptionHandler {

    public CustomExceptionHandler(ErrorAttributes errorAttributes,
                                   ApplicationContext applicationContext,
                                   ServerCodecConfigurer configurer) {
        super(errorAttributes, new DefaultErrorAttributes(), applicationContext);
        setMessageReaders(configurer.getReaders());
        setMessageWriters(configurer.getWriters());
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), request -> {
            Map<String, Object> errorProps = getErrorAttributes(request, ErrorAttributeOptions.defaults());
            return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(errorProps);
        });
    }
}

 

Spring-boot의 기본 제공하는 auto-config를 확인해 보면 
ErrorWebExceptionHandler 관련 빈이 등록되어있지 않다면 
DefaultErrorWebExceptionHandler를 제공한다. 

ErrorWebFluxAutoConfiguration.class

@Bean
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
@Order(-1)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,
		WebProperties webProperties, ObjectProvider<ViewResolver> viewResolvers,
		ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {
	DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
			webProperties.getResources(), this.serverProperties.getError(), applicationContext);
	exceptionHandler.setViewResolvers(viewResolvers.orderedStream().toList());
	exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
	exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
	return exceptionHandler;
}

 

DefaultErrorWebExceptionHandler의 코드를 살펴보면 
만약 custom으로 exceptionHandler를 작성하고 싶은 경우 참고할 수 있을 것이다. 

Map<String, Object> errorAttributes를 가져오는 코드에서는 기본적으로는 DefaultErrorAttributes가 사용되어 
timestamp, path, status, error, requestId 만 포함되고 

message, exception, trace를 추가 설정으로 넣을 수 있다. 

server:
  error:
    include-exception: true
    include-message: always
    include-stacktrace: always
    include-binding-errors: always
반응형