서블릿,필터,리스너 보충자료

1. 서블릿 필터(Filter)

역할

  • 요청(request) 및 응답(response) 데이터 처리: 클라이언트로부터 들어오는 요청을 가로채거나, 서블릿이나 JSP가 응답을 생성한 후 이를 가로채 추가 처리를 수행합니다.

  • 주로 다음과 같은 작업에 사용됩니다:

    • 요청/응답 데이터 수정

    • 요청 로깅 및 모니터링

    • 인증 및 권한 검사

    • 데이터 압축 (예: GZIP)

    • CORS 설정 등

동작 방식

  1. 클라이언트의 요청이 서버로 들어오면, **필터 체인(Filter Chain)**이 실행됩니다.

  2. 필터는 요청을 처리하거나, 필요한 경우 요청을 다음 필터나 서블릿으로 전달합니다.

  3. 서블릿이 응답을 생성하면, 필터가 응답을 가로채 필요한 처리를 수행한 뒤 클라이언트로 보냅니다.

동작 순서

  1. web.xml 또는 @WebFilter에 등록된 필터는 순서대로 실행됩니다.

  2. 필터 체인의 흐름:

    • 요청(Request) → 필터1 → 필터2 → 서블릿 → 필터2 → 필터1 → 응답(Response)

필터 구현 방법

  1. javax.servlet.Filter 인터페이스 구현.

  2. 주요 메서드:

    • init(FilterConfig config): 필터 초기화.

    • doFilter(ServletRequest request, ServletResponse response, FilterChain chain): 요청/응답 처리.

    • destroy(): 필터 종료 처리.

@WebFilter("/*") // 모든 요청에 대해 적용
public class LoggingFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println("Request received at " + new Date());
        chain.doFilter(request, response); // 다음 필터나 서블릿으로 요청 전달
        System.out.println("Response sent at " + new Date());
    }
}

2. 서블릿 리스너(Listener)

역할

  • 이벤트 기반 동작: 웹 애플리케이션의 특정 이벤트(예: 시작, 종료, 세션 생성/소멸 등)를 감지하고 처리합니다.

  • 주요 사용 사례:

    • 애플리케이션 초기화 작업 (예: 리소스 로딩, 데이터베이스 연결 설정).

    • 세션 추적 (예: 로그인 사용자 수 추적).

    • 리소스 정리 (예: 데이터베이스 연결 종료).

동작 방식

  1. 리스너는 특정 이벤트가 발생할 때 자동으로 호출됩니다.

  2. 웹 컨테이너는 이벤트 발생 시 리스너를 실행하여 적절한 작업을 수행합니다.

동작 순서

  1. 애플리케이션 시작 시 등록된 리스너가 호출됩니다.

  2. 세션 관련 이벤트가 발생하면, 관련 리스너가 실행됩니다.

  3. 애플리케이션 종료 시 종료 이벤트 리스너가 호출됩니다.

리스너 종류

  1. ServletContextListener:

    • 애플리케이션 시작/종료 이벤트 처리.

    • 주요 메서드:

      • contextInitialized(ServletContextEvent event): 애플리케이션 초기화 작업.

      • contextDestroyed(ServletContextEvent event): 애플리케이션 종료 작업.

  2. HttpSessionListener:

    • 세션 생성/소멸 이벤트 처리.

    • 주요 메서드:

      • sessionCreated(HttpSessionEvent event): 세션 생성 시 호출.

      • sessionDestroyed(HttpSessionEvent event): 세션 종료 시 호출.

  3. ServletRequestListener:

    • 요청(Request) 생성/소멸 이벤트 처리.

    • 주요 메서드:

      • requestInitialized(ServletRequestEvent event): 요청 초기화 시 호출.

      • requestDestroyed(ServletRequestEvent event): 요청 종료 시 호출.

리스너 구현 방법

  1. 리스너 인터페이스를 구현.

  2. @WebListener 또는 web.xml에 등록.

@WebListener
public class AppContextListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("Application started!");
    }
    
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("Application stopped!");
    }
}

3. 필터와 리스너의 차이점

특징

필터(Filter)

리스너(Listener)

동작 방식

요청/응답을 가로채 처리

특정 이벤트를 감지하고 처리

목적

요청/응답 전후 데이터 변경, 인증, 로깅 등

애플리케이션, 세션, 요청의 상태 변화 감지

실행 시점

요청이 들어오거나 응답이 생성될 때 실행

애플리케이션/세션/요청의 생명 주기 이벤트 발생 시 실행

등록 방법

@WebFilter 또는 web.xml

@WebListener 또는 web.xml

체인 구조

필터 체인으로 연결되어 동작

독립적으로 동작


4. 필터와 리스너의 동작 순서

  1. 애플리케이션 초기화:

    • ServletContextListener.contextInitialized 실행.

    • 초기화 작업(예: 설정 파일 로딩, 데이터베이스 연결).

  2. 클라이언트 요청 처리:

    • ServletRequestListener.requestInitialized 실행.

    • 필터 체인 실행: 요청 → 필터1 → 필터2 → 서블릿.

    • 서블릿 처리 후 응답 생성 → 필터 체인 응답 처리: 필터2 → 필터1.

    • ServletRequestListener.requestDestroyed 실행.

  3. 세션 관련 이벤트:

    • 세션 생성 시: HttpSessionListener.sessionCreated 실행.

    • 세션 종료 시: HttpSessionListener.sessionDestroyed 실행.

  4. 애플리케이션 종료:

    • ServletContextListener.contextDestroyed 실행.

    • 종료 작업(예: 리소스 정리, 로그 기록).


