Skip to content

웹 서버와 WAS의 차이 ​

웹 서버(Web Server)는 미리 만들어 둔 파일(정적 콘텐츠)을 그대로 돌려주는 서버이고, WAS(Web Application Server)는 요청이 올 때마다 프로그램을 실행해서 결과(동적 콘텐츠)를 새로 만들어 돌려주는 서버입니다. 실제 서비스에서는 둘을 나눠서 웹 서버를 앞에, WAS를 뒤에 두는 구성을 많이 씁니다.

한눈에 보기 ​

  • 웹에서 주고받는 내용은 크게 두 종류입니다.
    • 정적 콘텐츠: 누가 언제 요청해도 똑같은 파일 (HTML, CSS, 이미지, JS 파일)
    • 동적 콘텐츠: 요청한 사람이나 값에 따라 달라지는 결과 (내 장바구니, 검색 결과, 로그인한 사람 이름)
  • 비유: 식당을 떠올리면 쉽습니다.
    • 웹 서버는 카운터 직원입니다. 물, 휴지, 포장된 음료처럼 미리 준비된 것은 주방에 묻지 않고 바로 건넵니다.
    • WAS는 주방(요리사) 입니다. 주문 내용에 맞춰 냉장고(DB)에서 재료를 꺼내 그때그때 요리합니다.
    • 요리사가 물 따르는 일까지 하면 요리가 늦어집니다. 그래서 일을 나눕니다.

정적 페이지와 동적 페이지 ​

[정적 요청]  /logo.png
   브라우저 ──▶ 웹 서버 ──▶ 디스크의 logo.png를 그대로 전달

[동적 요청]  /cart?id=7
   브라우저 ──▶ 웹 서버 ──▶ WAS ──▶ DB에서 7번 회원의 장바구니 조회
                              │
                              └──▶ 조회 결과로 HTML(또는 JSON)을 새로 만들어 전달

아래 그림의 왼쪽이 정적 요청, 오른쪽이 동적 요청입니다. 웹 서버는 저장된 파일을 꺼내 그대로 돌려주고, WAS는 같은 주소로 요청이 와도 Alice와 Bob에게 각각 다른 결과를 만들어 돌려줍니다.

정적 페이지와 동적 페이지: 웹 서버는 파일을 그대로, WAS는 서블릿이 사람마다 다른 응답을 만든다

그림 출처: Heee's Development Blog - Web Server와 WAS의 차이와 웹 서비스 구조

정적 페이지 (Static Pages) ​

바뀌지 않는 페이지

  • 웹 서버는 파일 경로를 받으면, 그 경로와 일치하는 파일 내용을 그대로 돌려줍니다.
  • 그래서 항상 같은 페이지를 돌려줍니다.
  • 예: 컴퓨터에 저장된 이미지, HTML, CSS, 자바스크립트 파일

동적 페이지 (Dynamic Pages) ​

요청에 담긴 값(인자)에 따라 바뀌는 페이지

  • 요청 내용에 맞는 결과를 그때그때 만들어서 돌려줍니다.
  • 이 결과는 WAS 위에서 실행되는 프로그램이 만듭니다.
  • 자바 진영에서는 이 프로그램을 서블릿(Servlet) 이라고 부릅니다. 개발자는 서블릿 안에 GET 요청을 처리하는 doGet(), POST 요청을 처리하는 doPost() 같은 메서드를 구현합니다. GET과 POST의 차이는 HTTP 요청 메서드에서 다룹니다.

웹 서버 ​

웹 서버라는 말은 두 가지 뜻으로 쓰입니다.

구분뜻
하드웨어웹 서버 프로그램이 설치되어 있는 컴퓨터
소프트웨어브라우저(클라이언트)로부터 HTTP 요청을 받아 정적 콘텐츠(HTML, CSS 등)를 제공하는 프로그램

웹 서버의 기능 ​

웹 서버는 HTTP 프로토콜을 기반으로 클라이언트의 요청을 처리하며, 요청에 따라 두 가지 중 하나를 합니다.

  1. 정적 콘텐츠 제공: WAS를 거치지 않고 자기가 가진 파일을 바로 돌려줍니다.
  2. 동적 콘텐츠 요청 전달: 요청을 WAS에 넘기고, WAS가 처리한 결과를 받아서 클라이언트에게 전달합니다. 이렇게 뒤쪽 서버에 대신 요청을 전달해 주는 역할을 리버스 프록시(Reverse Proxy) 라고 합니다.

