필터의 흐름
HTTP 요청 > WAS > 필터 > 서블릿(디스패처 서블릿) > 컨트롤러
필터제한
HTTP 요청 > WAS > 필터 > 서블릿 > 컨트롤러 // 로그인 사용자일 경우
HTTP 요청 > WAS > 필터( 적절하지 않은 요청이라 판단, 서블릿 호출 X) // 비 로그인 사용자일 경우
필터체인
HTTP 요청 > WAS > 필터1 > 필터2 > 필터3 > 서블릿 > 컨트롤러
public interface Filter {
    public default void init(FilterConfig filterConfig) throws ServletException {}
    
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;
            
    public default void destroy() {}
    
}
@Slf4j
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("log filter init");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("log filter doFilter");
        // 기능이 많지 않기때문에 다운캐스팅
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();
        String uuid = UUID.randomUUID().toString();
        try{
            log.info("REQUEST [{}{}]", uuid, requestURI);
            // 다음필터를 호출한다.
            chain.doFilter(request,response);
        }catch (Exception e){
            throw  e;
        }finally {
            log.info("RESPONSE [{}{}]", uuid, requestURI);
        }
    }
    @Override
    public void destroy() {
        log.info("log filter destroy");
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
    public FilterRegistrationBean logFilter() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new LogFilter()); // 만든 필터 넣어주고
        filterFilterRegistrationBean.setOrder(1); // 필터 우선순위 정해주고
        filterFilterRegistrationBean.addUrlPatterns("/*"); // 모든 경로에 다 적용한다.
        return filterFilterRegistrationBean;
    }
}
logback mdc도 한번 검색해보자.
@Slf4j
public class LoginFilter implements Filter {
    // 들어올수 있는 경로
    private static final String[] whiteList = {"/", "/members/add", "/login", "/logout", "/css/*"};
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        try{
            log.info("인증체크 필터 시작 {}",requestURI);
            if(isLoginCheckPath(requestURI)) {
                log.info("인증 체크 로직 실행 {}", requestURI);
                HttpSession session = httpServletRequest.getSession(false);
                if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)==null) {
                    log.info("미인증 사용자가 요청했다 !{}", requestURI);
                    // 로그인으로 리다이렉트
                    httpServletResponse.sendRedirect("/login?redirectURL=" + requestURI);
                    return ;
                }
            }
            chain.doFilter(request,response);
        }catch (Exception e) {
            throw  e;
        } finally {
            log.info("인증체크 필터 종료 {}", requestURI);
        }
    }
    /**
     * 화이트 리스트의 경우 인증체크 X
     */
    private boolean isLoginCheckPath(String requestURI) {
        return !PatternMatchUtils.simpleMatch(whiteList, requestURI);
    }
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
리다이렉트 부분은 컨트롤러에서 @RequestParam(defaultValue="/") String redirectURL 파라미터로 받아서 처리해줘야 한다.