
안녕하세요, 오늘은 Filter 와 Intercepter 에 대해 정리해 보았습니다.
💡 Filter가 나오게된 이유는 공통 관심사(Cross Cutting Concern) 때문이다. ex) 요구사항에 따라서 로그인 한 유저만 특정 API(게시글 작성)를 사용할 수 있어야 한다면?공통 관심사
@Controller
@RequestMapping("/post")
public class PostController {
@PostMapping("/")
public void create(CreateRequestDto requestDto) {
// 로그인 여부 확인 로직
}
@PutMapping("/")
public void update(UpdateRequestDto requestDto) {
// 로그인 여부 확인 로직
}
@DeleteMapping("/")
public void delete(DeleteRequestDto requestDto) {
// 로그인 여부 확인 로직
}
}
위와같이 여러가지 로직에서 공통으로 관심이 있는 부분(로그인 등)을 공통 관심사 라고 한다.
// 로그인 여부 확인 로직 -> 공통 관심사 (인증 : 로그인)
공통 관심사는 Spring AOP를 활용할 수 있다.
Web과 관련된 공통 관심사는 Servlet Filter나 Spring Intercepter를 사용한다.
HttpServletRequest 객체를 제공하기 때문에 HTTP 정보나 URL 정보에 접근하기 쉽다.💡 Servlet Filter는 보안, 로깅, 인코딩, 인증/인가 등 다양한 작업을 처리하기 위해 사용된다. ex) 필터를 사용하여 모든 요청과 응답에 대한 로그를 저장할 수 있다.Servlet Filter
filterChain.doFilter(request, response); 다음 필터로 제어를 전달한다.




package jakarta.servlet;
import java.io.IOException;
public interface Filter {
// default
public default void init(FilterConfig filterConfig) {
}
public void doFilter(
SevletRequest request,
SevletResponse response,
FilterChain chain
) throws IOException, ServletException;
// default
public default void destroy() {
}
}
Filter Interface를 Implements하여 구현하고 등록하면
Servlet Container가 Filter를 Singleton 객체로 생성 및 관리한다.
doFilter()
doFilter() 메서드가 호출된다.doFilter() 내부에 필터 로직(공통 관심사 로직)을 구현하면 된다.doFilter() 를 호출해주고 하나의 필터의 doFilter()가 통과된다면doFilter() 를 호출한다.doFilter() 를 호출할 Filter가 없으면 Servlet이 호출된다.init()
destroy()
@Slf4j
public class CustomFilter implements Filter {
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
// Filter에서 수행할 Logic
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
try {
log.info("request URI={}", requestURI);
chain.doFilter(request, response); // chain 이 없으면 Servlet을 바로 호출
} catch(Exception e) {
throw e;
}
}
}
ServletRequest 는 기능이 별로 없다.HttpServletRequest 를 사용한다.(다운 캐스팅)@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean customFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new CustomFilter()); // Filter 등록
filterRegistrationBean.setOrder(1); // Filter 순서 설정
filterRegistrationBean.addUrlPatterns("/*"); // 전체 URL에 Filter 적용
return filterRegistrationBean;
}
}
GET /test 에 Mapping되는 Controller는 만들지 않았다.

