Java 웹 프로그래밍에서 프로그램 흐름의 앞 혹은 뒤에 공통적인 처리를 할 수 있는 방법이 있는데, 필터, 인터셉터, AOP가 있다. 이 세가지 방법에 중에서도 스프링과 무관한 Filter에 대해 먼저 정리해보았다.
Filter
필터는 HTTP의 요청과 응답을 변경할 수 있는 재사용 가능한 클래스이다. 서블릿 2.3 규약에 새롭게 추가되었다. 필터는 객체의 형태로 존재해 클라이언트에서 오는 요청(request)와 최종 자원(JSP, 서블릿 등) 사이에 위치해 클라이언트의 요청 정보를 알맞게 변경할 수 있다. 또한, 필터는 최종 자원과 클라이언트로 가는 응답(response) 사이에 위치해 요청 결과를 알맞게 변경할 수 있다.
주요 응용
- 응답 데이터 변환
- 특정 아이피만 접근허용
- 인코딩 변환 처리
- xss방어
필터의 기본 구조
위 그림으로 보면 알 수 있듯이, 자원이 받게되는 요청 정보는 클라이언트와 자원 사이에 존재하는 필터에 의해 변경된 요청 정보가 되며, 또한 클라이언트가 보는 응답 정보는 필터가 변경한 응답 정보가 된다. 필터는 한 개 뿐 아니라, 여러개의 필터가 모여 하나의 체인(chain)을 형성할 수도 있다.
여러 개의 필터가 모여 하나의 체인을 형성할 때, 첫번째 필터가 변경하는 요청정보는 클라이언트의 요청 정보가 되지만, 체인의 두 번째 필터가 변경하는 요청 정보는 첫번째 필터를 통해 변경된 요청정보가 된다. 응답 정보의 경우 요청 정보와 비슷한 과정을 거치지만, 필터의 적용 순서과 요청 때와는 반대라는 것을 유념해두어야 한다.
필터는 정보 뿐 아니라 흐름도 변경할 수 있다. 즉, 필터는 클라이언트의 요청을 필터 체인의 다음 단계에 보내는 것이 아니라, 다른 자원의 결과를 클라이언트에 전송할 수 있다. 이런 기능은 사용자 인증이나 권한 검사 같은 기능 구현시 용이하게 사용할 수 있다.
필터 구현 관련 인터페이스 및 클래스
필터를 구현하는데 있어 핵심 역할을 하는 아래 세가지가 있다.
javax.servlet.Filter
인터페이스 : 클라이언트와 최종 자원 사이에 위치하는 필터를 나타내며, 개발자가 구현해야 하는 인터페이스javax.servlet.ServletRequestWrapper
클래스 : 필터가 요청을 변경한 결과를 저장하는 래퍼 클래스javax.servlet.ServletResponseWrapper
클래스 : 필터가 응답을 변경하기 위해 사용하는 래퍼 클래스
개발자는 이 세 타입을 이용해 필터를 구현하고, 요청과 응답 정보를 변경하는 기능을 구현 할 수 있다.
Filter 인터페이스
Servlet 패키지에서는 아래와 같이 인터페이스가 정의되어 있다.
package javax.servlet;
import java.io.IOException;
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public void destroy();
}
위 인터페이스를 알맞게 구현해주기 위해 아래 메소드를 오버라이딩 해주어야 한다.
public void init(FilterConfig filterConfig)
- 필터를 초기화 할 때 호출된다.
- ` public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)`
- 필터 기능을 수행하는 메소드. 클라이언트의 요청이 있을때마다 실행되며, chain을 이용해 체인의 다음 필터로 처리를 전달 할 수 있다.
- ` public void destroy()` :
- 필터가 웹 컨테이너에서 삭제될 때 호출. 보통 초기화시 생성했던 자원을 소멸시킬 때 사용된다.
필터 구현 및 등록 예제
필터를 구현하는 방법은 간단하다. Filter
인터페이스를 구현하여 필터 클래스를 만들고, web.xml
설정파일에서 어떤 경로로 접근할 때 필터를 적용할 지 등록해주기만 하면 끝이다! 아래 예제는 요청시 requestURL을 찍어주는 예제이다.
소스 부분
package org.test.one;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class TestFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // 모든 요청(Request)에 대해서 이 부분이 실행된다.
HttpServletRequest request = (HttpServletRequest)req;
System.out.println("RequestURL : "+request.getRequestURL());
chain.doFilter(req, res); //다음 필터로 넘어간다.
}
@Override
public void init(FilterConfig config) throws ServletException { // 초기화할 때 실행되는 부분
String testParam = config.getInitParameter("testParam");
System.out.println("testParam : "+testParam);
}
@Override
public void destroy() {
}
}
web.xml 부분
...
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>
org.test.one.TestFilter
</filter-class>
<init-param>
<param-name>testParam</param-name>
<param-value>This is Test Parameter!!!</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
필터 설정하기 : web.xml 이용
필터를 설정하는 방법은 두가지가 있는데, 첫번째 방법은 web.xml파일에 관련 정보를 추가하는 방법이다.
....
<filter>
<filter-name>NullParameter</filter-name>
<filter-class>filter.NullParameterFilter</filter-class>
<init-param>
<param-name>parameterNames</param-name>
<param-value>id,name</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>NullParameter</filter-name>
<url-pattern>*.jsp</url-pattern>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<filter>
<filter-name>LoginCheck</filter-name>
<filter-class>filter.LoginCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginCheck</filter-name>
<url-pattern>/board/*</url-pattern>
<url-pattern>/board2/*</url-pattern>
<servlet-name>testServlet</servlet-name>
</filter-mapping>
</web-app>
여기서 주요 태그를 살펴보자.
<filter>
: 필터를 등록한다.<filter-name>
에는 필터의 이름을 지정하고,<filter-class>
는 구현한 필터 클래스를 패키지명까지 포함하여 지정해준다. 웹 컨테이너는 서버를 띄울 때 web.xml 설정의<filter>
태그를 읽어 필터 객체를 생성하고 초기화한다.<filter-mapping>
: 등록한 필터의 동작 범위를 설정할 수 있다.<filter-name>
은 앞서 등록한 필터명을 지정하고,<url-pattern>
은 URL패턴을 입력한다. 서블릿 등록시의 규칙과 동일하다. url패턴 말고 서블릿을 기준으로도 등록할 수 있는데,<servlet-name>
태그를 사용하면 필터링할 서블릿을 지정할 수 있다.(서블릿 클래스명이 아닌 web.xml에서 등록한 명칭이라는 점을 유의하자!)- 이 태그에서는 여러개의
<url-pattern>
과<servlet-name>
을 등록할 수 있다.
- 이 태그에서는 여러개의
<dispatcher>
: 필터가 적용하는 시점을 지정할 수 있다.REQUEST
: 클라이언트의 요청일 경우(DEFAULT 값)FORWARD
:forward()
를 통해 제어 흐름을 이동하는 경우INCLUDE
:include()
를 통해 포함되는 경우ERROR
: 에러페이지에 적용
필터 설정하기 : @WebFilter
애노테이션을 이용
web.xml을 이용해 설정하는것 외에도, 자바파일에서 @WebFilter
애노테이션을 사용해 필터를 설정할 수 있다. 애노테이션을 사용하는 방법은 아래와 같다.
@WebFilter(filterName = "xsltFilter", urlPatterns = {"/xml/*"})
public class XSLTFilter implements Filter {
...필터 구현 내용
}
이 경우, /xml/*을 통해 요청을 들어올경우 위 필터를 적용하게 된다. @WebFilter
애노테이션의 주요 속성은 아래와 같다.
urlPattrns
: 필터를 적용할 URL 패턴 목록을 지정한다.servletNames
: 필터를 적용할 서블릿 이름 목록을 지정한다.filterNames
: 필터 이름을 지정한다.initParams
: 초기화 파라미터 목록을 지정한다.dispatcherTypes
필터를 적용할 범위를 지정한다.
필터의 실행 순서
web.xml을 통해 필터를 생성하였을 경우, 해당 파일에 작성된 순서대로 필터가 실행된다. 즉, <filter-mapping>
이 정의된 순서를 기준으로 필터체인의 정렬 순서를 정의한다.
<filer-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filer-mapping>
<filer-mapping>
<filter-name>secondFilter</filter-name>
<url-pattern>/*</url-pattern>
</filer-mapping>
<filer-mapping>
<filter-name>thirdFilter</filter-name>
<url-pattern>/third</url-pattern>
</filer-mapping>
위와 같이 작성된 web.xml파일에서, /third/hi
를 요청할 경우 firstFilter -> secondFilter -> thirdFilter 순으로 실행이 된다. 하지만 @WebFilter
어노테이션을 이용하였을 경우, 필터의 실행 순서를 지정하는 속성이 없기 때문에 마구잡이로 실행될 수 있다..
참고 자료 :