5. 필터와 리스너를 함께 사용하는 예시

  • 필터:

    • 모든 요청에 대해 로깅.

    • 특정 API에 대해 인증 처리.

  • 리스너:

    • 애플리케이션 시작 시 설정 파일 로드.

    • 세션 생성/종료 시 사용자 활동 로그 기록.

생명주기

서블릿 생명주기의 주요 단계

  1. 서블릿 생성 (Initialization)

    • 서블릿 컨테이너는 서블릿 객체를 메모리에 로드하고 초기화합니다.

    • 초기화는 init() 메서드를 통해 이루어지며, 서블릿이 최초로 요청되기 전에 한 번만 호출됩니다.

  2. 요청 처리 (Request Handling)

    • 클라이언트가 서블릿에 요청을 보내면 컨테이너는 service() 메서드를 호출하여 요청을 처리합니다.

    • service() 메서드는 HTTP 요청 메서드(GET, POST 등)에 따라 적절한 doGet(), doPost() 등의 메서드를 호출합니다.

  3. 서블릿 소멸 (Destruction)

    • 애플리케이션 종료 시 또는 서블릿이 더 이상 필요 없을 때, 컨테이너는 서블릿 객체를 소멸시킵니다.

    • 소멸 시 destroy() 메서드가 호출되며, 리소스 정리(예: 데이터베이스 연결 종료, 스레드 정리 등)를 수행할 수 있습니다.


서블릿 생명주기 메서드

1. init()

  • 호출 시점: 서블릿이 처음 생성될 때 한 번 호출됩니다.

  • 목적: 서블릿의 초기화 작업 수행 (예: 설정 파일 읽기, 데이터베이스 연결 설정).

  • 예시:

    @Override
    public void init() throws ServletException {
        System.out.println("Servlet is being initialized");
    }

2. service()

  • 호출 시점: 클라이언트 요청이 있을 때마다 호출됩니다.

  • 목적: 요청에 따라 적절한 메서드(doGet, doPost)를 호출.

  • 컨테이너 동작: 요청 메서드(GET, POST 등)에 따라 doGet() 또는 doPost() 메서드로 분기.

  • 예시:

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        System.out.println("Processing request");
        super.service(req, resp); // 부모 클래스의 service() 호출
    }

3. destroy()

  • 호출 시점: 서블릿이 컨테이너에서 제거될 때 호출됩니다.

  • 목적: 리소스 정리(예: 열려 있는 파일이나 데이터베이스 연결 종료).

  • 예시:

    @Override
    public void destroy() {
        System.out.println("Servlet is being destroyed");
    }

생명주기 흐름 (Lifecycle Flow)

  1. 서블릿 로드 및 초기화:

    • 웹 애플리케이션이 시작되거나 서블릿에 처음 요청이 들어올 때 서블릿 클래스가 로드됩니다.

    • 컨테이너는 서블릿 객체를 생성하고, init() 메서드를 호출합니다.

  2. 요청 처리:

    • 클라이언트의 HTTP 요청이 들어오면, 컨테이너는 service() 메서드를 호출합니다.

    • service()는 요청 타입에 따라 doGet(), doPost() 등 적절한 메서드를 실행합니다.

  3. 서블릿 소멸:

    • 웹 애플리케이션이 종료되거나 서블릿이 필요 없게 되면, 컨테이너는 destroy() 메서드를 호출하여 서블릿을 소멸시킵니다.


서블릿 생명주기의 단계별 예시 코드

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class MyServlet extends HttpServlet {

    // 1. 초기화 단계
    @Override
    public void init() throws ServletException {
        System.out.println("Servlet Initialized");
    }

    // 2. 요청 처리 단계
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        System.out.println("Handling GET request");
        resp.getWriter().write("Hello, World!");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        System.out.println("Handling POST request");
        resp.getWriter().write("POST request received");
    }

    // 3. 소멸 단계
    @Override
    public void destroy() {
        System.out.println("Servlet Destroyed");
    }
}

생명주기와 멀티스레드

  • 서블릿은 기본적으로 멀티스레드 환경에서 실행됩니다.

  • 하나의 서블릿 객체가 여러 클라이언트 요청을 동시에 처리하므로, 스레드 안전성에 주의해야 합니다.

    • 서블릿 인스턴스 변수(멤버 변수) 사용 시 동기화가 필요합니다.

    • 지역 변수는 스레드 안전하므로 선호됩니다.


주의 사항

  1. init()은 한 번만 호출됨:

    • 서블릿 객체는 싱글톤으로 생성되며, init()은 한 번만 호출됩니다.

  2. service()는 요청마다 호출됨:

    • 클라이언트 요청이 올 때마다 호출되므로, 비즈니스 로직을 구현하는 핵심 메서드입니다.

  3. destroy()는 마지막 정리 작업에 사용:

    • 리소스 정리 코드를 반드시 작성해야 메모리 누수를 방지할 수 있습니다.

  4. 애플리케이션 종료 시 동작:

    • 서블릿 컨테이너가 종료되거나 서블릿이 언로드될 때 destroy()가 호출됩니다.

Last updated