Java

2024.07.02 JSP 프로그래밍 기본 서블릿 필터와 리스너란 뭘까?

정훈5 2024. 7. 2. 09:37

 

1. 필터란?

필터(Filter)는 서블릿이나 JSP에 요청이 도달하기 전에 요청과 응답을 가로채서 처리하는 컴포넌트입니다.

필터요청을 수정하거나, 응답을 변경하거나, 로깅 및 인증 등의 작업을 수행할 수 있습니다.

(서버 상에서 메모리에 올라가서 동작하는 것)

 

 

필터는 클라이언트와 자원사이에 여러개의 필터가 모여서 하나의 체인(Chain)을 형성할수 도 있습니다.

 

AS 서버에 필터를 설정하는 방법은 web.xml 파일에서 설정하거나 자바 코드측에 애노테이션을 사용하여 설정할 수 있는 방법이 존재 합니다.

 

예시 코드 - web.xml 파일에 설정 가능

<filter>
    <filter-name>LoggingFilter</filter-name>
    <filter-class>com.example.LoggingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>LoggingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

예시 코드 - java 파일에 설정 가능

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class LoggingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 작업
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 요청 전 처리
        System.out.println("Request received at " + new java.util.Date());
        chain.doFilter(request, response); // 다음 필터나 서블릿으로 요청 전달
        // 응답 후 처리
        System.out.println("Response sent at " + new java.util.Date());
    }

    @Override
    public void destroy() {
        // 필터 종료 작업
    }
}

 

필터의 주요 기능

  • 요청 및 응답의 전처리 및 후처리
  • 로깅 처리
  • 인증 및 권한 부여
  • 데이터 압축

서블릿 리스너

리스너란?

컨테이너에서 발생하는 이벤트를 모니터링하다가 특정 이벤트가 발생하면 실행되는

특수한 서블릿으로, '이벤트 리스너'라고도 한다. 즉, 리스너는 웹 애플리케이션에서 특정한 사건이 일어났을 때
그것을 알아차리고 적절한 처리를 하는 역할을 합니다.

 

예를 들어 애플리케이션이 시작되거나 종료될 때 발생하는 사건 이나 사용자가 웹사이트에 로그인하거나 로그아웃하는 등의 사건 또는 사용자가 웹페이지를 요청하거나 특정 작업을 수행할 때 발생하는 사건 등 리스너는 이러한 다양한 사건들을 감지하고, 필요한 작업을 자동으로 수행하는 중요한 역할을 합니다.

 

 

WAS 서버에 리스너를 설정하는 방법은 서블릿과 마찬가지로 web.xml 파일에서 설정하거나 자바 코드측에 애노테이션을 사용하여 설정할 수 있는 방법이 존재 합니다.

 

 

web.xml 파일에 설정 방식

<web-app ..>
	<listener>
	    <listener-class>com.example.AppContextListener</listener-class>
	</listener>
</web-app>

 

 

자바코드 애노테이션으로 설정하는 방식

@WebListener
public class AppContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 애플리케이션 시작 시 처리 작업
        System.out.println("Application started at " + new java.util.Date());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 애플리케이션 종료 시 처리 작업
        System.out.println("Application stopped at " + new java.util.Date());
    }
}

 

 

시나리오 코드 1

프로젝트 생성 - filterListenerEx

com.tenco.controller HomeServlet.java

package com.tenco.controller;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/home")
public class HomeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public HomeServlet() {
        super();
    }
	
	// 주소 설계 - http://localhost:8080/fi/home
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("HomeServlet doGet() 메서드 호출");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

 

com.tenco.filters SimpleFilter.java

package com.tenco.filters;

import java.io.IOException;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;

/*
 * 필터 클래스를 만드는 방법
 * 1. jakarta.servlet.Filter 를 구현해야 한다. - 회사에서 톰캣 9버전 사용하면(javax.servlet.Filter)
 * 2. URL 패턴을 설정해야 한다. (web.xml, 어노테이션) 기반으로 설정 가능
 */

@WebFilter(urlPatterns = "/*", dispatcherTypes = {DispatcherType.REQUEST})
public class SimpleFilter implements Filter {
	
	
	
