본문으로 바로가기

[Spring Boot] 스프링 부트 필터 적용 (Filter)

category Backend/Spring 2021. 11. 29. 14:00

목차

    1. 필터를 왜 쓰나요?

    결론부터 말하면 공통부분을 따로 작성하여 중복 소스를 줄이고 소스 관리도 쉬워져 유지보수에 좋습니다.

     

    Spring (자바) 개발을 하다 보면 공통적으로 처리해야 할 부분들이 많습니다.

    예를 들으면 인코딩, CORS, XSS, LOG, 인증, 권한, 로그인(인증) 체크, 권한(인가) 체크 등과 같은 작업들이 있는데, 이러한 작업 코드를 소스마다 작성한다면 중복된 코드도 많아지고 프로젝트가 클 경우 부하를 줄 수도 있고 유지보수도 힘들게 됩니다.

    필터는 이러한 공통부분을 따로 작성할 수 있게 도와주는 역할을 합니다.

    인터셉터 외에도 인터셉터나 AOP가 있습니다.

    2. 구조

    구조

    3. 필터 특징

    • 요청과 응답을 거른 뒤 정제하는 역할을 합니다.
    • 필터는 DispatcherServlet 이전에 실행이 되는데 필터가 동작하도록 지정된 경로의 앞에서 요청 데이터를 확인 및 변경하거나, 응답 데이터에 대해서도 확인 및 변경하는 처리를 할 수가 있습니다.
    • WebApplicationContext 기능으로 Spring Framework와 무관하게 동작하며, Spring 자원을 이용할 수 없습니다.
    • 필터에서는 애플리케이션의 비즈니스 로직과 관계없는 작업을 주로 처리합니다.
    • 주로 인코딩 변환 처리, XSS방어, CORS, Log 처리 등의 요청에 대한 처리로 사용합니다.

    4. 필터 주요 메서드

    init() - 필터 인스턴스 초기화 시 실행되는 메서드
    doFilter() - 클라이언트의 요청/응답 처리 시 실행되는 메서드
    destroy() - 필터 인스턴스가 제거될 때 실행되는 메서드

    5. 필터 작성

    스프링 부트의 필터 등록에는 두 가지가 있습니다. 맘에 드는 방식으로 필터를 등록하면 됩니다.

    1. 설정 파일에서 빈으로 등록
    2. @WebFilter 어노테이션 활용

     

    먼저 Filter를 implements(상속) 하여 필터를 작성합니다.

    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /*
    init()
    웹 컨테이너(톰캣)이 시작될 때 필터 최초 한 번 인스턴스 생성
    
    doFilter()
    클라이언트의 요청 시 전/후 처리
    FilterChain을 통해 전달
    
    public void destroy()
    필터 인스턴스가 제거될 때 실행되는 메서드, 종료하는 기능
     */
    
    @Slf4j
    public class apiFilter implements Filter {
    
    
        /*
            - 필터 인스턴스 초기화
         */
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("---필터 인스턴스 초기화---");
        }
    
        /*
            - 전/후 처리
            - Request, Response가 필터를 거칠 때 수행되는 메소드
            - chain.doFilter() 기점으로 request, response 나눠집니다.
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
    
            String requestURI = req.getRequestURI();
    
            log.info("---Request(" + requestURI + ") 필터---");
            chain.doFilter(request, response);
            log.info("---Response(" + requestURI + ") 필터---");
        }
    
    
        /*
            - 필터 인스턴스 종료
         */
        @Override
        public void destroy() {
            log.info("---필터 인스턴스 종료---");
        }
    }

    5.1. 설정 파일 필터 빈 등록

    import com.test.api.filter.apiFilter;
    import com.test.api.interceptor.ApiInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import java.util.Arrays;
    
    @Configuration
    public class WebMvcConfiguration implements WebMvcConfigurer {
    
        //Filter에 포함되는 URL 주소
        private static final String[] INCLUDE_PATHS = {
                "/test/*",
                "/test2/*"
        };
    
        @Bean
        public FilterRegistrationBean filterBean() {
        
            FilterRegistrationBean registrationBean = new FilterRegistrationBean(new apiFilter());
            registrationBean.setOrder(Integer.MIN_VALUE); //필터 여러개 적용 시 순번
    //        registrationBean.addUrlPatterns("/*"); //전체 URL 포함
    //        registrationBean.addUrlPatterns("/test/*"); //특정 URL 포함
    //        registrationBean.setUrlPatterns(Arrays.asList(INCLUDE_PATHS)); //여러 특정 URL 포함
            registrationBean.setUrlPatterns(Arrays.asList("/test/*", "/test2/*")); 
    
            return registrationBean;
        }
    }

    5.2. @WebFilter 어노테이션 등록

    1. 필터 소스 위에 @WebFilter 어노테이션을 이용해 필터링할 패턴을 추가해줍니다.

    @Slf4j
    @WebFilter(urlPatterns = {"/test/*", "/test2/*"})
    public class apiFilter implements Filter {
    
    	// (...) 생략
    }

     

    2. @SpringBootApplication 어노테이션을 가지고 있는 스프링 부트 실행 파일에 @ServletComponentScan 어노테이션을 추가합니다.

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    @ServletComponentScan //추가
    @SpringBootApplication
    public class ApiApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ApiApplication.class, args);
    	}
    
    }