요즘 웹 서버는 이 밖에도 다음 일을 함께 맡는 경우가 많습니다.

  • HTTPS 처리(SSL/TLS 종료): 암호화와 복호화를 웹 서버가 대신 맡아서, 뒤쪽 WAS는 암호화를 신경 쓰지 않아도 됩니다.
  • 로드 밸런싱: 요청을 여러 대의 WAS에 골고루 나눠 줍니다.
  • 압축과 캐싱: 파일을 gzip 등으로 압축하거나, 자주 요청되는 응답을 저장해 두었다가 바로 돌려줍니다.

대표적인 웹 서버: Apache HTTP Server, Nginx, IIS(Microsoft)

WAS (Web Application Server) ​

DB 조회나 여러 가지 로직 처리가 필요한 요청에 대해 동적인 콘텐츠를 제공하기 위해 만들어진 애플리케이션 서버

  • HTTP를 통해 애플리케이션을 실행해 주는 미들웨어입니다. 미들웨어는 운영체제와 애플리케이션 사이에서 공통 기능(통신, 스레드 관리, DB 연결 등)을 대신 처리해 주는 소프트웨어입니다.
  • 자바 진영에서는 JSP와 서블릿을 실행시켜 주는 부품을 웹 컨테이너(서블릿 컨테이너) 라고 부르고, WAS는 이 컨테이너를 품고 있습니다. 그래서 흔히 WAS = 웹 서버 기능 + 웹 컨테이너라고 설명합니다.

WAS의 구조: 요청을 받아 넘기는 웹 서버 부분과 JSP·서블릿을 실행하는 웹 컨테이너, 그리고 DB 연결

그림 출처: Heee's Development Blog - Web Server와 WAS의 차이와 웹 서비스 구조

WAS의 주요 기능 ​

  1. 프로그램 실행 환경과 DB 접속 기능 제공
  2. 여러 트랜잭션(한 묶음으로 처리되어야 하는 작업) 관리
  3. 업무를 처리하는 비즈니스 로직 수행

이 밖에도 보안, 스레드 처리, 분산 트랜잭션처럼 여러 서버가 함께 일하는 환경에서 필요한 기능을 제공하며, 주로 DB 서버와 함께 쓰입니다.

대표적인 WAS: Apache Tomcat, JBoss(WildFly), Jetty, WebLogic, JEUS

참고: "WAS"는 주로 자바 진영에서 쓰는 말입니다. 다른 언어에서도 역할은 같아서, Python의 Django나 FastAPI를 실행하는 Gunicorn·Uvicorn, Node.js로 만든 Express 서버도 동적 콘텐츠를 만든다는 점에서 WAS와 같은 자리에 있습니다. 스프링 부트(Spring Boot)는 아예 톰캣을 안에 품고 있어서 따로 설치하지 않아도 됩니다.

둘을 나누는 이유 ​

웹 서버가 필요한 이유 ​

웹 서버가 정적 콘텐츠만 처리하도록 일을 나누면, WAS의 부담이 줄어듭니다.

브라우저가 HTML 문서를 받을 때, 그 안의 이미지 파일은 함께 오지 않습니다.
먼저 HTML 문서를 받고, 거기 적힌 이미지 파일을 다시 서버에 따로 요청합니다.

이미지 같은 정적 파일은 WAS까지 갈 필요 없이
앞단의 웹 서버가 빠르게 돌려줄 수 있습니다.

WAS가 필요한 이유 ​

사용자마다 원하는 결과를 모두 미리 만들어 두는 것은 불가능합니다. 회원이 100만 명이면 장바구니 페이지도 100만 개를 미리 만들어야 하기 때문입니다. WAS는 요청이 들어올 때마다 DB와 비즈니스 로직을 이용해 필요한 결과만 만들어서 자원을 효율적으로 씁니다.

WAS가 웹 서버 역할까지 다 하면 안 되나요? ​

