Spring Boot에서 Filter란 무엇인지, 어떻게 구현하고 활용하는지 정리합니다.
Filter는 Web 애플리케이션(서블릿 컨테이너)에서 관리되는 영역으로,
클라이언트로부터 오는 요청(Request) 과 응답(Response) 에 대해 최초/최종 단계에 위치합니다.
이를 통해 다음과 같은 작업을 수행할 수 있습니다.
💡 핵심 장점: 인증·인가 로직을 비즈니스 로직과 분리하여 관리할 수 있습니다.
Filter는 단 하나만 존재하는 것이 아니라,
여러 개가 Chain 형태로 연결되어 순서대로 처리됩니다.
Client
↓
Filter₀ ← @Order(1)
↓
Filter₁ ← @Order(2)
↓
Filter₂
↓
Servlet (DispatcherServlet)
각 Filter는
chain.doFilter(request, response)를 호출하여 다음 Filter로 넘겨줍니다.
이 메서드를 호출하지 않으면 요청이 더 이상 진행되지 않습니다.
jakarta.servlet.Filter 인터페이스를 구현하면 됩니다.
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// ✅ 전처리 (요청이 Servlet에 도달하기 전)
// ... 요청 관련 로직
chain.doFilter(request, response); // 다음 Filter 또는 Servlet으로 이동
// ✅ 후처리 (Servlet 처리 완료 후, 응답이 클라이언트로 가기 전)
// ... 응답 관련 로직
}
}
chain.doFilter()기준으로 위는 전처리, 아래는 후처리 영역입니다.
package com.mystudy.springauth.filter;
...
@Slf4j(topic = "LoggingFilter")
@Component // Spring Bean으로 등록 → 자동으로 Filter로 인식됨
@Order(1) // 필터 순서 지정 (낮을수록 먼저 실행)
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 전처리: HttpServletRequest로 캐스팅하여 URL 추출
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
log.info("요청 URL: {}", url);
chain.doFilter(request, response); // 다음 Filter로 이동
// 후처리: 비즈니스 로직이 모두 완료된 후 실행됨
log.info("비즈니스 로직 완료");
}
}
포인트:
log.info("비즈니스 로직 완료")는chain.doFilter()이후이므로,
Servlet 처리가 끝난 뒤 응답이 클라이언트로 가기 직전에 실행됩니다.
@Slf4j(topic = "AuthFilter")
@Component
@Order(2) // LoggingFilter(1) 다음에 실행
public class AuthFilter implements Filter {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
// 생성자 주입 (AuthFilter는 JwtUtil과 UserRepository에 의존)
public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
// ✅ 인증이 필요 없는 URL은 바로 통과
if (StringUtils.hasText(url) &&
(url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js"))) {
// 회원가입, 로그인 관련 API 및 정적 리소스는 인증 없이 통과
chain.doFilter(request, response);
} else {
// ✅ 그 외 모든 요청은 JWT 인증 처리
String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);
if (StringUtils.hasText(tokenValue)) { // 토큰이 존재하면 검증 시작
// 1. "Bearer " 접두어 제거
String token = jwtUtil.substringToken(tokenValue);
// 2. 토큰 유효성 검증
if (!jwtUtil.validateToken(token)) {
throw new IllegalArgumentException("Token Error");
}
// 3. 토큰에서 사용자 정보 추출
Claims info = jwtUtil.getUserInfoFromToken(token);
// 4. DB에서 사용자 조회
User user = userRepository.findByUsername(info.getSubject())
.orElseThrow(() -> new NullPointerException("Not Found User"));
// 5. request에 사용자 정보를 담아 다음 단계로 전달
request.setAttribute("user", user);
chain.doFilter(request, response);
} else {
throw new IllegalArgumentException("Not Found Token");
}
}
}
}
AuthFilter 동작 흐름 요약:
요청 수신
↓
URL 확인
├─ /api/user, /css, /js → 인증 없이 통과
└─ 그 외 URL
↓
쿠키에서 JWT 토큰 추출
↓
토큰 존재 여부 확인
├─ 없음 → IllegalArgumentException ("Not Found Token")
└─ 있음
↓
토큰 유효성 검증
├─ 실패 → IllegalArgumentException ("Token Error")
└─ 성공
↓
토큰에서 username 추출 → DB 조회
↓
request에 User 객체 저장 → 다음 Filter로 이동
Filter로 등록하고 싶지 않을 때는 @Component 어노테이션을 주석 처리하면 됩니다.
@Slf4j(topic = "LoggingFilter")
// @Component ← 주석 처리하면 Bean으로 등록되지 않아 Filter로 동작하지 않음
@Order(1)
public class LoggingFilter implements Filter {
...
}
| 어노테이션 | 설명 |
|---|---|
@Component | Spring Bean으로 등록. Filter 인터페이스 구현체에 붙이면 자동으로 필터로 동작 |
@Order(n) | 필터 실행 순서 지정. 숫자가 낮을수록 먼저 실행됨 |
| Filter | Interceptor | |
|---|---|---|
| 관리 주체 | 서블릿 컨테이너 (Tomcat 등) | Spring Context |
| 적용 위치 | DispatcherServlet 이전 | DispatcherServlet 이후, Controller 이전 |
| 주요 용도 | 인증/인가, 로깅, 인코딩 설정 | 공통 비즈니스 로직, 권한 체크 |
| Spring Bean 접근 | ⚠️ 제한적 (Spring DI는 가능) | ✅ 자유롭게 접근 가능 |
인증·인가처럼 Spring 전역에 걸쳐 처리해야 하는 로직은 Filter,
Controller 전후 비즈니스 레벨의 처리는 Interceptor가 적합합니다.
request.setAttribute()를 활용한 데이터 전달AuthFilter에서 인증된 User 객체를 request에 담아두면,
이후 Controller에서 꺼내어 사용할 수 있습니다.
// Filter에서 저장
request.setAttribute("user", user);
// Controller에서 꺼내기
User user = (User) request.getAttribute("user");