View
면접관이 "웹서버와 WAS의 차이가 무엇인가"라고 물었을 때 버벅거린 경험이 있는가? 나 또한 그랬다. 더 골치 아픈 점은 여기에 CGI, WSGI 같은 단어까지 섞여 들어오면 머리가 아예 멈춰버린다는 것이다. 이름은 모두 "서버 비슷한 무언가"라서 유사해 보이지만, 실제로는 각자 다른 계층에서 다른 일을 수행하는 존재들이다.
이 글에서는 CGI, WSGI, WAS의 차이를 시간축과 역할축 두 가지로 정리한다. 역사적으로 어떻게 등장했으며, 현재 실전 배포에서 어떻게 엮이는지까지 다루면 면접에서도 밀리지 않고 실무에서도 용어가 꼬이지 않는다. 파이썬 사용자는 WSGI를 왜 쓰는지 이해하게 되고, 자바 사용자는 톰캣이 왜 WAS로 불리는지 감을 잡게 된다.

출처: YouTube > I.T
웹서버부터 짚고 가자 (정적 vs 동적)
CGI, WSGI, WAS의 차이를 논하기 전에 "웹서버"가 무엇을 하는 존재인지부터 알아야 한다. Apache, Nginx 같은 것들을 의미한다. 이들의 본래 역할은 단순한 파일 배달부다. HTML, CSS, 이미지, JS 같은 정적 파일을 클라이언트(브라우저)가 요청하면 디스크에서 꺼내 그대로 전송하는 것이 전부였다.
그러나 1990년대 중반부터 사람들의 욕심이 생겼다. "로그인 여부에 따라 다른 화면을 보여주고 싶다", "게시판에 글을 써서 DB에 저장하고 싶다", "방문자마다 맞춤 콘텐츠를 제공하고 싶다" 같은 요구였다. 이것이 바로 동적 콘텐츠이다.
웹서버가 혼자서는 못 하는 일
정적 파일 배달만 하던 웹서버에게 동적 처리는 버거운 일이었다. 요청마다 상태가 달라야 하고, DB 쿼리 결과를 HTML로 변환해야 하며, 세션도 관리해야 하고, 파일 업로드도 받아야 한다. 이를 웹서버 코드 안에 모두 박아넣으면 웹서버가 괴물처럼 비대해지고 확장성도 무너진다.
그래서 사람들이 고안한 것이 "웹서버는 파일 배달만 계속하고, 동적 처리는 외부 프로그램에게 위임하자"는 접근이었다. 다만 외부 프로그램에게 어떻게 요청 정보를 넘기고 결과를 다시 받아올 것인지에 대한 약속(인터페이스) 이 필요했다. 여기서 등장한 것이 CGI이다.
CGI란 무엇인가 (1990년대의 해결책)
CGI는 Common Gateway Interface의 약자이다. 번역하면 "공통 게이트웨이 인터페이스"이다. 1993년 NCSA HTTPd 서버에서 처음 등장했고, 곧 웹 전체의 표준이 되었다. 의미 그대로 "웹서버와 외부 프로그램 사이의 공통 규약"이다.
동작 원리는 의외로 단순하다. 웹서버가 /cgi-bin/hello.pl 같은 경로로 요청을 받으면, 웹서버는 해당 스크립트 파일을 새 프로세스로 fork하여 실행한다. 요청 정보는 환경변수와 stdin으로 넘기고, 스크립트는 HTML을 stdout으로 출력한다. 웹서버는 그 출력을 받아 그대로 브라우저로 전송하는 구조이다.
CGI의 장점은 분명하다. 언어 독립적이다. 스크립트가 Perl이든 C든 셸 스크립트든 상관없다. stdin/stdout/환경변수만 다룰 줄 알면 된다. 표준화되어 있어서 Apache, NCSA 서버 어디서도 동일하게 동작했다.
다만 현재 거의 사용되지 않는 이유가 있다. 치명적인 성능 문제이다.
CGI가 쇠락한 이유는 무엇인가
요청 하나가 올 때마다 프로세스를 fork하는 구조가 문제였다. 동시접속자가 100명만 되어도 프로세스 100개가 순간적으로 생성되었다 사라진다. fork 비용, 프로세스 생성 오버헤드, 메모리 낭비가 상당한 수준이다.
더 큰 문제는 상태 공유가 불가능하다는 점이다. DB 커넥션 풀은 어떻게 할 것인가? 요청마다 프로세스가 새로 뜨기 때문에 매번 커넥션을 다시 열어야 한다. 캐시는? 프로세스가 종료되면서 함께 사라진다. 세션 데이터는? 파일이나 DB로 별도 분리해야 한다. 2000년대에 접어들어 웹 트래픽이 폭증하면서 CGI로는 도저히 감당할 수 없었다.
개선 시도로 FastCGI(1996)가 나오기도 했다. 프로세스를 미리 띄워놓고 요청이 올 때마다 재사용하는 방식이다. 그러나 FastCGI도 결국 언어 독립 인터페이스의 한계를 넘지 못했고, 각 언어 생태계가 자신의 언어 전용 인터페이스를 만들기 시작했다. 파이썬은 WSGI로, 자바는 서블릿으로 갈라졌다.
참고로 MDN 용어집에 CGI 설명이 잘 나와 있다. 관심이 있다면 MDN CGI 문서를 한 번 읽어보면 된다.
WSGI — 파이썬이 선택한 표준 인터페이스
WSGI는 Web Server Gateway Interface이다. 2003년에 제안되었고 2010년 PEP 3333으로 공식 표준화되었다. CGI가 지녔던 "언어 독립성"이라는 장점을 포기하는 대신 "파이썬 전용 표준" 으로 방향을 튼 것이다.
핵심은 이것이다. WSGI는 "파이썬 웹 앱과 웹서버가 어떻게 대화할지 정한 규약"일 뿐이다. 실제로 무언가를 수행하는 존재가 아니다. 프로토콜/스펙 문서일 뿐이며, 이 규약을 실제로 구현한 프로그램을 WSGI 서버(또는 WSGI 컨테이너)라고 부른다.
구조는 다음과 같다.
[브라우저]
↓ HTTP
[웹서버: Nginx] — 정적 파일 직접 응답
↓ (동적 요청만 소켓으로 넘김)
[WSGI 서버: Gunicorn] — 파이썬 인터프리터 내장
↓ (WSGI 규약에 따라 함수 호출)
[파이썬 앱: Flask/Django] — 비즈니스 로직
Nginx가 직접 Flask를 실행하지 못하는 이유는 무엇인가
처음에는 다소 헷갈리는 부분이다. "Nginx가 뛰어난데 왜 Flask 앱을 직접 실행하지 못하는가"라는 의문이 드는데, 이유는 단순하다. Nginx에는 파이썬 인터프리터가 없다. Nginx는 C로 작성된 고성능 HTTP 서버일 뿐이며, 파이썬 코드 실행 능력 자체가 없다.
그래서 중간에 파이썬 인터프리터를 품고 있는 통역사가 필요한 것이다. 그것이 바로 Gunicorn이나 uWSGI 같은 WSGI 서버이다. 이들이 Nginx로부터 요청을 소켓으로 받아서, 파이썬 인터프리터에서 Flask/Django 앱을 호출하고, 응답을 다시 Nginx로 돌려주는 역할을 수행한다.
WSGI 서버의 종류
파이썬을 사용하면 한 번쯤 들어봤을 이름들이다.
- Gunicorn: "Green Unicorn"이다. 가장 많이 사용된다. 설정이 간단하고 안정적이다. 대부분 기본값으로도 잘 동작한다. 공식 문서도 깔끔하다.
- uWSGI: 기능이 매우 많다. 다만 옵션이 수백 개라 배우기가 까다롭다. 대규모 트래픽 튜닝 시 진가가 드러난다.
- mod_wsgi: 아파치 모듈 형태이다. 아파치를 쓰는 환경에서의 선택지이다.
- waitress: 윈도우 개발 환경에서 많이 쓴다. 순수 파이썬 구현이라 설치가 간편하다.

