Servlet 02: Listener와 Filter
비트캠프 서초본원 엄진영 강사님의 수업을 듣고 정리했습니다.
Servlet 02: Listener와 Filter
1. Listener
서블릿 컨테이너가 관리하는 컴포넌트
- 서블릿, 필터, 리스너
리스너 만들기
- 블릿 컨테이너 또는 서블릿, 세션 등의 객체 상태가 변경되었을 때 보고 받는 옵저버
Observer디자인 패턴이 적용된 것이다.- ServletContextListener
- 서블릿 컨테이너를 시작하거나 종료할 때 보고 받고 싶다면 이 인터페이스를 구현하라.
- ServletRequestListener
- 요청이 들어오거나 종료될 때 보고 받고 싶다면 이 인터페이스를 구현하라.
- HttpSessionListener
- 세션이 생성되거나 종료될 때 보고 받고 싶다면 이 인터페이스를 구현하라.
- XxxListener
- 기타 다양한 인터페이스가 있다. 문서를 참고하라.
리스너 배포하기
- 방법1) DD 파일(web.xml)에 리스너를 등록한다.
- web.xml 파일에 등록하여 호출하면, 서버를 시작하기만 하면 Listener01.contextInitialized()가 호출된다.
- 서버를 종료해야 Listener01.contextDestroyed()가 호출된다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- 리스너 등록 -->
<listener>
<listener-class>com.eomcs.web.ex02.Listener01</listener-class>
</listener>
</web-app>
- 방법2) @WebListener 애노테이션으로 설정한다. 경로를 지정하지 않아도 된다.
- 서블릿을 요청했을 때 Listener02.requestInitialized()가 호출된다.
- 요청 처리를 완료하면 Listener02.requestDestroyed()가 호출된다.
@WebListener
public class Listener02 implements ServletContextListener {
@Override
public void requestInitialized(ServletRequestEvent sre) { }
@Override
public void requestDestroyed(ServletRequestEvent sre) { }
}
Listener02.requestInitialized()
클라이언트 IP: 0:0:0:0:0:0:0:1
요청 URL: /ex01/s04
Servlet04.service(ServletRequest,ServletResponse)
Listener02.requestDestroyed()
리스너의 용도
- 서블릿 컨테이너나, 세션 등이 특별한 상태일 때 필요한 작업을 수행한다.
- ServletContextListener
- 웹 애플리케이션을 시작할 때 Spring IoC 컨테이너 준비하기
- 웹 애플리케이션을 시작할 때 DB 커넥션 풀 준비하기
- 웹 애플리케이션을 종료할 때 DB 커넥션 풀에 들어 있는 모든 연결을 해제하기
// web.xml 파일에 등록하여 호출
public class Listener01 implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 웹 애플리케이션이 시작될 때 호출된다.
System.out.println("Listener01.contextInitialized()");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 웹 애플리케이션이 종료될 때 호출된다.
System.out.println("Listener01.contextDestroyed()");
}
}
- ServletRequestListener
- 요청이 들어 올 때 로그 남기기
@WebListener
public class Listener02 implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
// 요청이 들어 왔을 때 호출된다.
System.out.println("Listener02.requestInitialized()");
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
// sre.getServletRequest()의 리턴 타입은 ServletRequet(범용)이다.
// HttpServletRequest(특정 기능)이라고 이야기(형변환)해준다.
// 쉽게 생각해서 ServletRequest는 빵이고, HttpServletRequest는 단팥빵이다.
// 왜? HttpServletReqeust는 웹 기술을 다룰 수 있기 때문에,
// HTTP 프로토콜로 통신한다면 HttpServletRequest를 사용하는 것이 좋다.
System.out.println("클라이언트 IP: " + request.getRemoteAddr());
System.out.println("요청 URL: " + request.getServletPath());
// Servlet 객체는 범용 환경에서 사용할 수 있도록 기획 & 설계되었지만
// HttpServlet은 Http 프로토콜에서 더 값을 넣기 쉽게 되어있다.
// HttpServlet는 HTTP 프로토콜 정보를 가지고 있기 때문에
// getRemoteAddr()이나 getServletPath()를 사용할 수 있다.
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 요청 처리를 완료할 때 호출된다.
System.out.println("Listener02.requestDestroyed()");
}
}
리스너 vs 필터
- 리스너는 특정 요청을 구분할 수 없다. 요청이 들어오면 무조건 일을 한다.
- 필터는 요청을 구분한다. 조건을 걸어서 필터를 실행하거나 실행하지 않도록 설계할 수 있다.
2. Filter
필터 만들기
- javax.servlet.Filter 인터페이스 규칙에 따라 작성한다.
필터 배포하기
- 방법1) DD 파일(web.xml)에 설정한다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- 필터 등록 -->
<filter>
<filter-name>Filter01</filter-name>
<filter-class>com.eomcs.web.ex02.Filter01</filter-class>
</filter>
<!-- 필터를 적용할 URL을 지정 -->
<filter-mapping>
<filter-name>Filter01</filter-name>
<url-pattern>/ex02/s1</url-pattern>
</filter-mapping>
</web-app>
- 방법2) @WebFilter(“/ex02/*”) 애노테이션으로 설정하면 된다.
//@WebFilter("/ex02/*")
public class Filter02 implements Filter {
...
}
필터의 용도
- 블릿을 실행하기 전후에 필요한 작업을 수행
- 서블릿 실행 전
- 웹브라우저가 보낸 암호화된 파라미터 값을 서블릿으로 전달하기 전에 암호 해제하기
- 웹브라우저가 보낸 압축된 데이터를 서블릿으로 전달하기 전에 압축 해제하기
- 서블릿의 실행을 요청할 권한이 있는지 검사하기
- 로그인 사용자인지 검사하기
- 로그 남기기
- 서블릿 실행 후
- 클라이언트로 보낼 데이터를 압축하기
- 클라이언트로 보낼 데이터를 암호화시키기
public class Filter01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 필터 객체를 생성한 후 제일 처음으로 호출된다.
// 필터가 사용할 자원을 이 메서드에서 준비한다.
// => 웹 애플리케이션을 시작할 때 필터는 자동 생성된다.
System.out.println("Filter01.init()");
}
@Override
public void destroy() {
// 웹 애플리케이션이 종료될 때 호출된다.
// init()에서 준비한 자원을 해제한다.
System.out.println("Filter01.destroy()");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 요청이 들어 올 때 마다 호출된다.
// => 단 필터를 설정할 때 지정된 URL의 요청에만 호출된다.
// => 서블릿이 실행되기 전에 필터가 먼저 실행된다.
// => 서블릿을 실행한 후 다시 필터로 리턴한다.
System.out.println("Filter01.doFilter() : 시작");
// 다음 필터를 실행한다.
// 만약 다음 필터가 없으면,
// 요청한 서블릿의 service() 메서드를 호출한다.
// service() 메서드 호출이 끝나면 리턴된다.
chain.doFilter(request, response);
// 체인에 연결된 필터나 서블릿이 모두 실행된 다음에
// 다시 이 필터로 리턴될 것이다.
System.out.println("Filter01.doFilter() : 종료");
}
}
@WebFilter("/ex02/*")
public class Filter02 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 필터 객체를 생성한 후 제일 처음으로 호출된다.
// 필터가 사용할 자원을 이 메서드에서 준비한다.
System.out.println("Filter02.init()");
}
@Override
public void destroy() {
//
// 웹 애플리케이션이 종료될 때 호출된다.
// init()에서 준비한 자원을 해제한다.
System.out.println("Filter02.destroy()");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 요청이 들어 올 때 마다 호출된다.
// => 단 필터를 설정할 때 지정된 URL의 요청에만 호출된다.
// => 서블릿이 실행되기 전에 필터가 먼저 실행된다.
// => 서블릿을 실행한 후 다시 필터로 리턴한다.
System.out.println("Filter02.doFilter() : 시작");
// 다음 필터를 실행하거나 요청한 서블릿을 실행하려면 다음 코드를 반드시 실행해야 한다.
chain.doFilter(request, response);
// 체인에 연결된 필터나 서블릿이 모두 실행된 다음에 다시 이 필터로 리턴될 것이다.
System.out.println("Filter02.doFilter() : 종료");
}
}
필터 실행 순서
- 필터의 실행 순서를 임의로 조정할 수 없다.
- 필터를 정의할 때 순서에 의존하는 방식으로 프로그래밍 하지 말라.
- 필터의 실행 순서에 상관없이 각 필터가 독립적으로 동작하도록 작성하라.
필터 실행 확인
Listener02.requestInitialized()
클라이언트 IP: 0:0:0:0:0:0:0:1
요청 URL: /ex02/a/s2
Filter02.doFilter() : 시작
/ex02/a/s2 서블릿 실행!
Filter02.doFilter() : 종료
Listener02.requestDestroyed()
Listener02.requestInitialized()
클라이언트 IP: 0:0:0:0:0:0:0:1
요청 URL: /ex02/s1
Filter01.doFilter() : 시작
Filter02.doFilter() : 시작
/ex02/s1 서블릿 실행!
Filter02.doFilter() : 종료
Filter01.doFilter() : 종료
Listener02.requestDestroyed()