객체는 언제 만들어지고, 메서드는 언제 호출되는가
서블릿을 이해할 때 가장 중요한 전제는 이것이다.
서블릿 코드는 개발자가 직접 실행하는 코드가 아니라, 컨테이너가 호출하는 코드이다
즉 서블릿은 “내가 new 해서 쓰는 객체”가 아니라
“요청이 들어오면 컨테이너가 알아서 관리하며 쓰는 객체”이다.
이 컨테이너의 관리 흐름을 서블릿 라이프사이클(lifecycle) 이라고 한다.
라이프사이클이 필요한 이유
웹 서버에는 동시에 많은 사용자가 들어온다.
요청이 올 때마다 매번 서블릿 객체를 새로 만들고 없애면 비효율적이다.
그래서 서블릿 컨테이너는 보통 이런 전략을 쓴다.
- 서블릿 객체는 한 번 만들어두고 재사용한다
- 요청이 들어올 때마다 같은 객체의 메서드를 호출한다
- 서버가 종료될 때만 정리한다
이게 바로 “init은 한 번, service/doGet/doPost는 여러 번, destroy는 한 번”이라는 구조이다.
서블릿 라이프사이클 3단계
서블릿 라이프사이클은 크게 3단계로 생각하면 된다.
- 초기화(init)
- 요청 처리(service → doGet/doPost)
- 종료(destroy)
1) 초기화: init()은 언제 호출되는가
서블릿 컨테이너는 어떤 URL 요청이 들어오면
그 요청을 처리할 서블릿이 필요하다는 것을 알게 된다.
이때 컨테이너는 보통 다음 일을 한다.
- 서블릿 클래스를 로딩한다
- 서블릿 객체를 생성한다
- init()을 호출한다
즉, init()은 “서블릿이 실제로 요청을 처리하기 전에 한 번만 실행되는 초기화 작업”이다.
여기서 할 수 있는 일은 이런 것들이다.
- 초기 설정값 읽기
- 공용 자원 준비(DB 연결 풀, 설정 객체 등)
- 로그 출력
- 캐시 준비
핵심은 이것이다.
init()은 서블릿 인스턴스당 한 번만 호출된다
load-on-startup이 있으면 타이밍이 달라진다
보통은 “요청이 처음 들어왔을 때” 서블릿이 로딩되고 init이 호출된다.
이걸 지연 로딩(lazy loading)이라고 생각하면 된다.
하지만 설정에 따라서는 서버(톰캣)가 시작할 때
미리 서블릿을 로딩하고 init을 호출하게 만들 수도 있다.
이게 흔히 말하는 load-on-startup 같은 개념이다.
핵심은 동일하다.
- init은 한 번
- 호출 시점만 “요청 시점”이냐 “서버 시작 시점”이냐 차이이다
2) 요청 처리: service()와 doGet()/doPost()는 언제 호출되는가
서블릿은 본질적으로 요청(request)을 받아 응답(response)을 만드는 객체이다.
즉 요청이 들어올 때마다 “처리 메서드”가 실행된다.
컨테이너는 요청이 들어오면 보통 아래 흐름으로 서블릿을 호출한다.
- request/response 객체를 준비한다
- 서블릿의 service()를 호출한다
- service() 내부에서 HTTP 메서드(GET/POST)에 맞게 doGet() 또는 doPost()로 분기한다
개발자가 HttpServlet을 상속해서 쓰는 이유가 여기 있다.
- HttpServlet이 이미 service() 로직을 가지고 있다
- GET이면 doGet, POST면 doPost로 자동 분기해준다
- 개발자는 필요한 메서드만 오버라이드하면 된다
여기서 중요한 포인트는 이것이다.
같은 서블릿 객체 하나가 여러 요청을 처리한다
그리고 요청이 동시에 들어오면 여러 스레드가 같은 서블릿 객체를 동시에 실행할 수 있다
즉 doGet()이나 doPost()는 “한 번만 실행되는 메서드”가 아니라
요청이 들어올 때마다 반복 호출되는 메서드이다.
3) 종료: destroy()는 언제 호출되는가
서버(톰캣)가 종료되거나, 웹 애플리케이션이 내려갈 때
컨테이너는 서블릿을 정리해야 한다.
이때 호출되는 메서드가 destroy()이다.
destroy는 보통 다음 용도로 쓴다.
- 사용하던 자원 정리
- 파일/소켓/스레드 정리
- 종료 로그 출력
핵심은 이것이다.
destroy()도 서블릿 인스턴스당 한 번만 호출된다
라이프사이클 요약
서블릿 한 개를 기준으로 보면 호출 횟수는 이렇게 된다.
- init() : 한 번
- doGet()/doPost() : 요청이 올 때마다 여러 번
- destroy() : 한 번
즉, 서블릿은 “객체를 계속 재사용하는 구조”이다.
여기서 가장 중요한 주의점: 멀티스레드
서블릿은 한 객체를 재사용한다.
그리고 동시에 여러 요청이 들어오면 같은 객체에 여러 스레드가 진입할 수 있다.
그래서 서블릿 코드에서는 특히 아래를 조심해야 한다.
- 서블릿의 멤버 변수(필드)에 요청별 데이터를 저장하지 않는다
- 공유 자원(공용 리스트, 카운터 등)을 막 쓰면 경쟁 조건이 생긴다
- 요청별 데이터는 request 객체, 로컬 변수에 둔다
이 멀티스레드 성격은 나중에 필터, 세션, 스프링 MVC에서도 계속 이어진다.
핵심 정리
- 서블릿은 컨테이너가 생성하고 호출한다
- init은 한 번, doGet/doPost는 여러 번, destroy는 한 번이다
- 서블릿 객체는 재사용되며 멀티스레드로 동시에 실행될 수 있다
- 그래서 서블릿 필드에 요청 데이터를 두면 위험하다
'Web > Web Basics' 카테고리의 다른 글
| [서블릿과 JSP] 4. JSP는 왜 필요한가 (0) | 2026.03.29 |
|---|---|
| [서블릿과 JSP] 3. HttpServletRequest/HttpServletResponse (0) | 2026.03.29 |
| [서블릿과 JSP] 1. 서블릿이란? (0) | 2026.03.29 |
| [웹 기초] 9. 개발자 도구(Network)로 request/response를 눈으로 확인하는 방법 (0) | 2026.03.29 |
| [웹 기초] 8. HTTP가 비연결성이고 무상태성이라는 말의 의미 (0) | 2026.03.29 |