[SPRING] Filter는 왜 GlobalExceptionHandler에서 잡지 못할까?

림민지·2025년 4월 14일

Today I Learn

목록 보기
44/62

Filter에서는 왜 GlobalExceptionHandler가 동작하지 않을까?
Spring Boot로 로그인 기능을 구현하면서 예상치 못한 문제를 만났다.
로그인을 하지 않은 사용자가 접근하면, 필터에서 예외를 던져 GlobalExceptionHandler에서 처리되길 기대했는데... 실제로는 그게 작동하지 않는것이다ㅠㅠㅠ

그래서 이 글에서는 다음과 같은 흐름으로 문제를 정리해보려 합니다:

  1. GlobalExceptionHandler란?

  2. Filter에서 예외를 던졌는데 왜 잡히지 않을까?

  3. 예외를 잡기 위한 대안 방법

GlobalExceptionHandler란?

Spring에서는 @ControllerAdvice와 @ExceptionHandler를 이용해 전역 예외 처리를 할 수 있습니다.

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(CustomUnauthorizedException.class)
    public ResponseEntity<String> handleUnauthorized(CustomUnauthorizedException ex) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ex.getMessage());
    }
}

컨트롤러나 서비스에서 예외가 발생하면 이 핸들러가 동작해 클린한 에러 응답을 보낼 수 있죠.
하지만 중요한 것은,,
이 방식은 DispatcherServlet 이후의 흐름에서만 동작한다는 것!

❗ Filter에서 예외를 던지면 왜 GlobalExceptionHandler가 작동하지 않을까?

Filter는 Spring MVC의 DispatcherServlet보다 앞단에서 동작합니다.
즉, 컨트롤러 로직에 도달하기도 전에 실행되는 레벨!

Client → Filter → DispatcherServlet → Controller → Service

@ControllerAdvice가 동작하는 영역은 DispatcherServlet 이후!
➡️ Filter에서 발생한 예외는 GlobalExceptionHandler까지 도달하지 않음...

즉, 예외는 던졌지만, 그것을 잡을 애가 없었던 것!!!!

✅ 예외를 처리해보자

💡 방법 1: 필터 내부에서 직접 예외 응답을 작성

Filter에서 response에 직접 에러 응답을 작성하는 방법이 있다

public class AuthFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        HttpSession session = req.getSession(false);

        if (session == null || session.getAttribute("user") == null) {
            res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            res.setContentType("application/json;charset=UTF-8");
            res.getWriter().write("{\"message\": \"로그인이 필요합니다.\"}");
            return;
        }

        chain.doFilter(request, response);
    }
}

예외를 던지는 게 아니라, 직접 클라이언트에게 메시지를 작성하는 방식

💡 방법 2: Spring의 HandlerInterceptor 사용 고려

만약 @ControllerAdvice와 함께 예외 처리를 하고 싶다면,
Filter 대신 HandlerInterceptor 를 사용하는 것도 좋은 대안입니다.

Interceptor는 DispatcherServlet 이후에 동작하기 때문에 GlobalExceptionHandler에서 예외를 잡을 수 있다!~

@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HttpSession session = request.getSession(false);

        if (session == null || session.getAttribute("user") == null) {
            throw new CustomUnauthorizedException("로그인이 필요합니다.");
        }

        return true;
    }
}

그리고 이렇게 던진 예외는 GlobalExceptionHandler에서 정상적으로 처리됩니다.

✍️ 정리

  1. Filter는 DispatcherServlet보다 앞단이라 GlobalExceptionHandler가 동작하지 않는다.

  2. Filter에서는 예외 대신 response를 직접 작성해야 한다.

  3. GlobalExceptionHandler를 활용하고 싶다면 HandlerInterceptor를 고려하자.

profile
@lim_128

0개의 댓글