리스너(Listener)
2023.07.03 수업
교재 : 자바 웹 개발 워크북(215p)
※ 필터와 리스너의 차이
* 필터 : 들어온 요청을 서블릿으로 보내고, 또 서블릿이 작성한 응답을 클라이언트로 보내기 전에 특별한 처리가 필요한 경우에 사용
-> 여러 서블릿에서 반복적으로 수행해야 하는 작업을 공통으로 처리할 수 있다는 장점
* 리스너 : 서블릿에서 다양한 시점에서 발생하는 이벤트와 이벤트를 처리하기 위한 인터페이스
* 필터는 클라이언트와 서블릿 사이에서 작업을 미리 수행하고,
리스너는 오가는 요청이나 응답에 관계 없이 특정 이벤트에 의해 실행된다.
● 리스너
- 인터페이스
- 이벤트라는 특정한 데이터가 발생하면 자동으로 실행
-> 기존의 코드를 변경하지 않고 추가적인 기능을 수행할 수 있음
- 내가 정의한 특별상황에서만 적용
* 이벤트는 발생한 특정 사건이나 일을 말한다
ex) 마우스 클릭, 키보드 키 입력, 버튼 클릭, 텍스트 입력, 웹어플리케이션의 시작이나, 종료, 특정 객체의 생성, 소멸 등
* 옵저버(observer) 패턴
- 특정한 변화를 구독하는 객체들을 보관하고 있다가 변화가 발생(publish)하면 구독 객체들을 실행
※ 리스너 종류
ServletContext 객체(서버가 살아있는 동안) > HttpSession 객체(브라우저가 살아있는 동안) > HttpServletRequest 객체(요청/응답이 처리되는 동안)
- 해당 웹 애플리케이션이 시작되거나 종료될 때 특정한 작업을 수행
- HttpSession에 특정한 작업 대한 감시와 처리
- HttpServletRequest에 특정한 작업에 대한 감시와 처리
이벤트 소스 | 이벤트 리스너 | 발생 이벤트 객체 |
ServletContext | ServletContextListener | ServletContextEvent |
ServletContextAttributeListener | ServletContextAttributeEvent | |
HttpSession | HttpSessionListener | HttpSessionEvent |
HttpSessionAttributeListener | HttpSessionBindingEvent | |
ServletRequest | ServletRequestListener | ServletRequestEvent |
ServletRequestAttributeListener | ServletRequestAttributeEvent |
▶ 연습
▷ ServletContextListener
- 해당 프로젝트가 실행되자마자 실행되었으면 하는 작업에 사용
- 스프링 프레임워크를 웹 프로젝트에서 미리 로딩하는 작업을 처리할때 사용
- 웹어플리케이션의 시작과 종료 이벤트를 핸들링할 수 있는 이벤트 리스너
패키지 org.zerock.w2.listener
인터페이스 ServletContextListener
클래스 W2AppListener
- @WebListener 추가
- ServletContextListener 인터페이스 구현
[contextInitialized() / contextDestroyed() 메소드를 구현해야함]
-> 파라미터로 ServletContextEvent 라는 객체가 전달됨
- ServletContextEvent를 이용하여 ServletContext에 접근
- ServletContext : 현재의 웹 애플리케이션 내 모든 자원들을 같이 사용하는 공간, JSP나 컨트롤러 등에서 이를 활용, Application 범위
- ServletContext를 이용해 저장된 객체는 서블릿 JSP/EL에서 사용 가능
- ServletContext에서는 setAttribute()를 이용하여 원하는 이름으로 객체 보관 가
package org.zerock.w2.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import lombok.extern.log4j.Log4j2;
@WebListener
@Log4j2
public class W2AppListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
// 웹앱이 실행되면 전체 앱에서 사용할 초기화 작업
log.info("---------- init ----------");
log.info("---------- init ----------");
log.info("---------- init ----------");
// 앱웹의 작업 메모리공간 사용
// Application 레벨 변수 범위 얻기
ServletContext servletContext = sce.getServletContext();
// 애플리케이션 레벨 변수 범위에 데이터 추가
servletContext.setAttribute("appName", "W2");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("---------- destroy ----------");
log.info("---------- destroy ----------");
log.info("---------- destroy ----------");
}
}
▷ TodoListController 수정
HttpServletRequest의 getServletContext() 메소드 활용하여 ServletContext 이용
package org.zerock.w2.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.zerock.jdbcex.domain.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import lombok.extern.log4j.Log4j2;
// 로그인 코드를 구현하지 않음
@WebServlet(name = "todoListController", value = "/todo/list")
@Log4j2
public class TodoListController extends HttpServlet {
private static final long serialVersionUID = 1L;
private TodoService todoService = TodoService.INSTANCE;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
log.info("todo list.......");
ServletContext servletContext = req.getServletContext(); // 추가
log.info("appName : " + servletContext.getAttribute("appName")); // 추가
try {
List<TodoDTO> dtoList = todoService.listAll();
req.setAttribute("dtoList", dtoList);
req.getRequestDispatcher("/WEB-INF/todo/list.jsp").forward(req, res);
} catch(Exception e) {
log.error(e.getMessage());
throw new ServletException("list error");
}
}
}
▷ list.jsp 수정
- ServletContext를 이용해 저장된 객체 활용
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Todo List</h1>
<h2>${appName}</h2>
<h2>${userInfo}</h2>
<h3>${userInfo.mname}</h3>
<c:forEach var="dto" items="${ dtoList }" >
<!-- 상세리스트로 보기 -->
<li>
<!-- 게시물 번호 읽기 링크 -->
<span>
<a href="/todo/read?tno=${ dto.tno }">${ dto.tno }</a>
</span>
<!-- 게시물 제목 -->
<span>${ dto.title }</span>
<!-- todo 게시 일자 -->
<span>${ dto.dueDate }</span>
<!-- todo 완료 여부 -->
<span>${ dto.finished ? "DONE" : "NOT YET" }</span>
</li>
</c:forEach>
<form action="/logout" method="post">
<button>Logout</button>
</form>
</body>
</html>
▷ 세션 관련 리스너
- 서블릿의 리스너 중에는 HttpSession 관련 작업을 감시하는 HttpSessionListener 나 HttpSessionAttributeListener 등을 활용하여 리스너들을 등록할 수 있다.
- HttpSession이 생성되거나 setAttribute()등의 작업이 이루어질 때 감지
패키지 org.zerock.w2.listener
클래스 LoginListener
인터페이스 HttpSessionAttributeListener 구현
[attributeAdded(), attributeRemoved(), attributeRe-placed()]
package org.zerock.w2.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import lombok.extern.log4j.Log4j2;
@WebListener
@Log4j2
public class LoginListener implements HttpSessionAttributeListener{
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
// 발생한 이벤트 이름 얻기
String name = event.getName();
Object obj = event.getValue();
if (name.equals("userInfo")) {
log.info("A user logined.....");
log.info(obj);
}
}
}