할 수는 있습니다. 톰캣도 정적 파일을 돌려줄 수 있습니다. 하지만 실제 서비스에서는 나누는 경우가 많습니다.

  • 부하 분산: WAS는 DB 조회와 로직 처리에 집중해야 합니다. 단순한 정적 파일 요청까지 처리하면 부하가 커지고, 정작 동적 콘텐츠 처리가 늦어져 페이지가 느리게 뜹니다.
  • 여러 대의 WAS 연결: 웹 서버 한 대 뒤에 WAS 여러 대를 두고 요청을 나눠 줄 수 있습니다.
  • 장애 대응: 웹 서버가 WAS의 상태를 확인하다가 한 대에 문제가 생기면 그쪽으로 요청을 보내지 않고, 그동안 문제가 생긴 WAS를 재시작할 수 있습니다. 사용자는 오류를 거의 느끼지 못합니다.
  • 보안: 바깥에는 웹 서버만 드러내고 WAS와 DB는 안쪽 네트워크에 숨길 수 있습니다.
  • 배포와 유지 보수: 정적 파일만 바꿀 때는 WAS를 건드리지 않아도 됩니다.

정리하면 자원 이용의 효율성, 장애 극복, 배포와 유지 보수의 편의성 때문에 웹 서버와 WAS를 나눠서 씁니다.

실제 예: 지금 보고 계신 이 사이트(1mo.dev)는 모든 페이지를 미리 HTML로 만들어 두는 정적 사이트라서, WAS 없이 GitHub Pages라는 정적 파일 서버만으로 운영됩니다. 로그인, 댓글처럼 사람마다 다른 결과가 필요해지는 순간 WAS(또는 외부 API)가 필요해집니다.

가장 효율적인 구성과 요청 처리 흐름 ​

웹 서버를 WAS 앞에 두고, 필요한 WAS들을 웹 서버에 연결해 두면 효율적으로 요청을 나눠 처리할 수 있습니다.

                         ┌──▶ WAS 1 ──┐
 브라우저 ──▶ 웹 서버 ───┤            ├──▶ DB
              (Nginx)    └──▶ WAS 2 ──┘
                 │
                 └──▶ 정적 파일은 여기서 바로 응답

자바 서블릿 기준으로 이 구성을 더 자세히 그리면 아래와 같습니다. 웹 서버가 동적 요청을 웹 컨테이너에 넘기면, 컨테이너가 스레드를 배정하고 서블릿의 service()가 doGet()/doPost()를 불러 DB와 함께 결과를 만듭니다.

웹 서비스 구조: 클라이언트, 웹 서버, WAS 안의 웹 컨테이너와 스레드, 서블릿 메서드, DB

그림 출처: Heee's Development Blog - Web Server와 WAS의 차이와 웹 서비스 구조

자바 서블릿 기준으로, 동적 요청 하나가 처리되는 과정은 다음과 같습니다.

  1. 클라이언트의 요청을 웹 서버가 먼저 받고, 동적 요청이면 WAS에 넘깁니다.
  2. WAS는 설정(web.xml 또는 애너테이션)을 보고 이 요청을 처리할 서블릿을 찾습니다. 서블릿 객체는 처음 요청될 때 한 번만 메모리에 올라가고, 이후에는 그 객체를 계속 재사용합니다.
  3. WAS는 스레드 풀에서 놀고 있는 스레드 하나를 꺼내 이 요청에 배정합니다.
  4. 요청 정보를 담은 HttpServletRequest와 응답을 담을 HttpServletResponse 객체를 만들어 서블릿에 전달합니다.
  5. 스레드는 서블릿의 service() 메서드를 호출하고, service()는 요청 방식에 맞게 doGet() 또는 doPost()를 호출합니다.
  6. doGet()/doPost()는 요청에 맞는 동적 페이지를 만들어 Response 객체에 담습니다.
  7. WAS는 Response 객체를 HTTP 응답으로 바꿔 웹 서버로 보내고, 웹 서버가 클라이언트에게 전달합니다.
  8. 다 쓴 Request/Response 객체는 버리고, 스레드는 종료하지 않고 스레드 풀로 돌려보내 다음 요청에 다시 씁니다.

스레드를 요청마다 새로 만들고 없애면 비용이 크기 때문에, 미리 만들어 둔 스레드를 빌려 쓰고 돌려주는 스레드 풀을 씁니다. 스레드가 무엇인지는 프로세스와 스레드를 참고하세요.

코드로 감 잡기: 정적 응답과 동적 응답 ​

Python 내장 http.server로 아주 작은 서버를 만들어, 한 서버 안에서 "파일을 그대로 주는 길"과 "그때그때 만들어 주는 길"을 나눠 봅니다. 실제 서비스에서는 첫 번째 길을 Nginx 같은 웹 서버가, 두 번째 길을 WAS가 맡습니다.

