리스너란? 이벤트, 리스너, 이벤트핸들러 간략 개념정리
리스너는 단어의 뜻으로 보자면 (소리를) 듣는 사람, 청취자입니다. 프로그래밍에서의 리스너는 무언가 소리를 듣는 사람을 뜻하기 보다는 특정 이벤트(특정한 사건)가 발생하기를 '귀 기울여' 기다리다가 실행되는 컴포넌트(메서드나 함수)를 말합니다. 

리스너는 이벤트가 발생함과 동시에 특정 행동을(메서드나 함수를 실행)하는데, 이것을 이벤트 핸들링이라고 합니다. 따라서 리스너를 이벤트 핸들러라고 부르기도 합니다.

여기서 이벤트라는 말이 등장하는데, 이벤트발생한 특정 사건이나 일을 말하는데, 예를 들어보자면 마우스 클릭, 키보드 키 입력, 버튼 클릭, 텍스트 입력 등이 있습니다.

꼭 입출력과 관련된것만이 이벤트는 아니며, 이 글에서 설명하려는 Servlet/JSP의 리스너와 같이 웹어플리케이션의 시작이나, 종료, 특정 객체의 생성, 소멸과 같은것도 이벤트라고 할 수 있습니다. 추가적으로 이벤트 소스가 있는데 이벤트 소스는 이벤트가 발생한 대상(이벤트 발생원)을 말합니다. 앞서 설명했던 '마우스 클릭' 이라는 이벤트의 이벤트 소스는 마우스입니다.

그런 개념에서는 클라이언트로부터의 요청(이벤트)이 발생했을때 실행되는 Servlet도 일종의 리스너라고 할 수 있습니다.

  • 이벤트 - 발생한 특정 사건(마우스 클릭, 키보드 입력, 클라이언트로부터의 HTTP 요청, 웹어플리케이션 시작, 웹어플리케이션 종료 등)
  • 이벤트 소스 - 이벤트가 발생한 대상(근원지)으로 마우스, 키보드, 웹어플리케이션(ServletContext) 등
  • 리스너, 핸들러 - 이벤트가 발생되기를 기다렸다가 발생시 실행되는 메서드나 함수. 또는 메서드를 가진 객체

즉 정리하자면 리스너는 수많은 이벤트 소스들로부터 이벤트가 발생하기를 기다리는(귀 기울여 청취하는) 컴포넌트(함수나 객체)입니다. Java에서 리스너는 객체가 되며, 특정 이벤트가 발생했을때 실행되는(이벤트를 처리할) 메서드를 가지고 있습니다.

이벤트 리스너와 이벤트 핸들러 라는 용어는 사람에 따라서 이 둘이 다르다고 하는 분들도 많이 계시지만 공식적으로 구분하여 정의되지는 않으며, 대부분 크게 구분하여 사용하지는 않는 것 같습니다. 깊은 지식이 없는 제 기준으로는 이 둘을 같은것으로 설명하고 있으니 참고 부탁드립니다.





Servlet/JSP 이벤트소스와 리스너 종류
Servlet/JSP는 웹 어플리케이션을 개발하기 위한 기술이므로, 웹 환경에 관련된 이벤트들이 존재합니다. 따라서 웹과 관련된 이벤트를 리스닝하고 처리할 수 있는 리스너를 구현해야 합니다. Servlet/JSP에서는 웹 환경과 관련된 이벤트 리스너를 구현할 수 있도록 몇 가지 리스너 인터페이스를 제공하고 있습니다.

이벤트 소스
이벤트 리스너
발생 이벤트 객체
설명



ServletContext

ServletContextListener

ServletContextEvent
웹어플리케이션의 시작, 종료 이벤트에 대한 이벤트 리스너입니다. 핸들러 메서드에서는 ServletContext에 대한 참조를 얻을 수 있습니다.

ServletContextAttributeListener

ServletContextAttributeEvent
ServletContext에 attribute를 추가하거나 제거, 수정됐을 때에 대한 이벤트 리스너입니다. 핸들러 메서드에서는 추가하거나 제거, 수정된 attribute 정보를 얻을 수 있습니다.



HttpSession

HttpSessionListener

HttpSessionEvent
HTTP 세션의 시작, 종료 이벤트에 대한 이벤트 리스너입니다. 핸들러 메서드에서는 현재 세션 객체를 얻을 수 있습니다.

