필터, 인터셉터 - Filter를 등록하는 4가지 방법

김회민·2023년 2월 16일
2

Spring

목록 보기
17/25

들어가기 전에..

이 글은 인프런의 김영한님의 스프링 MVC 2편을 보다가 "섹션 7. 로그인 처리 2 - 필터, 인터셉터"의 "정리" 부분에서 나오는 필터를 등록하는 다른 방법이 지금 내 환경(Spring boot 3.0)에서 먹히지 않아, 궁금해져서 찾은 내용을 담고 있다.

@Configuration

LogFilter - 로그 필터

@Slf4j
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);
        }
    }
}

LoginCheckFilter - 로그인 필터

@Slf4j
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);
        }
    }
}

FilterConfig - Filter 등록

@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(2);
        bean.addUrlPatterns("/*");

        return bean;
    }
}

실행 결과

[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End

특징

  • 설정을 위한 별개의 파일(@Configuration 이 붙은 객체)이 필요하다.
  • setOrder() 를 통해 순서를 정할 수 있다.
  • addUrlPatterns() 을 통해 베이스 URL 및 Whitelist를 설정할 수 있다.

@Component

LogFilter

@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);
        }
    }
}

LoginCheckFilter

@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);
        }
    }
}

실행 결과

[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End

특징

  • 설정을 위한 별개의 파일이 필요하지 않는다.
  • @Order 애노테이션을 이용해 순서를 설정할 수 있다.
  • 기본 URL Pattern이 /* 이며 설정할 수 없다.

@WebFilter + @ServletComponentScan

LogFilter

@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);
        }
    }
}

LoginCheckFilter

@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);
        }
    }
}

MainApplication

@ServletComponentScan
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringCoreMvc26Application.class, args);
    }

}

실행 결과

[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End

특징

  • 설정을 위한 별개의 파일이 필요하지 않는다.
    • 대신, 애플리케이션 실행되는 메인 객체위에 @ServletComponentScan 을 사용해야 한다.
  • @Order를 이용한 순서 등록을 사용할 수 없다.
    • 각 필터에 대한 순서를 보장할 수 없다.
  • @WebFiltervalueurlPatterns 파라미터를 이용해 whitelist 방식으로 베이스 URL을 설정할 수 있다.
    • @WebFilter("/filter/*")
    • @WebFilter({"/login", "/items"})
    • @WebFilter(urlPatterns = "/filter/*")
    • @WebFilter(urlPatterns = {"/login", "/items"})

@WebFilter + @Component

LogFilter

@Slf4j
@WebFilter
@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);
        }
    }
}

LoginCheckFilter

@Slf4j
@WebFilter
@Component
@Order(1)
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);
        }
    }
}

특징

  • 2번째 방법과 3번째 방법을 모두 사용하는 방법이다.
  • 설정을 위한 별개의 파일이 필요하지 않는다.
    • 컴포넌트 스캔 방식을 사용하기 때문에 @ServletComponentScan 도 필요없다.
  • @Order 애노테이션을 이용해 순서를 설정할 수 있다.
  • @WebFiltervalueurlPatterns 파라미터를 이용해 베이스 URL이나 Whitelist 방식으로 설정할 수 있다.
    • @WebFilter("/filter/*")
    • @WebFilter({"/login", "/items"})
    • @WebFilter(urlPatterns = "/filter/*")
    • @WebFilter(urlPatterns = {"/login", "/items"})
  • 애노테이션이 기본으로 3개가 필요하다.

정리

설정 방법순서 설정베이스 URLBean 여부
@ConfigurationFilterRegistrationBeansetOrderaddUrlPatternsX
@Component없어도 됨@Order사용할 수 없음O
@WebFilter@ServletComponentScan사용할 수 없음value, urlPatterns 파라미터O
@WebFilter +
@Component
없어도 됨@Ordervalue, urlPatterns 파라미터O

주의

첫 번째 방법과 네 번째 방법을 혼합하여 사용할 경우, 스프링 빈을 등록하는 과정에서 에러를 확인할 수 있다.

The bean 'logFilter', defined in class path resource [hello/springcoremvc26/config/FilterConfig.class], could not be registered. 
A bean with that name has already been defined in file [/project/java/spring/spring-core-mvc2-6/out/production/classes/hello/springcoremvc26/web/filter/LogFilter.class] and overriding is disabled.

이유를 자세히 읽어보면, logFilter가 이미 빈으로 등록이 되었다고 되어있다고 되어 있다.

다른 글을 찾아보면 동일한 필터가 2번 호출된다고도 하던데, 아마 버전의 차이이거나 스프링 부트의 설정에서 빈을 중복으로 등록할 수 있도록 변경할 수 있는 것으로 보인다.

결론

필터를 사용하기 위해서 4가지 방식을 알아보았다.

첫 번째 방법(@Configuration + FilterRegistrationBean)과 두 번째, 세 번째 방법을 합친 네 번째 방법(@WebFilter + @Component) 중에서 고려를 해야한다.

나의 경우에는 @Configuration + FilterRegistrationBean을 더 선호하는데, 이유는 Filter Class 위에 애노테이션을 여러개 붙이는 것 보다, 별도의 생성 파일을 하나 만들어서 관리하는게 더 편할 것 같아서이다.

하지만, @WebFilter + @Component스프링 빈으로 등록하는 방법이기에, 다른 스프링 빈을 의존성 주입 받을 수 있다. 그런 경우에는 첫 번째 방법으론 할 수 없으니, 네 번째 방법을 사용하자.

출저

profile
백엔드 개발자 지망생

1개의 댓글

comment-user-thumbnail
2023년 2월 22일

4 번째 방식에서 @WebFilter에 url패턴이 적용되지 않는거 같습니다.

답글 달기