python
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
from datetime import datetime
from pathlib import Path

STATIC_DIR = Path("static")

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        url = urlparse(self.path)

        # 1) 정적 콘텐츠: 파일을 그대로 읽어서 돌려줌 (웹 서버의 일)
        if url.path.startswith("/static/"):
            file = STATIC_DIR / url.path.removeprefix("/static/")
            body = file.read_bytes()
            self.reply(body, "text/html; charset=utf-8")

        # 2) 동적 콘텐츠: 요청 값에 따라 그때그때 결과를 만듦 (WAS의 일)
        elif url.path == "/hello":
            name = parse_qs(url.query).get("name", ["손님"])[0]
            now = datetime.now().strftime("%H시")
            body = f"<h1>{name}님, 지금은 {now}입니다</h1>".encode()
            self.reply(body, "text/html; charset=utf-8")
        else:
            self.send_error(404)

    def reply(self, body, ctype):
        self.send_response(200)
        self.send_header("Content-Type", ctype)
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, *args):   # 접속 기록 출력은 생략
        pass

HTTPServer(("127.0.0.1", 8765), Handler).serve_forever()

static/index.html 파일에 <h1>항상 같은 페이지</h1>를 넣고 서버를 켠 뒤, 터미널에서 요청을 보내 봅니다.

bash
curl -s http://127.0.0.1:8765/static/index.html
curl -s http://127.0.0.1:8765/static/index.html
# 한글 값은 주소에 그대로 넣으면 안 되고 인코딩해야 하므로 --data-urlencode를 씀
curl -s -G http://127.0.0.1:8765/hello --data-urlencode "name=철수"
curl -s -G http://127.0.0.1:8765/hello --data-urlencode "name=영희"

실행 결과:

<h1>항상 같은 페이지</h1>
<h1>항상 같은 페이지</h1>
<h1>철수님, 지금은 06시입니다</h1>
<h1>영희님, 지금은 06시입니다</h1>

정적 파일은 몇 번을 요청해도 똑같고, 동적 응답은 요청 값(이름)과 실행한 시각에 따라 내용이 달라집니다.

면접에서 자주 나오는 질문 ​

Q. 웹 서버와 WAS의 차이는 무엇인가요? 웹 서버는 HTML, CSS, 이미지 같은 정적 콘텐츠를 그대로 제공하고, 동적 요청은 WAS에 전달합니다. WAS는 DB 조회와 비즈니스 로직을 실행해서 요청마다 다른 동적 콘텐츠를 만들어 냅니다. 대표적으로 웹 서버는 Nginx, Apache, WAS는 Tomcat이 있습니다.

Q. WAS만으로도 서비스가 가능한데 왜 웹 서버를 따로 두나요? 정적 파일 처리를 웹 서버에 맡겨 WAS의 부하를 줄이고, 웹 서버가 여러 WAS에 요청을 나눠 주며(로드 밸런싱), 장애가 난 WAS를 빼고 요청을 보낼 수 있기 때문입니다. HTTPS 처리를 맡기거나 WAS를 바깥에 드러내지 않아 보안을 높이는 효과도 있습니다.

Q. 리버스 프록시가 무엇인가요? 클라이언트 대신 뒤쪽 서버에 요청을 전달해 주는 서버입니다. 클라이언트는 웹 서버하고만 이야기한다고 생각하지만, 실제로는 웹 서버가 뒤의 WAS에게 요청을 넘기고 응답을 받아 전달합니다.

Q. 서블릿 컨테이너는 요청마다 스레드를 새로 만드나요? 아닙니다. 미리 만들어 둔 스레드 풀에서 스레드를 꺼내 요청에 배정하고, 처리가 끝나면 다시 풀에 돌려줍니다. 서블릿 객체도 하나만 만들어 여러 스레드가 함께 씁니다.

정리 ​

구분웹 서버WAS
다루는 콘텐츠정적 콘텐츠 (파일 그대로)동적 콘텐츠 (요청마다 새로 만듦)
주요 역할파일 제공, 요청 전달(리버스 프록시), HTTPS 처리, 로드 밸런싱비즈니스 로직 실행, DB 연동, 트랜잭션 관리
비유카운터 직원주방 요리사
대표 예Nginx, Apache HTTP Server, IISTomcat, JBoss(WildFly), Jetty
나누는 이유부하 분산, 장애 극복, 보안, 배포·유지 보수 편의