필터(Filter) 와 리스너(Listener) 개념 및 실습

Yeppi's 개발 일기·2022년 5월 26일
0

Servlet&JSP

목록 보기
9/13

1. Filter

1) 필터 란?

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

  • 서블릿 실행 전, 먼저 동작하여 서블릿의 사전 처리 작업 수행
  • 서블릿 실행 후, 응답 메시지가 브라우저에 전송되기 전에 사후 처리 작업 수행
  • 서블릿 컨테이너가 구동되는 시점에 생성
  • Pre-Loading
    해당 컨테이너가 구동/생성되는 시점에 객체가 생성되는 것


2) 동작 방식

  1. 브라우저가 서버에게 HTTP 요청

  2. 서블릿 컨테이너에게 요청된 정보를 필터가 가로채고 사전처리

  3. 필터가 서블릿 컨테이너를 호출

  4. 서블릿 컨테이너가 실행

  5. 필터가 사후처리

  6. 브라우저에게 HTTP 응답


ex. 인코딩과 유사

  • 사전 처리할때 인코딩을 한번만 처리하면?
    insert, update, delete, get 모두 변경하지 않아도됨 → 수정 간편해짐
  • 필터를 적용하면?
    수정사항 발생 시 필터만 수정하고 나머지 파일은 변경하지 않아도됨 → 수정 간편

프리로딩

  • = 필터
  • ❗ 브라우저 요청 없이도 생성 가능 ❗
  • 톰캣(서버) 구동 후,
    필터가 이미 등록되어있는 상태에서 컨테이너가 web.xml 읽어들이면?
    컨테이너가 읽어들이자마자 TimeCheckFilter 바로 생성

레이지로딩

  • = 서블릿
  • ❗ 브라우저가 요청 시 생성됨 ❗


3) 필터 생성

  • 필터 클래스 생성
  • 필터 위저드로 필터 클래스 생성하면?
    서블릿처럼 web.xml 파일에 자동 등록
  • 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>
  • TimeCheckFilter.java
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() 호출



4) 📌필터 실습📌

특정 파일 요청 시에만 필터 실행하기

  • login.do 요청시에만 필터 실행되도록 설정
  • web.xml
	<filter-mapping>
		<filter-name>timeCheck</filter-name>
		<url-pattern>/login.do</url-pattern>
	</filter-mapping>
  • TimeCheckFilter.java
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("---[ 사후 처리 ]---");

	}

}
  1. 브라우저가 서버에게 요청

  2. 필터 즉, doFilter() 가 호출

  3. 여기서 사전처리

  4. chain.doFilter() 에서 서블릿 호출
    해당 코드 주석 처리하면? 서블릿 실행 안됨

  5. 서블릿 실행

  6. 실행 후 사후 처리

  7. 브라우저에게 응답 전송



서블릿 소요 시간 구하기 (2가지)

방법 1 ) web.xml 사용

  • TimeCheckFilter.java
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)초");

	}
}
  • 브라우저에서 실행

👉 이때 여러 서블릿이 있다면? 속도가 느린 서블릿을 찾아서 속도를 빠르게 해줄 수 있음

  • web.xml 에서 *.do 모든 서블릿 동작하게
	<filter-mapping>
		<filter-name>timeCheck</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
  • 로그인, 로그아웃, 게시글 열람 등 클릭 할때마다 모든 서블릿 실행 속도를 알 수 있음


방법 2 ) @ 어노테이션 사용

  • TimeCheckFilter.java
@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)초");

	}

}
  • 위 방법1과 결과는 동일


반복되는 인코딩 필터 처리하기 (2가지)

방법 1 ) web.xml 사용

  • 인코딩 필터 생성 및 설정
  • CharacterEncodingFilter.java
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);
	}
}
  • 그리고 기존에 인코딩 설정이 되어있는 서블릿 파일(ex. insertboard, update, insertuser 등)
    서블릿에 있는 인코딩 설정을 모두 지우기

👉 인코딩 설정 한번에! 아주 간편



방법 2 ) @ 어노테이션 사용

  • web.xml 에 자동으로 등록된 설정도 지우기
	<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>
  • TimeCheckFilter.java 에서 어노테이션으로 설정
@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 가 묵시적 타입 변환된 것을 다시 원래 타입으로 되돌리기
  • 그러면 어느 URL(어느 페이지에서 동작했는지)인지 확인 가능
@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)초");
		
	}
}



5) 여러개 필터 등록

여러개 필터 동작

  1. HTTP 요청

  2. TimeCheckFilter 에서 *.do 의 사전 처리 수행

  3. chain.doFilter() 에서 CharacterEncodingFilter 로 요청 전달

  4. CharacterEncodingFilter 에서 *.do 의 사전 처리 수행

  5. chain.doFilter() 에서 서블릿으로 요청 전달

  6. 서블릿 실행

  7. 서블릿에서 CharacterEncodingFilterchain.doFilter() 로 응답 전달

  8. CharacterEncodingFilter 에서 *.do 의 사후 처리 수행

  9. 사후처리가 끝나면 TimeCheckFilterchain.doFilter() 로 응답 전달

  10. 사후처리가 끝나면 브라우저에게 HTTP 응답


  • 여러개 필터가 등록된 경우
    → 필터하나당 하나의 기능 가짐
  • 모든 *.do 에서 실행 될 때는?
    TimeCheckFilter, CharacterEncodingFilter 중 뭐가 먼저 실행되는 지 중요하지 않음

필터체인

  1. 사전처리→ 다음 필터의 사전처리→ . .

  2. 필터가 더이상 없다면 서블릿 수행

  3. 사후 처리→ 다음 필터의 사후처리→ . .



2. Listener

1) 리스너 란?

이벤트가 발생하면 실행

  • 청취자
  • 이벤트가 발생하길 계속 기다리고 있음
  • 리스너 라이프사이클은 2가지만 존재
    Containerstartstop


2) 리스너 생성

  • 리스너 파일 생성
  • web.xml
<listener>
    <listener-class>com.ssamz.web.common.BoardWebContextListener</listener-class>
</listener>
  • 서블릿, 필터와 마찬가지로 web.xml 설정 삭제 후 @어노테이션 설정 가능
@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) 리스너 종류

이벤트가 발생될 수 있는 객체는 딱 3가지
ServletContext 객체 > HttpSession 객체 > HttpServletRequest 객체


객체의 생성/삭제

리스너기능
ServletContextListenerServletContext 객체의 생성과 삭제 이벤트 처리
HttpSessionListenerHttpSession 객체의 생성과 삭제 이벤트 처리
ServletRequestListenerHttpServletRequest 객체의 생성과 삭제 이벤트 처리
  • HttpSessionListener → 거의 사용 안함
    세션을 생성하거나 삭제

객체에 정보 등록/삭제/대체

리스너기능
ServletContextAttributeListenerServletContext 객체에 정보 등록/삭제/대체 이벤트 처리
HttpSessionAttributeListenerHttpSession 객체에 정보 등록/삭제/대체 이벤트 처리
ServletRequestAttributeListenerHttpServletRequest 객체에 정보 등록/삭제/대체 이벤트 처리
  • HttpSessionAttributeListener → 가끔 사용
    세션 생성 후, 세션에 뭔가가 등록될 때/대체될 때


4) 📌HttpSession📌

  • LintenerTestServlet.java
@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"); // 세션 객체 정보 삭제
	}
}
  • 실행결과


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

profile
imaginative and free developer. 백엔드 / UX / DATA / 기획에 관심있지만 고양이는 없는 예비 개발자👋

0개의 댓글