filterRegistrationBean.addUrlPatterns("/*") doFilter() 메서드가 정상적으로 실행되면 Controller를 호출한다.Filter를 사용하려면 Filter Interface를 Implements 하여 구현해야 한다.
HTTP 요청이 오면 doFilter() 메서드가 호출된다.
chain.doFilter(request, response)
(순서를 설정해둔)다음 필터가 있으면 Filter를 호출한다.
다음 실행할 필터가 없으면 Servlet을 호출한다.
해당 메서드를 호출하지 않으면 다음 단계로 진행되지 않는다.
→ 다음 필터나 Servlet을 호출하지 않는다.
FilterRegistrationBean 사용setFilter()setOrder()addUrlPatterns()@WebFilter 어노테이션은 필터 등록이 가능하지만 순서 조절이 안되니 사용하지 않는다.💡 Servlet Filter를 사용하여 인증 처리를 하는 방법에 대해 알아보자.Servlet Filter - 인증
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.PatternMatchUtils;
@Slf4j
public class LoginFilter implements Filter {
// 인증을 하지 않아도될 URL Path 배열
private static final String[] whiteListURL = {"/", "/user/signup", "/login", "/logout"};
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
// 다양한 기능을 사용하기 위해 다운 캐스팅
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
// 다양한 기능을 사용하기 위해 다운 캐스팅
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
log.info("로그인 필터 로직 실행");
// 인증 필터 로직
// 로그인을 체크 해야하는 URL인지 검사
// whiteListURL이 아닌경우 true 반환
// 즉, 로그인 여부를 확인해야하는 URL인 경우
if (isLoginPath(requestURI)) {
// 로그인 확인 -> 로그인하면 session에 값이 저장되어 있다는 가정.
// 세션이 존재하면 가져온다. 세션이 없으면 session = null
HttpSession session = httpRequest.getSession(false);
// 로그인하지 않은 사용자인 경우
if (session == null || session.getAttribute("sessionKey값") == null) {
throw new RuntimeException("로그인 해주세요.");
}
// 로그인 성공 로직
}
// 1번경우 : whiteListURL에 등록된 URL 요청이면 바로 chain.doFilter()
// 2번경우 : 필터 로직 통과 후 다음 필터 호출 chain.doFilter()
// 다음 필터 없으면 Servlet -> Controller 호출
chain.doFilter(request, response);
} catch(Exception e) {
// 예외처리
}
}
// 로그인 여부를 확인하는 URL인지 체크하는 메서드
private boolean isLoginPath(String requestURI) {
// request URI가 whiteListURL에 포함되는지 확인
// 포함되면 !true = false 반환
// 포함되지 않으면 !false = true 반환
return !PatternMatchUtils.simpleMatch(whiteListURL, requestURI);
}
}
Filter 등록
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean customFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new CustomFilter()); // Filter 등록
filterRegistrationBean.setOrder(1); // Filter 순서 1 설정
filterRegistrationBean.addUrlPatterns("/*"); // 전체 URL에 Filter 적용
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean loginFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginFilter()); // Filter 등록
filterRegistrationBean.setOrder(2); // Filter 순서 2 설정
filterRegistrationBean.addUrlPatterns("/*"); // 전체 URL에 Filter 적용
return filterRegistrationBean;
}
}
loginFilter() 메서드 내부에서 addUrlPatterns() 를 활용하여 URL 패턴을 지정할 수 있다.
addUrlPatterns() 에 URL 패턴으로 지정해두면 추후 수정할 가능성이 높음“/post/*”Filter가 많아질수록 성능적으로 문제가 발생하지는 않나요?
Postman
이미 만들어져있는 /api/save API 호출을 가정한다.
로그인하지 않고 (session이 비어있는 채로 요청) 요청한다.

throw new RuntimeException("로그인 해주세요."); 실행


whiteListURL isLoginPath()wihteListURL 을 제외한 모든 경우에 인증 체크를 하도록 만든 메서드throw new RuntimeException()💡 Servlet Filter와 같이 Web과 관련된 공통 관심사를 해결할 수 있는 기술중에 하나이다. Servlet Filter는 Servlet이 제공하는 기술이다. Spring Interceptor는 Spring이 제공하는 기술이다.Spring Interceptor
두 기술의 차이점은 적용 순서, 범위, 사용방법 등이 있다. 아래 내용을 통해 알아보자

로그인 O

로그인 X

Spring Interceptor 또한 Filter와 같이 인증 역할을 수행할 수 있다.


public interface HandlerInterceptor {
// Controller 호출 전
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// Controller 호출 후
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 요청 완료 이후
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
doFilter() 메서드 기능 하나만 제공이 된다.preHandle() : Controller 호출 전postHandl() : Controller 호출 후afterCompletion() : 요청 완료 이후
public interface HandlerInterceptor {
// Controller 호출 전
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// Controller 호출 후
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 요청 완료 이후
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
preHandle
postHandle
afterCompletion

public interface HandlerIntercepter {
// Controller 호출 전
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// Controller 호출 후
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 요청 완료 이후
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
postHandle 은 호출되지 않는다.afterCompletion 을 사용해야한다afterCompletion() 의 파라미터에 Exception이 전달된다.@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor())
.order(1)
.addPathPatterns("/**")
.excludePathPatterns("/error");
}
}
WebMvcConfigurer가 제공하는 addInterceptors() 사용하여 인터셉터를 등록하면 된다.Order() : 필터체인과 마찬가지로 인터셉터의 호출 순서를 지정할 수 있다.addPathPatterns() : 인터셉터를 적용할 URL Patterns을 지정한다.excludePathPatterns() : 인터셉터에서 제외할 URL Patterns를 지정한다.오늘의 코드카타
import java.util.ArrayList;
import java.util.List;
class Solution {
public String[] solution(String my_str, int n) {
List<String> list = new ArrayList<>();
for (int i = 0; i < my_str.length(); i += n) {
list.add(my_str.substring(i, Math.min(i + n, my_str.length())));
}
return list.toArray(new String[list.size()]);
}
}
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println(Arrays.toString(solution.solution("abc1Addfggg4556b", 6)));
System.out.println(Arrays.toString(solution.solution("abcdef123", 3)));
}
}
잘라서 배열로 저장하기