HttpSessionAttributeListener

HttpSessionBindingEvent
HttpSession에 attribute를 추가하거나 제거, 수정됐을 때에 대한 이벤트 리스너입니다. 핸들러 메서드에서는 추가하거나 제거, 수정된 attribute 정보를 얻을 수 있습니다.




ServletRequest

ServletRequestListener

ServletRequestEvent
클라이언트로부터의 요청으로 인한 ServletRequest 생성과 응답 이후 ServletRequest 제거시에 대한 이벤트 리스너입니다. 핸들러 메서드에서는 요청에 관련된 정보와 ServletContext에 대한 참조를 얻을 수 있습니다.
ServletRequestAttributeListener
ServletRequestAttributeEvent
ServletRequest에 attribute를 추가하거나 제거, 수정됐을 때에 대한 이벤트 리스너입니다. 핸들러 메서드에서는 추가하거나 제거, 수정된 attribute 정보를 얻을 수 있습니다.
위의 표 이외에도 HttpSessionActivationListenerHttpSessionBindingListenerAsyncListener 등이 있습니다.




ServletContextListener
리스너의 종류가 많으므로 이번 글에서는 ServletContextListener에 대해 알아보도록 하겠습니다. ServletContextListener는 인터페이스명에서도 알 수 있듯이 ServletContext와 관련된 리스너입니다. ServletContext는 웹 어플리케이션과 1:1 로 연결된 객체이므로 웹 어플리케이션과 관련된 이벤트 리스너라는것을 눈치 채셨을것입니다.

맞습니다. ServletContextListener웹어플리케이션의 시작과 종료 이벤트를 핸들링할 수 있는 이벤트 리스너입니다. 추가적으로 덧붙이자면 스프링 프레임워크의 루트 어플리케이션 컨텍스트인 ContextLoaderListener 또한 ServletContextListener의 구현체입니다. (무슨 말인지 이해가 안되신다면 흘리셔도 좋습니다. 이글에서는 중요하지 않으므로..)



ServletContextListener의 메서드
ServletContextListener 인터페이스의 명세를 보자면 다음과 같습니다. 어떤 리스너든 인터페이스 명세를 보면 대충 어떤 이벤트를 핸들링 할 수 있을지 감을 잡으실 수 있습니다.
public interface ServletContextListener extends EventListener {
 
 
    public void contextInitialized(ServletContextEvent sce);
 
 
    public void contextDestroyed(ServletContextEvent sce);
 
 
}
cs

public void contextInitialized(ServletContextEvent sce) - 웹어플리케이션 시작시 호출되며, 웹 어플리케이션의 모든 필터 또는 서블릿이 초기화 되기 전에 호출되는 메서드입니다.
public void contextDestroyed(ServletContextEvent sce) - 웹어플리케이션 종료시 호출되며, 웹 어플리케이션의 모든 필터 또는 서블릿이 종료된 이후 호출되는 메서드입니다.





리스너 구현 및 설정
그럼 앞서 설명한 ServletContextListener를 구현하고 등록해보도록 하겠습니다.


리스너 등록 및 처리 과정
리스너 등록 및 처리 과정을 이해하기 쉽게 설명하자면 다음과 같습니다.

  1. 개발자는 처리하고싶은 이벤트에 관련된 이벤트 리스너 인터페이스를 구현하고 web.xml에 설정을 등록합니다.
  2. 톰캣과 같은 서블릿 컨테이너는 web.xml 설정을 참고하여 개발자가 구현해둔 리스너 객체를 생성하고 관리하게 됩니다.
  3. 특정 이벤트가 발생했을때 그 이벤트와 관련된 리스너가 있는지 확인하여 해당 리스너에게 알려줍니다. 예를 들어 A라는 이벤트가 발생하면 자신이 생성하여 관리하던 리스너 중 A 이벤트와 관련된 리스너에게 알려주는것입니다.
  4. 리스너에는 개발자가 구현해둔 이벤트 핸들링 메서드가 있으므로 해당 메서드가 실행됩니다(이벤트 처리).





리스너 클래스 구현
ServletContextListener를 상속받은 리스너를 구현합니다. 저는 MyContextListener라는 이름으로 구현하였습니다.
package listener;
 
 
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
 
