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의 대세화
하지만 레거시 시스템 유지보수는 종종 만날 수 있다.
(내가 겪어왔던 프로젝트에서는 거의 존재했다 왜냐면 공공기관이거나 대기업에서도 레거시 시스템을 운용하기 때문이다.)