출처: YouTube
WAS란 무엇인가 — 웹서버와 어떻게 다른가
여기서 많은 이가 막힌다. WAS는 Web Application Server의 약자이다. "웹 애플리케이션을 실행하고 관리하는 서버"라는 의미이다. 다만 이 용어가 자바 진영에서 먼저 굳어진 탓에 맥락이 다소 꼬여 있다.
자바에서 WAS라고 하면 보통 Tomcat, JBoss(WildFly), WebLogic, WebSphere 같은 것들을 의미한다. 이들은 단순히 파이썬 앱 실행 수준이 아니라 다음을 모두 제공한다.
- 웹서버 기능 (HTTP 처리)
- 서블릿 컨테이너 (자바 웹 앱 실행 환경)
- JSP 컴파일러
- DB 커넥션 풀
- 트랜잭션 관리
- EJB 컨테이너 (요즘은 거의 사용되지 않음)
- 클러스터링, 세션 복제
이 기능들을 한 덩어리로 묶어 제공하는 것이다. 그래서 자바 쪽에서는 "Tomcat은 WAS다"라는 공식이 꽤 자연스럽게 받아들여진다.
그러나 파이썬이나 노드 쪽으로 넘어오면 용어가 다소 다르게 쓰인다. 파이썬에서 "Gunicorn + Flask 조합"은 자바의 Tomcat과 사실상 같은 레이어이다. 노드 쪽은 아예 Node.js 런타임 자체가 HTTP 서버까지 내장하여 WAS 개념을 따로 논하지 않는다. PM2 같은 프로세스 매니저를 WAS 포지션으로 보기도 한다.
웹서버 vs WAS 한 줄 정리
헷갈릴 때마다 되뇌는 공식이다.
- 웹서버: 정적 파일 빠르게 배달 + 리버스 프록시 역할 (Nginx, Apache)
- WAS: 동적 로직 실행 + 앱 라이프사이클 관리 (Tomcat, Gunicorn+Flask, PM2+Node)
실전 배포에서는 이 둘을 함께 쓴다. 앞단에 웹서버, 뒷단에 WAS 구조가 거의 표준이다. 왜일까? 역할 분리로 성능과 보안을 모두 챙길 수 있기 때문이다.
면접 대비용 30초 답변
이 답변을 통째로 외워두면 면접에서 써먹을 만하다.
"웹서버는 정적 콘텐츠를 빠르게 처리하고, WAS는 동적 비즈니스 로직을 실행한다. 실전 배포에서는 Nginx 같은 웹서버가 앞단에서 요청을 받아, 정적 파일이면 직접 응답하고 동적 요청이면 뒷단의 Tomcat이나 Gunicorn 같은 WAS로 프록시 처리한다. 이렇게 역할을 나누면 정적 파일 응답 속도가 빨라지고, WAS는 비즈니스 로직에만 집중할 수 있으며, SSL 종단이나 로드밸런싱도 앞단에서 통합 처리가 가능하다."
이렇게 답변하면 "아, 이 사람은 개념이 정리되어 있구나" 하고 들린다. 실무 용어(리버스 프록시, SSL 종단, 로드밸런싱)를 섞어 쓰면 플러스 요인이다.
CGI → WSGI → ASGI 흐름 한눈에
세 개념의 시간축을 정리하면 다음과 같이 흘러왔다.
- 1993년: CGI 등장. NCSA HTTPd에서 처음 등장
- 1996년: FastCGI로 프로세스 재사용 개선
- 2003년: WSGI 초안 (PEP 333)
- 2010년: WSGI 공식화 (PEP 3333)
- 2019년: ASGI 1.0 스펙. 비동기 시대의 시작
ASGI가 등장한 이유는 무엇인가. WSGI는 근본적으로 동기 방식이라 요즘 트렌드인 WebSocket이나 긴 연결(long polling), 비동기 I/O를 제대로 지원하지 못했기 때문이다. 그래서 Asynchronous Server Gateway Interface로 확장한 것이 ASGI이다. 자세한 내용은 ASGI 공식 사이트에 잘 나와 있다.
CGI, WSGI, ASGI 비교표
| 항목 | CGI | WSGI | ASGI |
| 등장 시기 | 1993년 | 2003년 (PEP 3333: 2010) | 2019년 |
| 처리 방식 | 요청마다 프로세스 fork | 동기 싱글스레드 (멀티 워커) | 비동기 이벤트루프 |
| 대표 구현체 | Perl/C 스크립트 | Gunicorn, uWSGI | Uvicorn, Daphne, Hypercorn |
| 대표 프레임워크 | 없음 (단순 스크립트) | Flask, Django, Pyramid | FastAPI, Django Channels, Starlette |
| WebSocket 지원 | 불가 | 불가 | 가능 |
| 언어 | 언어 독립 | 파이썬 전용 | 파이썬 전용 |
| 장점 | 언어 독립, 표준 | 안정적, 생태계 큼 | 동시성 강함, 실시간 |
| 단점 | 성능 최악 | 동기만 지원 | 상대적으로 새 기술 |
요즘 파이썬 신규 프로젝트에서는 FastAPI + Uvicorn 조합(ASGI)이 많이 늘었다. 다만 Django/Flask 기반의 기존 프로젝트는 여전히 Gunicorn(WSGI)이 현역이다. ASGI가 WSGI를 완전히 대체한 것은 아니며, 용도에 따라 공존하는 단계이다.
실전 배포 구조 예시
이론을 다 들었다면 실제로 어떻게 엮이는지 보는 것이 체감된다. 가장 흔한 조합 세 가지이다.
전통 파이썬 스택 (동기)
[브라우저] → [Nginx :80/:443] → [Gunicorn :8000] → [Flask/Django 앱]
Nginx가 SSL 종단, 정적 파일 서빙, 로드밸런싱을 담당한다. 동적 요청만 Gunicorn으로 프록시 처리한다. Gunicorn은 WSGI 스펙에 따라 Flask/Django 앱의 application 호출 가능한 객체를 실행한다.
비동기 파이썬 스택
[브라우저] → [Nginx] → [Uvicorn] → [FastAPI 앱]
구조는 거의 같지만 WSGI 대신 ASGI를 쓴다. WebSocket을 사용하거나 async/await를 전반적으로 쓰는 프로젝트라면 이쪽이 낫다.
자바 스택
[브라우저] → [Apache HTTPD/Nginx] → [Tomcat :8080] → [서블릿/Spring 앱]
톰캣이 WAS 역할을 모두 수행한다. 서블릿 컨테이너 + JSP 엔진 + 커넥션 풀까지 내장되어 있다. 앞에 아파치나 Nginx를 두는 것은 SSL과 정적 파일 처리 용도가 크다. 톰캣 자체도 HTTP를 직접 받을 수 있지만 앞단에 웹서버를 두는 것이 관례이다. Tomcat 공식 문서에 구조가 잘 나와 있다.
왜 항상 앞에 Nginx 같은 웹서버를 두는가.
- 정적 파일 처리 속도: Nginx가 C로 작성되어 파일 배달 속도가 매우 빠르다. WAS를 거쳐 응답하는 것보다 훨씬 효율적이다
- SSL 종단: HTTPS 복호화를 앞단에서 종료하고 뒷단은 HTTP로 통신한다. WAS 부담이 줄어든다
- 로드밸런싱: WAS 여러 개를 띄우고 앞단의 Nginx가 부하를 분산한다
- 보안: WAS를 외부에 직접 노출시키지 않는다. DDoS 완화, rate limit 등을 앞단에서 처리한다
- 압축/캐싱: gzip, 브라우저 캐시 헤더 관리를 앞단에서 통합 처리한다
3줄 요약 — CGI, WSGI, WAS의 차이 한 번 더
긴 글을 읽느라 수고했다. 각설하고, 기억해야 할 핵심만 압축하면 다음과 같다.
- CGI는 1993년에 등장한 공통 게이트웨이 인터페이스이다. 요청마다 프로세스를 fork하여 동적 처리를 수행한 구식 방법이다. 이제 거의 쓰이지 않는다
- WSGI는 파이썬 전용 규약(PEP 3333)이다. Gunicorn, uWSGI 같은 WSGI 서버가 실제 구현체이며, Flask/Django 앱과 Nginx 사이에서 통역사 역할을 한다
- WAS는 "웹 앱 실행 서버"의 통칭이다. 자바의 Tomcat이 대표적이며, 파이썬에서는 Gunicorn+앱 조합, 노드에서는 런타임 자체가 그 포지션을 차지한다
CGI, WSGI, WAS 차이의 핵심은 시간축(언제 등장했는가) 과 역할축(어느 레이어에서 무엇을 수행하는가) 을 함께 보는 것이다. 이 두 축을 머릿속에 그려놓으면 앞으로 새로운 서버 용어가 나와도 "아, 이것은 이 레이어의 존재로구나" 하고 감을 잡을 수 있다.
다음 단계로 무엇을 할지 막막하다면, Flask 앱을 띄우고 Gunicorn으로 서비스를 돌린 뒤 앞에 Nginx를 붙여보는 실습을 추천한다. 한 번 해보면 이 글의 내용이 머릿속에 각인된다. ASGI를 맛보고 싶다면 FastAPI + Uvicorn으로 간단한 API를 만들어보는 것이 가장 빠르다. 솔직히 직접 띄워보기 전에는 개념이 애매한 것이 정상이다.
'DevOps' 카테고리의 다른 글
| RabbitMQ vs Kafka, 언제 무엇을 사용해야 하는지 정리한다 (0) | 2026.04.30 |
|---|---|
| 도커를 만든 사람을 알면 더 흥미롭다, 솔로몬 하익스 이야기 (0) | 2026.04.30 |
| 트래픽 Traffic 1000배가 터져도 죽지 않는 서버를 만드는 법 (1) | 2026.04.24 |
| 쿠버네티스(Kubernetes)는 누가 만들었는가? 개발자 3인방과 탄생 배경을 파헤치다 (1) | 2026.04.24 |
| CPU 아키텍처에 따른 Docker Multi Architecture 빌드 구성하기 (0) | 2024.02.22 |
