이전 포스팅을 통해서 기본적인 Filter를 구현하는 방법에 대해서 알아봤습니다. 그럼 실제 프로젝트에 어떻게 적용할 것인지에 대해서 추가로 확인해볼 필요가 있어서 해당 포스팅을 작성했습니다.
우선, LogFilter를 실제 프로젝트에 적용한다고 가정하겠습니다.
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
filterChain.doFilter(req, res);
}finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
@Slf4j
public class LoginCheckFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getRequestURI();
log.info("[{}] LoginCheckFilter doFilter Start", requestURI);
try {
filterChain.doFilter(req, res);
}finally {
log.info("[{}] LoginCheckFilter doFilter End", requestURI);
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> logFilter(){
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LogFilter());
bean.setOrder(1);
bean.addUrlPatterns("/*");
return bean;
}
@Bean
public FilterRegistrationBean<Filter> loginCheckFilter(){
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoginCheckFilter());
bean.setOrder(1);
bean.addUrlPatterns("/*");
return bean;
}
}
2024-07-23T00:44:36.650+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LogFilter : [/] LogFilter doFilter Start
2024-07-23T00:44:36.651+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter Start
2024-07-23T00:44:36.672+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter End
2024-07-23T00:44:36.672+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LogFilter : [/] LogFilter doFilter End
위 방식은 Filter 구현체를 정의 후 Configuration 어노테이션을 활용하여 FilterRegistrationBean을 통해 Filter Bean으로 등록해주는 방법입니다.
@Slf4j
@Component
@Order(1)
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
@Slf4j
@Component
@Order(2)
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
2024-07-23T00:44:36.650+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LogFilter : [/] LogFilter doFilter Start
2024-07-23T00:44:36.651+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter Start
2024-07-23T00:44:36.672+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter End
2024-07-23T00:44:36.672+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LogFilter : [/] LogFilter doFilter End
위 방식은 별도의 설정 클래스 없이 직접 Component 어노테이션을 사용하여 필터를 빈으로 등록해주는 방식입니다. 해당 Filter 등록 시 @Order 어노테이션을 사용하여 순서를 설정해줄 수 있습니다. 하지만, 필터를 적용할 URL 패턴 설정은 불가능합니다.('/*'로 고정)
@Slf4j
@WebFilter
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
@Slf4j
@WebFilter
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
@ServletComponentScan
@SpringBootApplication
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}
2024-07-23T00:44:36.650+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LogFilter : [/] LogFilter doFilter Start
2024-07-23T00:44:36.651+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter Start
2024-07-23T00:44:36.672+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter End
2024-07-23T00:44:36.672+09:00 INFO 34980 --- [study] [nio-8080-exec-2] com.test.study.filter.LogFilter : [/] LogFilter doFilter End
위 처럼 구현 시, ServletComponentScan 어노테이션을 통해 WebFilter 어노테이션이 붙은 클래스들을 필터로 활성화시켜줄 수 있습니다. 추가로, 어노테이션 attribute로 경로를 설정하여 해당 경로에 들어오는 요청 및 응답에 필터를 적용할 수 있습니다. 하지만, 필터 순서(Order)를 설정할 수 없으며 기본적으로 Filter 클래스명을 기준으로 오름차순으로 순서가 적용됩니다.
ex) AFilter > BFilter
@Slf4j
@Component
@WebFilter
@Order(1)
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
@Slf4j
@Component
@WebFilter
@Order(2)
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
@ServletComponentScan
@SpringBootApplication
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}
2024-07-23T01:51:57.216+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter Start
2024-07-23T01:51:57.217+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LogFilter : [/] LogFilter doFilter Start
2024-07-23T01:51:57.217+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LogFilter : [/] LogFilter doFilter Start
2024-07-23T01:51:57.217+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter Start
2024-07-23T01:51:57.241+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter End
2024-07-23T01:51:57.241+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LogFilter : [/] LogFilter doFilter End
2024-07-23T01:51:57.241+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LogFilter : [/] LogFilter doFilter End
2024-07-23T01:51:57.241+09:00 INFO 29028 --- [study] [nio-8080-exec-4] com.test.study.filter.LoginCheckFilter : [/] LoginCheckFilter doFilter End
위처럼 구현 후 실행해보니 @WebFilter를 사용한 LogFilter와 LoginCheckFilter가 2번씩 호출되었습니다. 이처럼 결과가 나온 이유는 @ServletComponentScan과 @Component 어노테이션이 동시에 적용되어 있기 때문입니다.
즉, 위 방법은 올바른 필터링 처리가 어렵습니다. 따라서, 사용하지 않는 것을 권장합니다.
결론적으로, 필터를 관리하는 수준에 따라서 구현 방식이 다릅니다. Filter의 Order와 요청 URL을 설정하여 Filter Chain을 구성하기 위해서는 @Configuration(1번) 방식을 사용해줍니다.
그것이 아닌 경우, 간단하게 @Component(2번) 방식 또는 @WebFilter(3번) 방식을 통해서 구현해보는 것도 좋습니다.