서블릿에 대한 사전 처리 작업 + 사후 처리 작업

브라우저가 서버에게 HTTP 요청
서블릿 컨테이너에게 요청된 정보를 필터가 가로채고 사전처리
필터가 서블릿 컨테이너를 호출
서블릿 컨테이너가 실행
필터가 사후처리
브라우저에게 HTTP 응답
web.xml 읽어들이면?TimeCheckFilter 바로 생성
web.xml 파일에 자동 등록 <filter>
<display-name>timeCheck</display-name>
<filter-name>timeCheck</filter-name>
<filter-class>com.ssamz.web.common.TimeCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>timeCheck</filter-name>
<url-pattern>/getBoardList.do</url-pattern>
</filter-mapping>
public class TimeCheckFilter extends HttpFilter implements Filter {
public TimeCheckFilter() {
System.out.println("===> TimeCheckFilter 생성");
}
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("===> init() 호출");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("===> doFilter() 호출");
chain.doFilter(request, response);
}
public void destroy() {
System.out.println("===> destroy() 호출");
}
}
실행 결과
톰캣 구동하자마자 TimeCheckFilter 생성
필터 생성 직후 init() 호출
브라우저에서 url로 입력 시 , doFilter() 호출
삭제되고 리로딩 되는 순간 destory() 호출
login.do 요청시에만 필터 실행되도록 설정 <filter-mapping>
<filter-name>timeCheck</filter-name>
<url-pattern>/login.do</url-pattern>
</filter-mapping>
public class TimeCheckFilter extends HttpFilter implements Filter {
public TimeCheckFilter() {
System.out.println("===> TimeCheckFilter 생성");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("---[ 사전 처리 ]---");
chain.doFilter(request, response);
System.out.println("---[ 사후 처리 ]---");
}
}

브라우저가 서버에게 요청
필터 즉, doFilter() 가 호출
여기서 사전처리
chain.doFilter() 에서 서블릿 호출
해당 코드 주석 처리하면? 서블릿 실행 안됨
서블릿 실행
실행 후 사후 처리
브라우저에게 응답 전송
public class TimeCheckFilter extends HttpFilter implements Filter {
public TimeCheckFilter() {
System.out.println("===> TimeCheckFilter 생성");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// System.currentTimeMillis()가 호출될 때의 시간 정보를 return
long beforeTime = System.currentTimeMillis();
chain.doFilter(request, response); // Servlet 호출
// System.currentTimeMillis()가 호출한 후의 시간 정보를 return
long afterTime = System.currentTimeMillis();
System.out.println("서블릿 실행에 소요된 시간 : " + (afterTime - beforeTime) + "(ms)초");
}
}

👉 이때 여러 서블릿이 있다면? 속도가 느린 서블릿을 찾아서 속도를 빠르게 해줄 수 있음
*.do 모든 서블릿 동작하게 <filter-mapping>
<filter-name>timeCheck</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>

@ 어노테이션 사용@WebFilter(urlPatterns = "*.do")
public class TimeCheckFilter extends HttpFilter implements Filter {
public TimeCheckFilter() {
System.out.println("===> TimeCheckFilter 생성");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long beforeTime = System.currentTimeMillis(); // System.currentTimeMillis()가 호출될 때의 시간 정보를 return
chain.doFilter(request, response); // Servlet 호출
long afterTime = System.currentTimeMillis(); // System.currentTimeMillis()가 호출한 후의 시간 정보를 return
System.out.println("서블릿 실행에 소요된 시간 : " + (afterTime - beforeTime) + "(ms)초");
}
}


