Tomcat은 멀티스레드 서버다
Tomcat은 멀티스레드 기반 서버다.
즉, 여러 사용자가 동시에 요청을 보내면 Tomcat은 다음과 같이 동작한다.
- 요청 하나당 스레드 하나를 할당한다
- 여러 스레드가 동시에 실행된다
- 같은 Servlet 인스턴스를 공유한다
이 구조를 정확히 이해하지 못하면
Servlet에서 의도하지 않은 버그가 발생한다.
Servlet 인스턴스는 몇 개가 생성되는가
Servlet은 요청마다 새로 생성되지 않는다.
- Tomcat이 시작될 때
- Servlet 클래스당 인스턴스 1개를 생성한다
- 이후 모든 요청은 이 인스턴스를 재사용한다
즉 구조는 다음과 같다.
- Servlet 인스턴스: 1개
- 요청 처리 스레드: 여러 개
- 여러 스레드가 같은 객체를 동시에 사용한다
이 점이 멀티스레드 문제의 출발점이다.



요청이 들어왔을 때 내부 동작 흐름
클라이언트가 요청을 보내면 Tomcat 내부에서는 다음 순서가 실행된다.
- 클라이언트가 HTTP 요청을 보낸다
- Tomcat이 요청을 수신한다
- 요청 하나당 스레드를 하나 생성한다
- request 객체와 response 객체를 생성한다
- 같은 Servlet 인스턴스의 service() 메서드를 호출한다
- HTTP 메서드에 따라 doGet() 또는 doPost()가 실행된다
여기서 핵심은 다음이다.
여러 스레드가 동시에 하나의 Servlet 객체에 진입한다
Servlet 필드에 상태 값을 저장하면 왜 문제가 되는가
Servlet 클래스에 다음과 같은 필드가 있다고 가정한다.
public class UserServlet extends HttpServlet {
private String userId;
}
이 필드는 Servlet 인스턴스의 상태다.
즉, 모든 요청이 이 변수를 공유한다.
실제로 발생하는 문제
- 사용자 A 요청 → userId = "A"
- 거의 동시에 사용자 B 요청 → userId = "B"
- 사용자 A의 응답을 만드는 도중 값이 바뀐다
- 사용자 A가 B의 정보로 처리되는 상황이 발생한다
이 문제는 다음 조건이 동시에 만족될 때 발생한다.
- Servlet 인스턴스가 하나다
- 멀티스레드 환경이다
- 인스턴스 필드에 요청별 상태를 저장했다
그래서 Servlet 필드에 상태를 저장하면 안 된다.
지역 변수는 왜 안전한가
반면 다음과 같은 코드는 안전하다.
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String userId = request.getParameter("userId");
}
이유는 명확하다.
- 지역 변수는 스택 영역에 생성된다
- 스레드마다 스택이 분리된다
- 다른 스레드가 접근할 수 없다
즉,
- 인스턴스 변수 → 공유됨 → 위험
- 지역 변수 → 요청 단위 → 안전
이다.
static 변수는 더 위험하다
static 변수는 Servlet 인스턴스보다도 더 넓은 범위에서 공유된다.
- 클래스 로딩 시 한 번 생성된다
- JVM 전체에서 하나만 존재한다
- 모든 Servlet 인스턴스, 모든 스레드가 공유한다
즉 static에 요청 상태를 저장하면 다음과 같은 문제가 발생한다.
- 모든 사용자 정보가 섞인다
- 테스트 환경에서는 잘 되는 것처럼 보인다
- 실제 트래픽이 늘면 즉시 사고가 난다
그래서 Servlet에서는 다음 원칙이 있다.
static 필드에는 절대 요청 상태를 저장하지 않는다
디버깅이 어려운 이유
이 문제의 가장 큰 특징은 다음과 같다.
- 항상 발생하지 않는다
- 트래픽이 적을 때는 안 터진다
- 동시에 요청이 들어올 때만 발생한다
- 재현이 매우 어렵다
그래서 이런 버그는 흔히 다음과 같이 오해된다.
- “가끔 이상하다”
- “환경 문제 같다”
- “서버가 불안정한 것 같다”
하지만 실제 원인은 멀티스레드 + 공유 상태다.
Servlet에서 반드시 지켜야 할 원칙
멀티스레드 환경에서 Servlet을 안전하게 사용하려면 다음 원칙을 지켜야 한다.
- 요청 데이터는 지역 변수로만 처리한다
- Servlet 인스턴스 필드에 상태를 두지 않는다
- static 필드에 요청 데이터를 저장하지 않는다
- 공유 자원이 필요하다면 동기화 전략을 명확히 한다
'Backend > Servlet' 카테고리의 다른 글
| 서블릿이 생성되고 요청이 처리되는 전체 흐름 (0) | 2026.02.08 |
|---|---|
| forward와 JSP 실행 흐름 (0) | 2026.02.06 |
| Servlet Container와 Servlet의 관계 (0) | 2026.02.05 |
| RequestMapping static Map은 괜찮은 이유 (0) | 2026.02.05 |
| 서블릿에서의 static (0) | 2026.02.05 |