웹 개발을 하다 보면 가장 먼저 마주치는 개념들이 있다. "웹 서버(Web Server)와 웹 애플리케이션 서버(WAS)의 차이는 무엇일까?", "톰캣(Tomcat)은 정확히 어떤 일을 할까?", "서블릿(Servlet)은 왜 필요할까?" 등과 같은 질문들이다.
이 개념들은 스프링(Spring)이나 스프링 부트(Spring Boot) 프레임워크 아래 감춰져 있어 평소에는 잘 보이지 않지만, 면접 질문의 단골 주제이자 실무에서 시스템 장애가 발생했을 때 문제의 원인을 파악하는 결정적인 열쇠가 된다.
오늘은 기초적인 웹 인프라의 아키텍처부터 서블릿 객체의 동작 흐름, 그리고 멀티 스레드(Multi-Thread) 환경에서의 스레드 풀(Thread Pool) 최적화 전략까지 한 번에 정리해 보겠다.
1. 웹 서버(Web Server) vs 웹 애플리케이션 서버(WAS)
우리가 매일 사용하는 인터넷 웹 시스템은 크게 두 종류의 서버로 나뉜다. 바로 웹 서버(Web Server)와 웹 애플리케이션 서버(WAS, Web Application Server)다. 두 서버 모두 HTTP 프로토콜을 기반으로 클라이언트의 요청을 처리하지만, 그 주된 목적과 역할이 완전히 다르다.

📌 웹 서버 (Web Server)
웹 서버는 정적인 리소스(Static Resources)를 제공하는 서버다. 클라이언트가 브라우저를 통해 특정 파일을 요청하면, 추가적인 연산 없이 저장소에 있는 파일을 그대로 반환해 준다.
- 주요 프로토콜: HTTP
- 제공 리소스: 고정된 HTML, CSS, JavaScript, 이미지, 영상 파일 등
- 대표적인 기술: Apache HTTP Server, Nginx
📌 웹 애플리케이션 서버 (WAS)
WAS는 웹 서버의 기능을 기본적으로 포함하면서, 추가로 프로그램 코드를 실행하여 애플리케이션 비즈니스 로직을 수행하는 서버다. 사용자(클라이언트)마다 서로 다른 화면을 보여주어야 하는 동적인 요청을 처리한다.
- 주요 역할: 동적 HTML 생성, HTTP API(JSON) 데이터 제공, 데이터베이스(DB) 연동 등
- 대표적인 기술: Apache Tomcat, Jetty, WildFly, 서블릿/JSP, 스프링 MVC
2. 효율적인 웹 시스템 구성: WEB - WAS - DB 3티어 아키텍처
실무에서는 WAS 하나만 단독으로 띄워서 웹 시스템을 구성하지 않는다. 보통 앞단에 웹 서버(WEB)를 두고, 그 뒤에 애플리케이션 서버(WAS), 그리고 가장 뒤에 데이터베이스(DB)를 배치하는 3-Tier(3계층) 아키텍처를 구성한다.
왜 굳이 서버를 쪼개어 배치할까? 그 원리는 '효율적인 관리와 높은 고가용성'에 있다.