public class CharacterEncodingFilter extends HttpFilter implements Filter {
private String encoding;
public CharacterEncodingFilter() {
System.out.println("===> CharacterEncodingFilter() 생성");
}
public void init(FilterConfig fConfig) throws ServletException {
encoding = fConfig.getInitParameter("encoding");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 모든 서블릿 인코딩 처리
request.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
}
👉 인코딩 설정 한번에! 아주 간편
@ 어노테이션 사용 <display-name>BoardWeb</display-name>
<welcome-file-list>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
@WebFilter(urlPatterns = "*.do", initParams = @WebInitParam(name = "encoding", value = "UTF-8"))
public class CharacterEncodingFilter extends HttpFilter implements Filter {
private String encoding;
public CharacterEncodingFilter() {
System.out.println("===> CharacterEncodingFilter() 생성");
}
public void init(FilterConfig fConfig) throws ServletException {
encoding = fConfig.getInitParameter("encoding");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 모든 서블릿 인코딩 처리
request.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
}
doFilter() 매개변수🧐Q. doFilter 메서드가 매개변수로 받는 것은 HttpServletRequest/Response가 아니다. 그 이유는?
ServletRequest/Response 에서 받아 편하게 사용하기 위해Q. 만약, HttpServletRequest 를 사용한다면?
HttpServletRequest 가 묵시적 타입 변환된 것을 다시 원래 타입으로 되돌리기 @WebFilter(urlPatterns = "*.do")
public class TimeCheckFilter extends HttpFilter implements Filter {
public TimeCheckFilter() {
System.out.println("===> TimeCheckFilter 생성");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request; // 명시적 타입 변환
String uri = req.getRequestURI(); // URI 정보 추출 가능
long beforeTime = System.currentTimeMillis(); // System.currentTimeMillis()가 호출될 때의 시간 정보를 return
chain.doFilter(request, response); // Servlet 호출
long afterTime = System.currentTimeMillis(); // System.currentTimeMillis()가 호출한 후의 시간 정보를 return
System.out.println(**uri** + " 요청 처리에 소요된 시간 : " + (afterTime - beforeTime) + "(ms)초");
}
}


HTTP 요청
TimeCheckFilter 에서 *.do 의 사전 처리 수행
chain.doFilter() 에서 CharacterEncodingFilter 로 요청 전달
CharacterEncodingFilter 에서 *.do 의 사전 처리 수행
chain.doFilter() 에서 서블릿으로 요청 전달
서블릿 실행
서블릿에서 CharacterEncodingFilter 의 chain.doFilter() 로 응답 전달
CharacterEncodingFilter 에서 *.do 의 사후 처리 수행
사후처리가 끝나면 TimeCheckFilter 의 chain.doFilter() 로 응답 전달
사후처리가 끝나면 브라우저에게 HTTP 응답
*.do 에서 실행 될 때는?TimeCheckFilter, CharacterEncodingFilter 중 뭐가 먼저 실행되는 지 중요하지 않음사전처리→ 다음 필터의 사전처리→ . .
필터가 더이상 없다면 서블릿 수행
사후 처리→ 다음 필터의 사후처리→ . .
이벤트가 발생하면 실행
Container 의 start 과 stop
<listener>
<listener-class>com.ssamz.web.common.BoardWebContextListener</listener-class>
</listener>
@어노테이션 설정 가능@WebListener
public class BoardWebContextListener implements ServletContextListener {
public BoardWebContextListener() {
System.out.println("===> BoardWebContextListener 생성");
}
public void contextInitialized(ServletContextEvent sce) {
System.out.println("---> ServletContxt 생성");
}
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("---> ServletContxt 삭제");
}
}
ServletContextListener
contextInitialized() 메서드를 호출contextDestroyed() 메서드 호출ServletContext 이벤트와 관련된 메서드contextInitialized() 딱 한번 호출contextDestroyed() 딱 한 번 호출
BoardWebContextListener 생성이벤트가 발생될 수 있는 객체는 딱 3가지
ServletContext 객체 > HttpSession 객체 > HttpServletRequest 객체

| 리스너 | 기능 |
|---|---|
| ServletContextListener | ServletContext 객체의 생성과 삭제 이벤트 처리 |
| HttpSessionListener | HttpSession 객체의 생성과 삭제 이벤트 처리 |
| ServletRequestListener | HttpServletRequest 객체의 생성과 삭제 이벤트 처리 |
| 리스너 | 기능 |
|---|---|
| ServletContextAttributeListener | ServletContext 객체에 정보 등록/삭제/대체 이벤트 처리 |
| HttpSessionAttributeListener | HttpSession 객체에 정보 등록/삭제/대체 이벤트 처리 |
| ServletRequestAttributeListener | HttpServletRequest 객체에 정보 등록/삭제/대체 이벤트 처리 |
@WebServlet("/sessionTest.do")
public class LintenerTestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(); // 세션 객체 생성
session.setAttribute("person", "yeppi"); // 세션 객체 정보 추가
session.setAttribute("person", "gurum"); // 세션 객체의 정보 덮어쓰기
response.removeAttribute("person"); // 세션 객체 정보 삭제
}
}

문서 작업으로 따지면 템플릿 같은 기능을 가진 필터를 배우고 나니
웹 유지보수 작업이 한결 편해지는 기술이라고 느꼈다