필터

김성환·2022년 7월 4일

스프링

목록 보기
2/4

필터란

필터란 무언가를 걸러내는 필터를 의미한다.
서블릿에서 필터의 경우도 마찬가지이다. 필터는 요청이 서블릿으로 전달되기 전, 후에 필터링을 하기 위한 기술을 의미한다.(인터페이스로 제공함)
필터의 위치dispatcher서블릿과 클라이언트 사이에 위치하고 있다.

무엇을 필터링하는가?

대표적으로 데이터의 본문을 UTF-8로 인코딩해야 하는 것이다.
필터가 처리하는 것은 대표적으로 보안,UTF-8로 인코딩, 등등이 있을 수 있다.

필터의 과정

필터의 경우 체인의 형태의 구조를 가진다. 서블릿에 필터가 여러개가 순서대로 구성되어 있는 체인의 형태이다.
우선 요청이 들어올 경우 요청이 서블릿으로 들어가기 전에 필터 체인을 사용해 순서대로 요청을 필터링하게 된다.
이렇게 필터링 된 요청을 서블릿이 받게 된다.
응답 역시 필터에 의해 필터링 된다.
필터의 과정을 순서대로 설명하면 다음과 같다.
① 서블릿 컨테이너는 웹 어플리케이션을 시작할 때 web.xml에 등록된 필터의 인스턴스를 생성하고 init()을 호출한다.
② 클라이언트 요청이 들어오면 해당하는 필터의 doFilter()를 호출한다.
③ doFilter()에서 작업을 실행하고 다음 필터의 doFilter()를 호출한다.
④ 마지막 필터까지 ③을 반복한다.
⑤ 마지막 필터는 서블릿의 service()(GET,POST등등의 작업)를 호출한다.
⑥ 서블릿의 service()가 끝나면 service()를 호출했던 이전 필터로 돌아간다.
⑦ 반복해서 제일 처음 호출됐던 필터까지 돌아간다.
⑧ 마지막으로 클라이언트에게 응답 결과를 보낸다.

필터 등록하는 방법

web.xml을 사용하는 방법

web.xml에 filter태그를 사용하여 사용할 필터를 등록해주는 방법이다.(서블릿과 동일하게 등록하면 된다.)
아래는 예시 코드

 <filter>		
	<filter-name>characterEncodingFilter</filter-name>		
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>		
	<init-param>			
		<param-name>encoding</param-name>			
		<param-value>UTF-8</param-value>		
 	</init-param>	
