백엔드/웹 개발

리스너(Listener)

study_yeon 2023. 7. 4. 17:54

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 ----------");

	}
	
}

 

초기화
중단
서버에서 Stop하면 중단 됨

▷ 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);
		}

	}
}