public class MyContextListener implements ServletContextListener {
 
 
    public void contextInitialized(ServletContextEvent sce) throws RuntimeException{
        ServletContext context = sce.getServletContext();
        System.out.println("!!!!!웹 어플리케이션 시작!!!!!");
        System.out.println("서버 정보 : " + context.getServerInfo());
    }
    
    public void contextDestroyed(ServletContextEvent sce)  {
        System.out.println("!!!!!웹 어플리케이션 종료!!!!!");
    }
    
}
cs


웹 어플리케이션이 시작되고 종료될 때 콘솔에 로그를 출력하도록 하였습니다. 이벤트 핸들러 메서드에는 매개변수로 ServletContextEvent를 넘겨받는데, ServletContext의 참조를 얻을 수 있습니다. 서블릿 컨테이너가 웹어플리케이션이 시작되고 종료될때 ServletContextEvent 객체를 넘겨주는 것입니다. 저는 이벤트 객체를 통해 ServletContext의 참조를 얻어서 getServletContext() 메서드를 통해 서버 정보를 출력하도록 했습니다.




web.xml에 리스너 설정하기
리스너 클래스를 구현했다면 web.xml에 다음과 같이 리스너를 등록해둡니다. 구현한 리스너 클래스의 패키지명과 클래스명을 적어줍니다.
  <listener>
      <listener-class>listener.MyContextListener</listener-class>
  </listener>
cs



<listener>태그와 <listener-class> 태그에 대한 설명
리스너는 <listener> 태그를 사용하여 등록하며 반드시 <listener-class>태그를 통해 구현해둔 리스너 클래스를 명시해 주어야 합니다. 
  <listener>
      <listener-class>listen.DataSourceListener</listener-class>
  </listener>
  
  <listener>
      <listener-class>listen.JmsListener</listener-class>
  </listener>
cs

<listener> - 리스너를 등록할 때 사용하는 태그입니다. 여러개의 리스너를 등록할 때는 <listener> 태그를 여러개 사용하면 됩니다.
<listener-class><listener>태그의 하위 태그로 반드시 하나를 지정해야 하며, 리스너 인터페이스를 구현한 리스너 클래스를 패키지명을 포함하여 지정합니다.





ServletContextListener 테스트
앞서 작성하고 web.xml에 등록해 두었던 MyContextListener 를 테스트 해보도록 하겠습니다. MyContextListener는 웹 어플리케이션 시작과 종료에 대한 이벤트 리스너이므로 서버를 켰다 꺼보는 것으로 테스트해볼 수 있습니다.

저는 이클립스에 연동해둔 톰캣을 기준으로 테스트했습니다.

서버시작시 아래와 같이 콘솔이 출력되었습니다. contextInitialized() 메서드가 실행되었다는 뜻입니다.



서버를 stop하니 톰캣이 종료되면서 아래와 같이 콘솔이 출력되었습니다. contextDestroyed() 메서드가 실행되었다는 뜻입니다. 톰캣을 종료하면 콘솔로그가 사라져 버릴 수 있는데 디버깅 모드로 contextDestroyed() 메서드에 브레이크 포인트를 걸어두면 확인하기 쉽습니다.







같은 이벤트에 대해 리스너가 여러개일때의 처리 순서
예를 들어 ServletContextListener를 구현한 같은 종류의 리스너가 두개 이상 등록되어 있다면 웹 어플리케이션이 시작될때에는 등록된 순서(DataSourceListener -> JmsListener)대로 실행되며, 종료 메서드는 반대의 순서(JmsListener -> DataSourceListener)로 실행됩니다.

마치 필터가 여러개 등록되었을때 요청과 응답의 필터링 순서가 반대인것과 비슷합니다.
  <listener>
      <listener-class>listen.DataSourceListener</listener-class>
  </listener>
  
  <listener>
      <listener-class>listen.JmsListener</listener-class>
  </listener>
cs




@WebListener 어노테이션으로 리스너 등록하기
servlet 3.0 부터는 web.xml 설정 없이 웹 컴포넌트를 등록할 수 있도록 여러가지 어노테이션을 제공하고 있는데, 다음과 같이 @WebListener를 리스너 클래스에 붙여주면 web.xml에 설정하지 않아도 리스너가 웹 컨테이너에 등록됩니다.
@WebListener
public class DataSourceListener implements ServletContextListener {
...
cs



다음글
블로그 이미지

도로락

IT, 프로그래밍, 컴퓨터 활용 정보 등을 위한 블로그

,