	// 필터가 생성될 때 초기화 작업을 수행하는 메서드
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// 필요하다면 초기화 코드를 작성하면 된다.
		System.out.println("Simple/Filter 초기화 ");
	}
	
	// 특정 URL 요청이 들어올 때 실행(통과)되는 메서드이다.
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
		System.out.println("SimpleFilter doFilter() 메서드 호출 됨.");
		
		// 다음 필터로 또는 서블릿으로 요청, 응답 객체를 전달한다.
		chain.doFilter(request, response);
		
	}

	@Override
	public void destroy() {
		System.out.println("종료될 때 호출 되는 메서드");
	}
}

 

 

com.tenco.filters IPBlockFilter.java

package com.tenco.filters;

import java.io.IOException;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

/*
 * 1. Filter 구현 
 * 2. URL 패턴 설정 (web.xml 파일에서 설정할 예정)
 */

public class IPBlockFilter implements Filter {
	
	// 192.168.0.144 <-- 내 아이피
	
	// 차단할 IP 대역의 접두사
	private static final String BLOCKED_IP_PREFIX = "192.168.0.25";
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("IPBlockFilter 초기화 ");
	}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		
		// 전처리 - 요청자에 IP를 확인
	String remoteIP	= request.getRemoteAddr();
	System.out.println("Request from IP : " + remoteIP);
	
	// 차단 시킬 코드 작성
	if(remoteIP.startsWith(BLOCKED_IP_PREFIX)) {
		System.out.println("필터 적용 확인 ...");
		response.setContentType("text/plain");
		response.setCharacterEncoding("UTF-8");
		response.getWriter().println("Access Denied !!");
		response.getWriter().println("너는 못 지나간다.");
		return;
	}
	
	chain.doFilter(request, response);
		
	}
}

 

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://jakarta.ee/xml/ns/jakartaee" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0">
  <servlet>
    <description></description>
    <display-name>HomeServlet</display-name>
    <servlet-name>HomeServlet</servlet-name>
    <servlet-class>com.tenco.controller.HomeServlet</servlet-class>
  </servlet>
  
  
  <servlet-mapping>
    <servlet-name>HomeServlet</servlet-name>
    <url-pattern>/HomeServlet</url-pattern>
  </servlet-mapping>
  
  <display-name>filterListenerEx</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <filter>
  	<filter-name>IPBlockFilter</filter-name>
  	<filter-class>com.tenco.filters.IPBlockFilter</filter-class>
  </filter>
  
  <filter-mapping>
  	<filter-name>IPBlockFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
</web-app>

 

 

 


Jakarta 외부 클래스 파일 문서보기

 sts 에서 Jakarta 문서 확인하는 방법

 

톰캣에 접속한다.

 

https://tomcat.apache.org/

 

Apache Tomcat® - Welcome!

The Apache Tomcat® software is an open source implementation of the Jakarta Servlet, Jakarta Pages, Jakarta Expression Language, Jakarta WebSocket, Jakarta Annotations and Jakarta Authentication specifications. These specifications are part of the Jakarta

tomcat.apache.org

 

다운로드 폴더에서 Tomcat10을 클릭한다.

 

 

zip 폴더를 다운로드 한다.

 

 

zip 파일을 tools 로 이동

 

 

객체에 마우스를 클릭한후 Ctrl를 누르고 마우스 좌클릭을 누르면 클래스나 메서드가 정의된 곳으로 간다. 

 

클래스나 메서드를 클릭하면 소스를 찾을수 없어 볼수가 없다.

 

Attach Source... 버튼을 클릭한다.

 

External location 을 클릭한다.

External File... 버튼을 클릭한다.

 

tools 폴더에 들어가서 아까전에 다운받은 zip 폴더 클릭 열기 버튼을 누른다.

 

선택한 zip 폴더가 맞는지 확인후 OK 버튼을 클릭한다.

 

OK 버튼을 클릭하면 아까전까지 지원하지 않는 클래스 파일을 볼 수 있다.

 


2. 리스너란?

리스너는 특정 이벤트가 발생했을 때 이를 감지하고, 그에 대응하는 작업을 수행하는 자바 객체입니다.

리스너는 서블릿 컨테이너에서 발생하는 다양한 이벤트를 처리할 수 있습니다.

사용하는 이유?

