Coding 공부/IntelliJ

[IntelliJ_Servlet_MaiaDB]TodoList#7, 리스너, contextInitialized 메소드 사용처, 리스너 예제코드

CBJH 2024. 5. 3. 11:19
728x90
반응형

1. @WebListener in Java

Java에서 @WebListener는 서블릿 컨텍스트, 세션, 요청의 생명주기 이벤트를 처리하는 리스너에 대한 애너테이션입니다. 서블릿 3.0부터 사용되기 시작한 이 애너테이션은 웹 애플리케이션에서 특정 이벤트가 발생했을 때 자동으로 호출되는 리스너 클래스를 정의할 때 사용됩니다.

  1.1 리스너(Listener)의 개념

리스너는 특정 이벤트를 기다리고 이를 감지하면 반응하여 처리를 수행하는 객체를 말합니다. 웹 애플리케이션에서 리스너는 주로 서블릿 컨텍스트, 세션, 요청 등의 생명주기와 관련된 이벤트를 감시합니다. 이벤트가 발생하면 리스너는 사전에 정의된 로직을 실행하여 이벤트에 대응합니다.

  1.2 리스너의 주요 유형

  1. ServletContextListener: 웹 애플리케이션의 시작과 종료 시점에 이벤트를 처리합니다. 예를 들어, 웹 애플리케이션이 시작할 때 초기 리소스를 로드하거나, 종료될 때 리소스를 정리하는 작업을 수행할 수 있습니다.
  2. HttpSessionListener: 세션의 생성과 소멸 시점에 이벤트를 처리합니다. 사용자가 로그인하여 세션이 시작되었을 때 또는 세션이 만료되어 종료될 때 필요한 로직을 실행할 수 있습니다.
  3. 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>