프로그래밍/JAVA

web.xml 대해서

모알 2026. 6. 1. 23:00

java 웹 애플리케이션의 설정 파일

┗ java 웹 애플리케이션 : 웹 브라우저를 통해 사용자가 주고받는 요청을 처리하는 프로그램(백엔드를 java로 구현)

 

왜 필요하지?

1. Servlet과 URL 매핑

Servlet : 웹페이지를 동적으로 생성하는 서버 측 프로그램(java 기반 웹 처리기)

※ Servlet 프로그램 구체적인 가동 모습

java 파일 > 컴파일(class) > 톰캣서버에 저장 > 톰캣서버 구동 > 프로그램 동작 대기

> 요청 전달(ex. https://www.test.com 입력)  > 톰캣 서버 특정 Servlet 프로그램을 메모리에 올려서 실행

> 로직 수행 > 결과 전송

[추가적인 사항]

매번 요청 전달이 된다고 Servlet 프로그램이 메모리에 매번 올리지 않는다.

톰캣이 켜질 때 딱 한번 객체를 생성해서 메모리에 올림( 싱글톤 패턴 ) - 재사용

싱글톤 패턴 : 어떤 클래스의 인스턴스가 딱 1개만 생성되도록 보장하는 디자인 패턴

service() 메소드를 쓰레드만 갈아끼우면서 호출

 

싱글톤 패턴을 왜 사용해야할까?

1~10명정도는 크게 상관없을 거 같은데 10,000명 혹은 10,000,000명이 동시에 들어와서 작업을 한다고 하면 

싱글톤이 아니면 매번 요청 > 메모리 생성 > 작업 끝나면 가비지 컬렉터가 수거 이 동작을 존재하는 명만큼 작업을 해야되서 

느려지거나 멈춰버린다.

그래서 하나의 인스턴스를 만들어서 순서에 따라서 스레드만 교체해서 실행하면 메모리 절약이 된다.

여기서 순서는 큐 > 락 

 

 

2. 필터 설정

대표적인 활용 : 인코딩 설정(UTF-8), 로그인 여부 확인하는 인증 필터 등

인코딩 : 다른 환경의 컴퓨터가 읽기 쉽게 데이터의 형태(포맷) 변환(용량이 줄수도 늘수도 있음) - 반대개념(디코딩)

 

? 갑자기 드는 생각 ?

인코딩과 컴파일의 차이는?

인코딩은 데이터를 컴퓨터가 읽기 좋게 변환하는 과정이라면

특징 : 변환 규칙을 알면 누구나 디코딩을 완벽하게 돌릴 수 있음

컴파일은 사람이 작성한 소스코드(하이레벨)를 컴퓨터가 읽기 쉬운 기계어(로우레벨)로 변환

특징 : 컴퓨터가 읽어야 하기 때문에 빠른 경로로 실행할 수 있도록 복잡한 연산 과정이 포함됨. 완벽한 소스코드로 디컴파일 하는 것이 매우 어려움

 

3. 애플리케이션 초기화 파라미터 및 리스너 설정

리스너(Listener): 웹 애플리케이션이 시작되거나 종료될 때 특정 이벤트를 감지해서 실행하는 하나의 클래스 파일

리스너 사용방법( 제일 많이 사용되는 리스너 )

  • ServletContextListener - 서버가 켜지고 꺼질 때를 포착
  • HttpSessionListener - 접속하거나 나갈 때 포착
  • HttpSessionAttributeListener - 데이터를 넣거나 삭제할 때 포착

위 3가지 안에 많은 메소드 들이 존재하는데 원하는 메소드를 

내가 만든 새로운 리스너 안에서 메소드 오버라이드해서 사용함

 

예를 들어

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

@WebListener // 과거에는 web.xml에 등록했으나, 요즘은 이 어노테이션 하나로 등록 끝!
public class UserCounterListener implements HttpSessionListener { <!-- UserCounterListener 클래스가  HttpSessionListener  인터페이스 구현 -->

    private static int activeUsers = 0;

    // 세션이 생성될 때 (새로운 사용자가 접속했을 때)
    @Override
    public void sessionCreated(HttpSessionEvent se) { <!-- HttpSessionListener안에  sessionCreated 메소드 존재 -->
        activeUsers++;
        System.out.println("새로운 사용자가 접속했습니다. 현재 접속자: " + activeUsers + "명");
    }

    // 세션이 소멸될 때 (로그아웃하거나 브라우저를 닫아서 세션이 끊겼을 때)
    @Override
    public void sessionDestroyed(HttpSessionEvent se) { <!-- HttpSessionListener안에  sessionDestroyed 메소드 존재 -->
        if (activeUsers > 0) {
            activeUsers--;
        }
        System.out.println("사용자가 나갔습니다. 현재 접속자: " + activeUsers + "명");
    }
}

implements : 인터페이스 구현한다는 의미

implements는 인터페이스에 있는 메소드의 규칙을 그대로 가져와서 오버라이드 해서 만드는 방식( 이름, 리턴타입, 파라미터 )

extend와 차이점은?

extend는 상속받은 부모 클래스의 메소드를 가져와서 바로 사용하거나 오버라이드해서 변경해서 사용 가능

요리로 비유하면

implements - 레시피( 정해진 결과물/기능 구현한다. 단, 구현 방식은 클래스마다 다를 수 있다. )

extends - 밀키트를 그대로 사용하거나 다른 방식으로 변형한 요리( 정석적인 방법 또는 완전 새로운 방법 )

 

? 갑자기 드는 생각 ?

웹 컴포넌트( Listener, Filter, Interceptor ) 실행 순서와 각각의 차이는?

1. 순서

사용자 요청 > Listener > Filter > Interceptor > 컨트롤러( 돌아올 때는 역순 )

제미나이 정정 : [사용자 요청] -> [Filter] -> [DispatcherServlet (서블릿 시작)] -> [Interceptor] -> [Controller]

DispatcherServlet : 스프링 MVC( Spring MVC ) 프레임워크의 핵심 엔진

Dispatcher뜻 : 보내다, 파견하다, 배차원

즉, 클라이언트에서 들어온 요청을 가장먼저 받아서 처리할 수 있는 적절한 컨트롤러에 나누어주는 중앙 배달원

 

DispatcherServlet  왜 생겨나게 됬을까?

디스패치 서블릿이 없던 시절

web.xml에 매번 등록(기능이 100개면 100개 다 등록)

비효율을 줄이고자 만들어지게 됨

 

리스너는 요청의 흐름 위에 존재하는게 아니다.

특정 사건(이벤트)에만 독자적으로 실행

그리고 Filter는 스프링 context안으로 들어오기 전에 동작한다

스프링 context : 

Interceptor는 스프링 컨테이너 내부에서 동작

스프링 컨테이너 : 

 

2. 차이점

"기술이 어느 영역에 속해 있는가" 가 기준

- 리스너(Listener)

영역 : 웹 애플리케이션(WAS)자체의 시작과 종료

- 필터(Filter)

영역 : 웹 컨테이너

- 인터셉터(Interceptor) - 스프링에만 존재

영역 : 스프링 컨테이

 

정적처리 : 단순 저장된 파일 보여주기(Web Server)

Web Server : 정적 자원을 그대로 돌려주는 서버

동적처리 : 인풋에 따라 결과 값이 다르게 보여주기(WAS)

WAS(Web Application Server) : 비즈니스 로직을 실행해서 그때 그때마다 다르게 결과값을 만들어 내는 서버

 

web.xml의 간단한 구조

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="3.1">

    <!-- 1. 한글 깨짐 방지를 위한 인코딩 필터 설정 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern> <!-- 모든 요청에 적용 -->
    </filter-mapping>

    <!-- 2. 서블릿 등록 및 URL 매핑 -->
    <servlet>
        <servlet-name>homeServlet</servlet-name>
        <servlet-class>com.example.HomeServlet</servlet-class> <!-- 2. 해당 패키지의 Servlet 실행 --> 
    </servlet>
    <servlet-mapping>
        <servlet-name>homeServlet</servlet-name>
        <url-pattern>/home</url-pattern> <!-- 1. /home으로 들어오면 실행 -->
    </servlet-mapping>

    <!-- 3. 웰컴 파일 (기본 메인 페이지) 지정 -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file> <!-- 1. 순차적으로 실행 -->

        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

 

 

최신 기술을 사용하는 곳에서는 못볼 수 있다.

이유는 2가지이다.

1. Servlet 3.0으로 오면서 @(어노테이션)으로  대신할 수 있기 때문

2. Spring Boot의 대세화

 

하지만 레거시 시스템 유지보수는 종종 만날 수 있다.

(내가 겪어왔던 프로젝트에서는 거의 존재했다 왜냐면 공공기관이거나 대기업에서도 레거시 시스템을 운용하기 때문이다.)