[IntelliJ_Servlet_MaiaDB]TodoList#7, 리스너, contextInitialized 메소드 사용처, 리스너 예제코드
1. @WebListener in Java
Java에서 @WebListener는 서블릿 컨텍스트, 세션, 요청의 생명주기 이벤트를 처리하는 리스너에 대한 애너테이션입니다. 서블릿 3.0부터 사용되기 시작한 이 애너테이션은 웹 애플리케이션에서 특정 이벤트가 발생했을 때 자동으로 호출되는 리스너 클래스를 정의할 때 사용됩니다.
1.1 리스너(Listener)의 개념
리스너는 특정 이벤트를 기다리고 이를 감지하면 반응하여 처리를 수행하는 객체를 말합니다. 웹 애플리케이션에서 리스너는 주로 서블릿 컨텍스트, 세션, 요청 등의 생명주기와 관련된 이벤트를 감시합니다. 이벤트가 발생하면 리스너는 사전에 정의된 로직을 실행하여 이벤트에 대응합니다.
1.2 리스너의 주요 유형
- ServletContextListener: 웹 애플리케이션의 시작과 종료 시점에 이벤트를 처리합니다. 예를 들어, 웹 애플리케이션이 시작할 때 초기 리소스를 로드하거나, 종료될 때 리소스를 정리하는 작업을 수행할 수 있습니다.
- HttpSessionListener: 세션의 생성과 소멸 시점에 이벤트를 처리합니다. 사용자가 로그인하여 세션이 시작되었을 때 또는 세션이 만료되어 종료될 때 필요한 로직을 실행할 수 있습니다.
- ServletRequestListener: 요청이 들어오고 응답이 완료될 때의 생명주기를 감시합니다. 이를 통해 요청이 처리되는 동안 필요한 작업을 수행할 수 있습니다.
1.3 @WebListener 사용 예시
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 컨텍스트 초기화 시 실행할 로직
System.out.println("웹 애플리케이션이 시작되었습니다.");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 컨텍스트 종료 시 실행할 로직
System.out.println("웹 애플리케이션이 종료되었습니다.");
}
}
위 예제에서는 MyServletContextListener 클래스가 ServletContextListener를 구현하고 있으며, @WebListener 애너테이션을 통해 리스너로 등록되었습니다. 이 클래스는 웹 애플리케이션의 생명주기 시작과 종료 시점에 각각 메시지를 출력합니다.
1.4 정리
@WebListener 애너테이션은 웹 애플리케이션에서 발생하는 다양한 이벤트를 효과적으로 관리하고 반응할 수 있도록 돕습니다. 이를 통해 개발자는 애플리케이션의 생명주기 관리, 리소스 관리, 사용자 세션의 추적 등 다양한 작업을 보다 체계적으로 처리할 수 있습니다.
1.5 contextInitialized 메소드 사용처
contextInitialized 메소드는 웹 애플리케이션이 시작될 때 실행되는 리스너 메소드로, 다양한 초기화 작업에 사용됩니다. 실무에서는 이 메소드를 통해 웹 애플리케이션의 시작 시 필요한 여러 초기화 작업을 수행합니다. 여기 몇 가지 실제 예시를 들어보겠습니다:
1) 데이터베이스 연결 초기화
웹 애플리케이션에서 데이터베이스와의 연결은 필수적인 부분입니다. contextInitialized에서는 데이터베이스 연결 풀을 초기화하거나, 필요한 데이터베이스 리소스를 사전에 로드하여 애플리케이션 전반에 걸쳐 사용할 수 있도록 설정할 수 있습니다.
@Override
public void contextInitialized(ServletContextEvent sce) {
DataSource ds = new DataSource();
ds.setURL("jdbc:mysql://example.com/db");
ds.setUsername("user");
ds.setPassword("pass");
ServletContext context = sce.getServletContext();
context.setAttribute("DATA_SOURCE", ds);
log.info("데이터베이스 연결 풀이 초기화되었습니다.");
}
2) 외부 시스템 통합 준비
어떤 애플리케이션들은 외부 시스템과의 통합을 필요로 합니다. 예를 들어, 외부 API를 사용하여 초기 데이터를 로드하거나, 외부 서비스와의 인증을 설정하는 등의 작업을 수행할 수 있습니다.
@Override
public void contextInitialized(ServletContextEvent sce) {
ExternalServiceAuthenticator authenticator = new ExternalServiceAuthenticator("API_KEY");
authenticator.initializeConnection();
ServletContext context = sce.getServletContext();
context.setAttribute("AUTHENTICATOR", authenticator);
log.info("외부 서비스 인증이 설정되었습니다.");
}
3) 애플리케이션 설정 로드
설정 파일이나 환경 변수에서 애플리케이션 설정을 읽어와 시스템에 적용하는 경우도 있습니다. 이러한 설정은 보안 설정, 로깅 레벨, 특정 기능의 활성화 여부 등을 포함할 수 있습니다.
@Override
public void contextInitialized(ServletContextEvent sce) {
Configuration config = Configuration.load("config/settings.properties");
ServletContext context = sce.getServletContext();
context.setAttribute("CONFIG", config);
log.info("애플리케이션 설정이 로드되었습니다.");
}
4) 배치 작업 스케줄러 설정
배치 작업이나 주기적으로 수행해야 하는 작업을 스케줄링할 때 사용할 수 있습니다. 스케줄러를 초기화하고, 필요한 배치 작업을 예약합니다.
@Override
public void contextInitialized(ServletContextEvent sce) {
Scheduler scheduler = new Scheduler();
scheduler.scheduleTask(new DailyTask(), "00:00");
ServletContext context = sce.getServletContext();
context.setAttribute("SCHEDULER", scheduler);
log.info("배치 작업 스케줄러가 설정되었습니다.");
}
5) 리소스 캐싱
자주 사용되는 리소스를 서버 시작 시 메모리에 캐시해 두면, 성능 향상에 큰 도움이 됩니다. 이미지, 데이터, 혹은 다른 종류의 자주 조회되는 정보를 캐시에 로드할 수 있습니다.
@Override
public void contextInitialized(ServletContextEvent sce) {
Cache cache = new Cache();
cache.preloadResources();
ServletContext context = sce.getServletContext();
context.setAttribute("CACHE", cache);
log.info("필수 리소스가 캐시에 로드되었습니다.");
}
2. TodoList 추가 코드
- 리스너 패키지
- LoginListener : HttpSessionAttributeListener 인터페이스를 구현해 로그인 할 때, 세션이 추가되면 실행하는 메소드 attributeAdded() 연습
- ExListener : ServletContextListener 인터페이스를 구현해 서버가 처음 시작할때 실행하는 메소드 contextInitialized()와 서버가 끊어질 때 실행하는 메소드 contextDestroyed() 연습
- TodoListController에서 ServletContext servletContext = req.getServletContext();를 받아와 servletContext.getAttribute()로 컨텍스트를 콘솔에 찍어보는 연습
- list.jsp에 <h2>${appName}</h2> EL문을 사용해 ExListener에서 setAttribute한 Name의 벨류값을 반환받는 연습
//ExListener 예제코드
@WebListener
@Log4j2
public class ExListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("-------------Listener init----------------------");
ServletContext servletContext = sce.getServletContext();
servletContext.setAttribute("appName", "jdbcex");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("-------------Listener destroy----------------------");
}
}
//LoginListener 예제코드
@WebListener
@Log4j2
//세션 리스너는 implements가 조금 다르다.
public class LoginListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent event) { //세션에 무언가 추가되었다면 동작할 함수
String name = event.getName(); //세션은 키(name, String), 벨류(value, Object) 값을 갖는다.
Object obj = event.getValue();
if(name.equals("loginInfo")) {
System.out.println("value: " + obj);
}
}
}
//TodoListController doGet에 추가된 부분 예제코드
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
System.out.println("Todo List....");
ServletContext servletContext = req.getServletContext();
System.out.println("appName: " + servletContext.getAttribute("appName"));
}
<!--list.jsp에 추가된 예제 코드-->
<body>
~
<h2>${appName}</h2>
~
</body>