</filter>	
<filter-mapping>		
	<filter-name>characterEncodingFilter</filter-name>		
	<url-pattern>/*</url-pattern>	
</filter-mapping>

필터에 사용되는 주요 태그

  • filter : 등록할 필터를 나타내는 태그
  • filter-name : 등록할 필터의 이름을 나타내는 태그
  • filter-class : 등록할 필터의 위치를 나타내는 태그
  • init-param : 필터가 초기화 될때, 전달되는 파라미터를 나타내는 태그
  • filter-mapping : 등록할 필터를 매핑해주는 태그
  • url-pattern : 필터가 처리할 url를 나타내는 태그

FilterRegistrationBean를 사용하는 방법

FilterRegistrationBean 이라는 클래스를 사용하여 필터를 등록할 수 있다.
FilterRegistrationBean의 경우 별도의 설정클래스(config)를 만들어서 여러 필터들을 등록하고 관리할 수 있다.
아래는 예제이다.

@Configuration
public class config {
    @Bean
    public FilterRegistrationBean<FirstFilter> firstFilter(){
        FilterRegistrationBean<FirstFilter> registrationBean =new FilterRegistrationBean<>();
        registrationBean.setFilter(new FirstFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        registrationBean.setName("first-filter");
        return registrationBean;
    }
    @Bean
    public FilterRegistrationBean<SecondFilter> secondFilter(){
        FilterRegistrationBean<SecondFilter> registrationBean =new FilterRegistrationBean<>();
        registrationBean.setFilter(new SecondFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(2);
        registrationBean.setName("second-filter");
        return registrationBean;
    }
}

위 예제는 config라는 클래스 안에 FirstFilter와 SecondFilter를 등록한 모습이다.
즉, config클래스는 필터를 등록하는 클래스이고, FirstFilter는 필터를 구현한 클래스인 것이다.

FilterRegistrationBean의 주요 메서드

  • setFilter(등록할 필터 객체) : 필터를 등록하는 메서드
  • addUrlPatterns(url) : 필터링 할 url지정하는 메서드
  • setOrder(순서) : filter체인의 순서를 정하는 메서드
  • setName(이름) : filter의 이름을 등록하는 메서드

어노테이션을 사용하는 방법

@WebFilter를 사용하여 필터를 등록할 수 있다. 이때, @ServletComponentScan을 사용해야 한다.(혹은 필터 클래스에 @Component를 사용해야 한다.)
@ServletComponetScan은 해당 위치부터 하위 항목들의 서블릿,필터등을 모두 조사하여, 등록해주는 역활을 하는 어노테이션이다.
@WebFilter은 urlPatterns이라는 매개변수를 사용해서 필터링 될 url를 지정할 수 있다.

@ServletComponetScan를 사용할 경우 주의점

@ServletComponetScan사용할 경우 필터 클래스에 @Component를 사용하면 안된다.
이 둘을 같이 사용할 경우 아래와 같은 문제가 발생한다.

왜 그럴까?

@SpringBootApplication 안에 @ComponentScan이 있기 때문이다.
그래서 @ServletComponetScan와 @ServletComponetScan 모두 컴포넌트를 잡기 때문에 이러한 문제가 발생하게 된다.
따라서 @Component만을 사용하거나 @ServletComponetScan만을 사용해야 한다.

필터 클래스 작성

필터 클래스는 Servlet과 동일하게 Filter라는 인터페이스를 구현하는 방식으로 작성할 수 있다.

Filter인터페이스의 메서드

  • init() : 필터를 초기화하는 메서드이다.
  • doFilter() : 필터가 처리하는 로직을 작성하는 메서드이다.
    doFilter의 경우 매개변수로 ServletRequest, ServletResponse, FilterChain을 받는데,
    ServletRequest의 경우 요청에 대한 정보를 가진 객체를 말한다.
    ServletRequest의 경우 응답에 대한 정보를 가진 객체를 말한다.
    이때, HttpServletRequest의 경우 ServletRequest를 상속한 것이며, http 프로토콜에서 존재하는 요청에 대한 추가적인 정보를 담고 있는 객체라고 생각하면 된다.
    FilterChain은 여러개의 필터들이 순서대로 연결되어 있는 필터체인의 객체이다.
    FilterChain의 메서드인 doFilter(ServletRequest,ServletRequest)를 사용하면 다음 순서의 필터를 실행하게 된다.
  • destory() : 필터를 종료하는 메서드이다.
    아래는 예시 코드이다.
@Slf4j
public class FirstFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        log.info("필터1 초기화!");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("필터1 시작!");
        filterChain.doFilter(servletRequest,servletResponse);
        log.info("필터1 끝!");
    }
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

위와 같은 코드를 가진 3개의 필터를 등록한 뒤 실행해보면 결과는 다음과 같다.(필터의 실행 순서는 필터체인에 등록한 순서대로 실행된다)

요청필터와 응답필터

필터의 경우 따로 요청필터와 응답필터를 구분하는 것은 없다.
하지만 다음 필터를 실행해주는 filterChain의 dofilter메서드를 사용할 때, 해당 메서드의 상단 부분은 요청을 받을때 처리하는 구간이라 요청필터라고 볼 수 있다.
또한 해당 메서드의 하단 부분은 응답을 받을때 처리하는 구간이라 응답필터라고 볼 수 있다.
즉, 아래와 같다.

@WebFilter("/*")
public class FirstFilter implements Filter{
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		Filter.super.init(filterConfig);
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 필터가 요청을 처리하는 구간(요청필터)
		chain.doFilter(request, response);
		// 필터가 응답을 처리하는 구간(응답필터)
	}
}

필터체인의 doFilter메서드를 사용할 경우 다음 순서의 필터가 실행되는데, 이전에 실행된 필터는 응답을 처리하기 위해 대기상태가 된다.

참고
https://atoz-develop.tistory.com/entry/Servlet-Filter-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

profile
개발자가 되고 싶다

0개의 댓글