3티어 아키텍처의 강력한 장점들
- 서버 부하 분산 및 효율적인 증설: 정적인 리소스(HTML, 이미지 등) 요청이 많아지면 값싼 WEB 서버만 대규모로 증설(Scale-out)하면 된다. 반대로 결제나 회원가입 등 서버 자원을 많이 먹는 연산이 많아지면 값비싼 WAS만 따로 늘릴 수 있어 비용 효율적이다.
- 보안 강화: 비즈니스 로직과 데이터베이스가 있는 중요한 레이어를 내부 네트워크에 숨기고, 외부 인터넷 망에는 정적인 정문 역할을 하는 웹 서버만 노출시켜 외부 공격으로부터 시스템을 보호한다.
- 높은 서비스 가용성(안정성):
- 정적 페이지만 제공하는 WEB 서버는 구조가 단순하여 거의 죽지 않는다.
- 반면 복잡한 비즈니스 로직을 처리하고 외부 라이브러리가 얽혀 있는 WAS 서버는 상대적으로 장애가 발생하여 잘 죽는다(Crash).
- 이때 WAS나 DB에 장애가 발생하더라도 WEB 서버가 살아있기 때문에, 사용자는 하얀 화면 대신 WEB 서버가 뿌려주는 예쁘게 디자인된 "현재 서비스 점검 중입니다" 에러 화면을 볼 수 있어 사용자 경험을 해치지 않는다.
3. 톰캣(Tomcat)이란 무엇인가?
자바 백엔드 생태계에서 단연코 가장 많이 사용되는 도구 중 하나가 바로 아파치 톰캣(Apache Tomcat)이다. 톰캣의 정체를 명확히 정의하자면 다음과 같다.
"Apache Tomcat은 자바 서블릿 컨테이너(Servlet Container)이자, 웹 애플리케이션 서버(WAS)다."
쉽게 말해 HTTP 요청과 응답을 자바 기반으로 처리하고, 서블릿과 JSP 기반의 자바 웹 프로그램을 구동시켜 주는 실행 환경(Runtime)이다.
톰캣과 일반 웹 서버(Nginx 등)의 결정적 차이
- Nginx, Apache HTTP Server: 정적 리소스 처리에 고도로 최적화되어 있어, 자바 코드를 읽거나 실행할 수 없다.
- Tomcat: 동적인 자바 코드(컨트롤러, 서비스 등)의 실행 결과를 만들어 내는 데 최적화되어 있다.
톰캣의 주요 역할 3가지
- HTTP 서버 역할: 클라이언트 브라우저가 보내는 물리적인 HTTP 요청(바이트 스트림)을 수신하고, 이를 자바 객체로 파싱하여 내부 자바 프로그램에 전달한 뒤, 실행 결과를 다시 HTTP 응답 메시지로 만들어 돌려준다.
- 예: GET /api/users 요청이 왔을 때 알맞은 스프링의 UserController가 매핑되어 실행되도록 징검다리를 놓아준다.
- 서블릿 컨테이너(Servlet Container) 역할: 뒤에서 설명할 '서블릿' 객체들의 생성부터 초기화, 실행, 소멸까지의 전체 생명주기(Life Cycle)를 관리한다. 또한, 스프링 MVC가 자랑하는 모든 요청의 입구인 DispatcherServlet도 결국 톰캣이라는 서블릿 컨테이너에 의해 실행되고 관리되는 서블릿의 일종이다.
- 스레드 관리 & 라이프사이클 관리: 수많은 사용자가 동시에 요청을 보낼 때, 각 요청마다 새로운 실행 스레드(Thread)를 할당하여 동시 요청을 처리한다. 개발자가 직접 복잡한 멀티 스레딩 코드를 짜지 않아도 톰캣이 뒷단에서 전부 알아서 최적화해 준다.
4. 서블릿(Servlet)과 HTTP 요청/응답 흐름
웹 애플리케이션 서버를 맨땅에서 직접 개발하려고 하면, 소켓 통신을 연결하고 HTTP 메시지 포맷을 한 줄씩 파싱하여 요청 헤더와 바디를 분리하는 등 비즈니스 로직과는 전혀 상관없는 엄청난 양의 보일러플레이트 코드를 작성해야 한다.
서블릿(Servlet)은 이 모든 네트워크 통신과 HTTP 파싱 표준 작업을 대신해 주고, 개발자로 하여금 순수한 비즈니스 로직 실행에만 집중할 수 있게 해주는 자바 클래스 사양이다.
톰캣과 서블릿의 명쾌한 비유
"톰캣은 무대(Stage)이고, 서블릿은 무대 위에서 연기하는 배우(Actor)다."
- 톰캣(컨테이너): 마이크 설치, 조명 조절, 무대 배경 세팅, 배우 호출 및 퇴장(환경 설정 및 관리).
- 서블릿(애플리케이션): 주어진 대본대로 대사를 읊고 액션을 함(실제 비즈니스 로직 수행).
서블릿 코드 살펴보기
서블릿은 기본적으로 HttpServlet 클래스를 상속받아 구현한다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) {
// 1. 요청 데이터를 편리하게 꺼내 쓰기
String username = request.getParameter("username");
// 2. 비즈니스 로직 실행
System.out.println("helloServlet 호출됨: " + username);
// 3. 응답 데이터를 편리하게 작성하기
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
try {
response.getWriter().write("hello " + username);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- @WebServlet: /hello 경로로 들어오는 HTTP 요청을 이 서블릿 클래스가 처리하겠다고 톰캣에게 알리는 애노테이션이다.
- HttpServletRequest: HTTP 요청 메시지를 톰캣이 파싱하여 자바 객체로 포장해 준 것이다. 이 객체에서 헤더 값, 파라미터, 쿠키 등을 쉽게 꺼내 쓸 수 있다.
- HttpServletResponse: 개발자가 HTTP 응답 헤더나 바디에 담을 데이터를 이 객체에 가공해서 채워 넣으면, 톰캣이 이를 기반으로 완벽한 포맷의 HTTP 응답 텍스트를 구성하여 클라이언트에게 전송한다.
🔄 HTTP 요청과 응답의 전체 실행 흐름

💡 서블릿 컨테이너의 4가지 주요 핵심 요약
- 생명주기 관리: 서블릿 인스턴스를 스스로 생성(init()), 호출(service()), 소멸(destroy())시킨다.
- 싱글톤(Singleton) 관리: 톰캣은 서블릿 객체를 단 하나만 생성하여 메모리에 올려둔 뒤, 모든 요청이 이를 공유(Reuse)하게 만든다. 요청이 올 때마다 인스턴스를 무겁게 새로 만들지 않으므로 자원을 아끼고 성능을 극대화할 수 있다.
- JSP 변환기 기능: 과거 동적 화면 생성에 쓰이던 JSP 파일 역시 내부적으로는 자바 서블릿 코드로 자동 변환되어 컴파일되고 실행된다.
- 멀티 스레드 지원: 동시에 요청하는 여러 사용자를 위해 백그라운드에서 멀티 스레드를 처리해 준다.
웹 서버의 숙명은 수많은 동시 접속 요청을 빠르고 멈춤 없이 처리하는 것이다.
자바 애플리케이션 코드를 순차적으로 하나하나 처리해 주는 가상의 실행 일꾼을 스레드(Thread)라고 한다.
만약 동시 요청 상황에서 이 스레드를 어떻게 관리하느냐에 따라 서버의 성능과 생존이 결정된다.
❌ 요청마다 스레드를 매번 새로 생성한다면? (Thread-per-Request)
동시 요청이 들어올 때마다 일꾼(스레드)을 매번 고용하여 일을 시키고 해고하는 방식이다.
- 단점 1 (비용): 자바에서 스레드 생성 비용은 매우 비싸며, 응답 속도가 늦어지게 된다.
- 단점 2 (컨텍스트 스위칭): 스레드가 너무 많아지면 CPU가 스레드를 전환해가며 일하느라 정작 실행 연산은 하지 못하는 병목 현상이 일어난다.
- 단점 3 (JVM 사망): 동시 접속자가 수만 명으로 폭증하면 스레드가 수만 개가 생성되면서 메모리가 터져 서버가 아예 다운(OOM, OutOfMemoryError)된다.
⭕ 해결책: 스레드 풀(Thread Pool)
일꾼(스레드)을 임의의 고정된 수만큼 미리 대기실(Pool)에 고용해 두고 필요할 때마다 빌려 쓰는 방식이다. 톰캣의 기본 최대 스레드 설정 개수는 200개다.

스레드 풀의 장점
- 응답 시간 대폭 단축: 스레드가 이미 준비되어 있으므로 스레드 생성 시간 없이 즉시 요청에 대응한다.
- 시스템 붕괴 방지: 요청이 폭증하더라도 최대 스레드 수가 200개로 묶여 있기 때문에, 서버는 기존에 맡은 200개의 요청만이라도 온전히 정상 처리할 수 있어 시스템 전체가 함께 다운되는 파멸을 막아준다.
⚠️ 개발자가 반드시 지켜야 할 철칙: 싱글톤 무상태(Stateless) 설계
스레드 풀과 서블릿 싱글톤의 조합으로 인해 서블릿 객체(또는 스프링 빈)는 멀티 스레드 환경에서 여러 스레드가 동시에 공유하며 접근하게 된다. 따라서 클래스 내부에 특정 사용자의 상태나 임의의 값을 보관하는 멤버 변수(필드)를 선언해서는 절대 안 된다! 객체는 언제나 "무상태(Stateless)"로 설계되어 메서드 내부의 지역 변수나 파라미터를 통해서만 값이 전달되고 흘러야 안전하다.
6. HTML, HTTP API, SSR vs CSR 완벽 요약
마지막으로 우리가 설계하는 웹 페이지가 사용자에게 어떻게 렌더링되고 데이터가 전달되는지에 대한 유형을 정리해 보겠다.
1) 정적 리소스 (Static Resources)
- 동작: 사전에 서버에 저장되어 있는 HTML, CSS, JavaScript, 이미지 파일을 가공 없이 그대로 클라이언트에 내려준다.
- 대상: 회사 소개 페이지, 고정 가이드 등 변화가 없는 정적인 화면.
2) 동적 HTML 페이지 (SSR)
- 동작: 서버가 클라이언트의 요청 파라미터나 DB 데이터를 바탕으로 최종 HTML 결과물을 서버 측에서 실시간으로 렌더링해서 완성된 HTML을 웹 브라우저에 던져준다.
- 기술: JSP, Thymeleaf, Freemarker 등.
- 주요 명칭: SSR (Server-Side Rendering). 주로 전통적인 백엔드 중심 개발 방식이다.
3) HTTP API (JSON 데이터 전송)
- 동작: HTML 껍데기를 보내는 대신, 순수한 데이터(주로 JSON 형식)만 주고받는다.
- 용도: 웹 브라우저뿐만 아니라 안드로이드/iOS 앱 통신, 혹은 다른 서버와 통신하는 서버 대 서버(Server-to-Server) 연동에 널리 활용된다.
4) 클라이언트 사이드 렌더링 (CSR)

