Dev./Spring

[Spring] Filter

limitation01 2025. 11. 13. 08:17

Filter란?

Filter는 Spring 전용 기술이 아닌, 자바 서블릿(Java Servlet) 표준 스펙에서 제공하는 기능이다.
즉, Spring이 등장하기 전부터 톰캣(Tomcat), 제티(Jetty) 같은 서블릿 컨테이너 수준에서
요청과 응답을 가장 먼저 가로채어 처리하는 구조다.

Spring Boot는 이런 Servlet Filter를 내부적으로 자동 등록하고
Spring 애플리케이션의 맨 앞단(DispatcherServlet 이전) 에서 실행되게 해준다.

동작 구조

출처:https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/

클라이언트 요청
    ↓
[Filter] ─── Servlet 레벨 (보안, 인코딩, 인증)
    ↓
DispatcherServlet ─── Spring MVC 진입점
    ↓
[Interceptor] ─── Spring 레벨 (로깅, 권한 검증, 비즈니스 전 전처리)
    ↓
Controller
    ↓
Service / Repository
    ↓
응답 반환

Filter는 언제 쓰나?

 

Spring에 진입하기 전, 모든 요청을 한 번 걸러야 할 때 

대표적인 예:

  • 로그인/권한 인증 (세션, JWT 등)
  • 요청 인코딩, 로깅
  •  XSS, CORS 같은 보안 필터
  • API Rate Limiting (요청 제한)
  • 요청 전체에 공통 적용되는 정책

예시 코드로 Filter를 파헤쳐보자

@Component
public class LoginFilter implements Filter {
    private static final List<String> EXCLUDED_URLS = List.of("/signup", "/signin");

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

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String requestURI = httpRequest.getRequestURI();

        // 로그인/회원가입 요청은 필터 통과
        if (isExcludedPath(requestURI)) {
            chain.doFilter(request, response);
            return;
        }

        // 세션 검증
        HttpSession session = httpRequest.getSession(false);
        if (session == null || session.getAttribute("userId") == null) {
            // 인증 실패 시 JSON 형태로 응답 반환
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.setContentType("application/json;charset=UTF-8");
            httpResponse.getWriter().write("""
                {
                    "code": "UNAUTHORIZED",
                    "message": "로그인이 필요합니다."
                }
            """);
            return;
        }

        // 인증 성공 → 다음 필터나 컨트롤러로 진행
        chain.doFilter(request, response);
    }

    private boolean isExcludedPath(String uri) {
        return EXCLUDED_URLS.stream().anyMatch(uri::startsWith);
    }
}
  1. @Component
    • Spring Boot는 @Component가 붙은 Filter 클래스를 자동으로 감지하 Servlet Filter Chain에 등록
    • 톰캣의 요청이 들어오면 DispatcherServlet 앞단에서 가장 먼저 실행
  2. private static final List<String> EXCLUDED_URLS = List.of("/signup", "/signin");
    • 회원가입, 로그인은 비로그인 상태에서 접근해야 하기 때문에 두가지 경로는 필터 검증에서 제외시키기 위함
  3. doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    • Filter의 핵심 메서드
    • Request가 들어올 때 실행되며 Filter Chain을 타고 다음 필터 또는 컨트롤러로 흐름을 넘길지 결정하는 역할
  4. HttpServletRequest, HttpServletResponse로 캐스팅
    • Filter 인터페이스의 매개변수는 Servlet의 가장 추상적인 타입이기 때문에 Spring의 getRequestURI, getSession, getParameter과 같은 매서드를 사용하려면 HttpServletRequest로 다운캐스팅을해야함
  5. String requestURI = httpRequest.getRequestURI();
    • 현재 요청된 url(/schedules)을 추출해 로그인 검증이 필요한 요청인지를 판단
  6. if (isExcludedPath(requestURI))
    • 예외 경로 감지시 통과시켜 다음 필터나 컨트롤러로 넘김
  7. HttpSession session = httpRequest.getSession(false);
    • 세션으로 로그인 검증하는 핵심 코드
    • getSession(true) → 세션이 없으면 새로 만듬
    • getSession(false) → 세션이 없으면 null을 반환
  8. if (session == null || session.getAttribute("userId") == null)
    • "userId"는 로그인시 세션에 저장한 값
    • 조건이 true면 현재 요청은 로그인 상태가 아님
  9. 인증 실패 시 응답 처리
    • SC_UNAUTHORIZED : 401
    • JSON 형태로 오류 메세지 작성 : @ControllerAdvice 예외 핸들러가 아직 개입할 수 없기 때문
  10. chain.doFilter(request, response)
    • 이게 실행되면 현재 필터는 통과되고 다음 필터 → DispatcherServlet → Controller 순으로 이동한다.