리스너는 웹 애플리케이션의 상태 변화(예: 시작, 종료, 세션 생성 및 소멸)를 감지하여 다음과 같은 작업을 수행할 수 있도록 도와줍니다.

  • 초기화 작업 (애플리케이션 시작 시 필요한 리소스 로드)
  • 정리 작업 (애플리케이션 종료 시 리소스 해제)
  • 세션 관련 작업 (로그인/로그아웃 로깅, 세션 속성 변경 감지)

주요 리스너 인터페이스

  • ServletContextListener: 애플리케이션 시작 및 종료 이벤트를 처리합니다.
  • HttpSessionListener: 세션 생성 및 소멸 이벤트를 처리합니다.
  • ServletRequestListener: 요청 객체의 생성 및 소멸 이벤트를 처리합니다.

com.tenco.listeners

ApplifecycleListener.java

package com.tenco.listeners;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import javax.xml.crypto.Data;

import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;

/*
 * 
 */
@WebListener
public class ApplifecycleListener implements ServletContextListener {
	
	private static final Logger logger =
			Logger.getLogger(ApplifecycleListener.class.getName());
	
	// 5분
	private String timeFormat() {
		// yyyy-MM-dd HH:mm:ss
		
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return formatter.format(new Date());
	}

	
	// 애플리케이션이 언제 시작을 했는지 로그, 파일을 남겨야 될 때 사용한다라고 가정하자
	@Override
		public void contextInitialized(ServletContextEvent sce) {
			System.out.println("------------------");
			logger.info("웹 애플리케이션 시작됨 >>> " + System.currentTimeMillis());
			System.out.println("------------------");
			System.out.println(timeFormat());
		}
	
	
	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("------------------");
		logger.info("웹 애플리케이션 종료됨 >>> " + System.currentTimeMillis());
		System.out.println("------------------");
	}
}

 

 

com.tenco.listeners

MySessionListener.java

package com.tenco.listeners;

import java.util.logging.Logger;

import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;

/*
 * 세션이 생성될 때 감지... 리스너 등록
 */
@WebListener
public class MySessionListener implements HttpSessionListener  {
	
	private static final Logger logger =
			Logger.getLogger(MySessionListener.class.getName());
	
	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// 세션 생성 시 실행 됨
	//	System.out.println("새로운 세션이 생성 됨 : " + se.getSession().getId());
		logger.info("새로운 세션이 생성 됨 : " + se.getSession().getId());
		se.getSession().setAttribute("loginTime", System.currentTimeMillis());
	}
	
	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// 세션 소멸 시 실행 됨
		Long loginTime = (Long)se.getSession().getAttribute("loginTime");
		Long logoutTime = System.currentTimeMillis();
		
		if (logoutTime != null) {
			// 로그인 부터 로그아웃 까지 시간
			Long sessionDurationMs = logoutTime - loginTime; // 밀리초 단위
			double sessionDurationSec = sessionDurationMs / 1000.0; // 초 단위로 변환
			System.out.println("세션 지속시간 : " +sessionDurationSec);
		}
	}

}

 

 

com.tenco.controller

SessionTestServlet.java

package com.tenco.controller;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

@WebServlet("/session-test")
public class SessionTestServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    public SessionTestServlet() {
        super();
    }
    
    // 주소 설계 - http://localhost:8080/fi/session-test
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// 새로운 세션을 생성해 보자.
		HttpSession session = request.getSession();
		response.setContentType("text/html");
		response.setCharacterEncoding("UTF-8");
		response.getWriter().print("당신에 세션 아이디는 : " + session.getId());
		
		// 세션 무효화 버튼
		response.getWriter().println("<form action='/session-test' method='POST'>");
		response.getWriter().println("<button type='submit' >세션 종료 (로그아웃)</button>");
		response.getWriter().println("</form>");	
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("doPost 실행 확인");
		HttpSession session = request.getSession(false); // 기존 세션을 반환, 없으면 null 반환
		if(session != null) {
			session.invalidate(); // 세션 무효화 (제거)
			
		}
		response.sendRedirect("session-test"); // 페이지 새로 고침
		// 새로운 request, response 객체가 새로 생성 된다.
		// sendRedirect --> 클라이언트로 돌아감 (브라우저로 돌아 감)
		// 		--> get 방식으로 http://localhost:8080/fi/session-test 다시 요청
	}

}