- 동작: 서버는 비어 있는 간단한 HTML 껍데기와 대용량 JavaScript 파일만 던져준다. 이후 브라우저가 자바스크립트를 해석하여 서버로부터 HTTP API를 통해 JSON 데이터만 추가로 받아와 브라우저 단에서 동적으로 화면을 렌더링하여 조립한다.
- 기술: React, Vue.js, Angular 등 프론트엔드 프레임워크 기반 개발 방식.
- 주요 명칭: CSR (Client-Side Rendering).
7. 마치며 ✍️
오늘 우리는 흔히 사용하는 웹 기술의 가려진 실체를 밑바닥부터 훑어보았다.
- Web Server는 가볍고 빠르게 정적인 리소스를 담당하고,
- WAS는 무겁지만 스마트하게 데이터 가공 및 비즈니스 연산을 담당한다.
- 그 WAS의 심장에는 Tomcat이라는 훌륭한 무대 감독이 있고, 그 위에서 일하는 Servlet이라는 명배우가 있다.
- 동시성이라는 엄청난 무게를 Thread Pool이 훌륭하게 버텨주고 있기에 우리는 오늘도 안정적인 서비스를 누릴 수 있다.
스프링 프레임워크 뒤에 꽁꽁 숨겨져 있던 인프라의 설계 철학을 마음에 새기고, 동시성과 무상태 설계를 의식하면서 개발해 보길 바란다.
'Spring > MVC' 카테고리의 다른 글
| [Spring MVC] Bean Validation과 전송 객체(DTO) 분리: 실무형 검증 및 예외 설계 정리 (0) | 2026.05.27 |
|---|---|
| [Spring MVC] 검증(Validation) 정리 : 수동 검증부터 @Validated와 현대적 전역 API 검증까지 (0) | 2026.05.26 |
| [Spring MVC] 프론트 컨트롤러 패턴 도입부터 어댑터(V5), 로깅, 요청 매핑까지 완벽 정리 (0) | 2026.05.22 |
| [Spring MVC] 역할 분담을 위한 MVC 패턴의 구조와 설계 (0) | 2026.05.21 |
| [Spring MVC] HttpServletRequest & HttpServletResponse 정리 : HTTP 메시지를 자바 객체로 (0) | 2026.05.20 |