필터
필터는 '서블릿 필터(Servlet Filter)'라고도 하며 리스너와 마찬가지로 웹 애플리케이션을 지원하기 위한 특수한 형태의 서블릿이다. 클라이언트 요청에 따라 서블릿이나 JSP가 실행되기 전에 response 혹은 request 객체의 조작이나 추가적인 처리를 할 수 있다.
필터는 기본적으로 특정 요청에만 동작하며, 여러 개의 필터가 정해진 순서에 따라 배치될 수 있는데 클라이언트 요청 처리 이전에 먼저 실행된다. 리스너와 마찬가지로 단순히 기능만 구현하는 웹 프로그램의 경우에는 필터를 꼭 만들지 않아도 된다. 하지만 애플리케이션 설계 관점에서 좀 더 유연하고 효과적인 애플리케이션 구현 및 운영이 필요하다면 필터에 대해 잘 알아 둘 필요가 있다.
필터는 기존 코드의 변경 없이 애플리케이션에서 공통적으로 사용할 수 있는 기능 구현에 널리 사용된다. 대표적으로 활용되는 분야는 다음과 같다.
인증(Authentication)
특정 페이지에서 로그인 여부나 특정 권한을 확인해야 할 때 컨트롤러에서 처리하는 방법이 있지만 로그인과 권한 확인과 같이 공통된 기능을 개별 컨트롤러에서 중복해서 구현하는 것은 좋지 않은 방법이다. 이 경우 필터를 이용하면 애플리케이션 구조와 상관없이 기존 소스를 최대한 수정하지 않고 인증 기능을 수행하도록 할 수 있다.
로깅/감사(Logging and Auditing)
특정 페이지 또는 기능에 대해 사용 현황을 모니터링하고 로그로 관리할 필요가 있을 때 인증의 경우와 마찬가지로 필터를 통해 해당 요청을 수행하기 전 로깅 처리를 할 수 있다.
국제화(Localization)
다국어 처리는 프레임워크 등에서 제공하는 국제화 방법을 사용할 수도 있으며 필터를 이용할 경우 특정 페이지에 들어갈 메시지 등을 해당 언어로 변환해 전달할 수도 있다.
한글 인코딩 처리(Encoding)
지금까지 서블릿이나 JSP를 이용해 POST 방식으로 전달되는 파라미터의 한글 처리를 위해 request.setCharacterEncoding("utf-8")을 사용해왔다. 그런데 모든 컨트롤러 서블릿에 이 코드를 추가하면 너무 많은 중복이 발생하며 캐릭터셋 변경 시에도 문제가 되기 때문에 필터를 통해 한 번에 처리하는 방법을 활용할 수 있다.
이 외에도 필터를 활용하는 방법은 다양하므로 필요에 따라 적극적으로 활용하도록 한다.
필터의 구조와 동작 과정
필터의 구조를 보면 리스너와 유사하다. 필터는 톰캣 서버를 시작할 때 필터 구현 클래스의 애너테이션을 참조하여 javax.servlet.Filter 인터페이스를 구현한 클래스가 초기화된다. 또한 필터는 여러 개 존재할 수 있으며, 각각의 필터는 init() 메서드를 통해 초기화 작업을 수행한다.
init() 메서드는 필터 초기화 시 한번만 실행된다. 이후 사용자 요청에 따라 서블릿이나 JSP가 호출될 때 애너테이션으로 설정된 필터 매핑 정보를 참조해 특정 서블릿이나 JSP에 대해 서로 다른 필터를 적용할 수 있다. doFilter()는 해당 필터가 적용되었을 때 수행할 작업을 구현하는 메인 메서드가 된다. destroy()는 필터가 종료될 때 수행할 내용을 구현한다.
위 그림에서 보는 것처럼 필터 A는 모든 서블릿에 적용되고, 필터 B는 서블릿a, b에 적용되며, 필터 C는 서블릿 a, b, c에 적용된다. 결과적으로 서블릿 a, b는 필터 A, B, C를 차례로 적용받게 된다. 필터의 적용은 각 필터의 doFilter() 메서드의 내용이 수행되는 것으로, ServletRequest와 ServletResponse의 내용을 가로채 필요한 작업을 수행하고 다음 필터로 전달하거나, 요청한 서블릿으로 이동하는 구조다.
예를 들어 가정에서 사용하는 정수기의 필터가 여러 종류이고, 수돗물이 각각의 필터를 통해 여과되고 정수되어 마실 수 있는 것과 유사한 구조라고 생각하면 된다. 즉 정수기의 물이 필터에서는 ServletRequest와 ServletResponse인 것이다. 물론 두 객체를 활용하지 않고 다른 필요한 작업만 수행하는 필터도 있을 수 있다.
위 그림과 같이 필터를 차례로 적용하기 위해서는 web.xml을 이용한 필터 등록이 필요하며 url 매핑 규칙을 잘 만들어 두어야 의도한 대로 동작한다.
필터 구현
리스너와 마찬가지로 이클립스에서 필터 생성 메뉴를 통해 손쉽게 생성하거나 javax.servlet.Filter 인터페이스를 구현하는 클래스를 직접 생성해도 된다.
또한 @WebFilter 애너테이션을 사용해 필터임을 알리고 서블릿과 유사하게 필터 요청을 위한 url 매핑 정보를 인자로 추가해주어야 한다.
자동 생성되는 오버라이딩 메서드는 필요한 부분만 구현하면 된다. 필터 자체의 생명 주기 메서드가 포함되어 있고, 실제로 필터가 동작할 때는 doFilter() 메서드가 호출된다.
@WebFilter("/EncodingFilter") // 필터임을 알림
public class EncodingFilter implements Filter{
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws
IOException, ServletException {
...
chain.doFilter(request, response);
}
}
- doFilter(): request, response, chain 등의 주어진 객체를 사용해 필요한 작업을 수행한 후에 필터를 실행하는 구조다. 만약 필터가 없을 경우 요청된 서블릿이나 jsp가 호출된다.
코드에서 보는 것처럼 request, response가 계속 전달되므로 원래 요청에 포함되지 않은 관련 작업을 수행하는 것이 가능하다. 물론 request, response와 관련 없이 별도의 데이터베이스나 로깅 작업, 다른 서비스와의 연동 등 필요한 작업이 있다면 구현하면 된다.
여러 개의 필터를 순서대로 적용하기
여러 개의 필터를 차례대로 적용하려면 애너테이션만으로는 불가능하다. 애너테이션에 의한 필터 실행은 특정 url 매핑 조건에 따라 이루어지는 것으로 실행 순서를 조정할 수 없기 때문이다.
만약 필터의 실행 순서를 지정하려면 'web.xml' 파일에 필터를 등록하는 과정을 거쳐야 한다. 이 경우 필터 서블릿의 @WebFilter에는 url 매핑 대신 filterName 속성이 들어가야 한다.
@WebFilter(filterName="filterOne")
public class FirstFilter implements Filter {
...
}
다음은 'web.xml'에 필터를 등록하는 코드의 예시다. 'web.xml'이 잘못된 경우 서버가 정상적으로 동작하지 않을 수 있으므로 주의하여 사용한다.
<filter-mapping>
<filter-name>filterOne</filter-name>
<url-pattern>/ch11/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filterTwo</filter-name>
<url-pattern>/ch11/*</url-pattern>
</filter-mapping>
'Programming > Web' 카테고리의 다른 글
[Web] JAX-RS (0) | 2022.10.26 |
---|---|
[Web] REST API (0) | 2022.10.26 |
[Web] 리스너(Listener) (0) | 2022.10.26 |
[Web] JDBC 기본구조와 API의 이해 (1) | 2022.10.26 |
[Web] SQL(DDL, DML) (0) | 2022.10.25 |