연결을 받고, 정적 파일을 주고, 나머지는 뒤로 넘기는 앞단 서버.
- 정적(static) — /logo.png · /app.js — 누가 언제 요청해도 같은 바이트
- 동적(dynamic) — /orders/1234 — 요청자·시점·DB 상태에 따라 달라진다
웹 서버 nginx · Apache httpd — 연결 수용 · 정적 파일 · 프록시 · TLS 종단
- WAS — Tomcat · Jetty · uWSGI · Puma — 애플리케이션 코드 실행
왜 나누나
-
① 잘하는 일이 다르다
- 동시 연결 1만 개를 유지하는 것과 무거운 코드를 돌리는 것은
- 최적의 설계가 정반대다 (이벤트 기반 vs 스레드 기반)
-
② 앞단이 완충한다
- 느린 클라이언트가 WAS 스레드를 붙잡는 것을 막는다
-
③ 배포·확장 단위를 나눈다
-
④ TLS·압축·캐시 헤더를 애플리케이션이 몰라도 된다
동시성 모델의 갈림
-
프로세스/스레드 기반 — 연결 하나에 스레드 하나
- 블로킹 호출을 그냥 써도 된다 (코드가 단순)
- 연결당 스택 메모리 + 컨텍스트 스위치 비용
- 1만 연결 = 1만 스레드 → 현실적으로 무리
-
이벤트 기반 — 워커 하나가 수천~수만 연결을 돌아가며 처리
- 유휴 연결이 거의 공짜 (자료구조 몇 KB)
- 한 곳에서 블로킹하면 그 워커 전체가 멈춘다
연결이 많고 각각은 한가한 워크로드는 이벤트 기반이 압도적이고, 연결이 적고 각각이 무거운 워크로드는 차이가 작다. 이 갈림이 그대로 배치가 된다 — 바깥은 이벤트 기반 웹 서버, 안쪽은 WAS.
웹 서버가 실제로 내리는 결정
- ① 어느 server 블록인가 — Host 헤더 · SNI
- ② 어느 location 인가 — 경로 매칭 규칙
- ③ 정적인가 프록시인가 — root/alias 로 줄지, upstream 으로 넘길지
- ④ 어떤 헤더로 넘기나 — Host · X-Forwarded-For · X-Forwarded-Proto
- ⑤ 응답을 어떻게 손보나 — gzip · 캐시 헤더 · 보안 헤더
- ⑥ 무엇을 기록하나 — access log · error log
죽었을 때의 차이
웹 서버가 죽으면 아무것도 안 뜬다
- WAS 가 죽으면 — 정적 파일은 계속 나가고, 동적 요청만 502 가 된다
- 점검 페이지를 앞단에서 띄울 수 있다는 뜻이기도 하다
죽었을 때의 차이가 설계를 말해 준다
웹 서버가 죽으면 아무것도 안 뜬다
- WAS 가 죽으면 — 정적 파일은 계속 나가고 동적 요청만 502 가 된다
-
점검 페이지를 앞단에서 띄울 수 있다는 뜻이다
-
location / {
- proxy_pass http://backend;
- proxy_intercept_errors on;
- error_page 502 503 504 /maintenance.html;
-
}
-
location = /maintenance.html { root /var/www/static; internal; }
-
이 다섯 줄이 '백엔드 전체 장애' 를 '안내 페이지' 로 바꿔 준다
지금은 이 그림이 더 길다
클라이언트 → CDN → 클라우드 LB → 인그레스/게이트웨이 → nginx → 애플리케이션
계층이 늘어도 하는 일은 셋뿐이다
- 연결을 받고 · 어디로 보낼지 정하고 · 응답을 손봐 돌려준다
대신 계층마다 타임아웃·버퍼·헤더 처리가 있어서 장애의 상당수가 '계층 간 값이 어긋나서' 생긴다
면접 함정
- ❌ "웹 서버가 없어도 WAS만으로 서비스된다" → 되긴 하지만 느린 클라이언트가 스레드를 잠식한다.
- ❌ "정적 파일도 WAS가 주면 된다" → 이벤트 기반 서버가 훨씬 싸게 처리한다.