저는 전에 다니던 회사에서 django를 사용했었습니다. django로 개발을 마치고 서버에 올리는 과정에서 django를 처음 접할 때 보았던 문구가 생각이 났습니다.

..ㄱ..그럼 어떻게 배포를 해야하는거지..? 찾아보니 wsgi server를 이용해서 서버를 배포해야 하는 것 같았습니다. 당시에는 다들 이렇게 하니까 저도 wsgi server중 하나인 gunicorn을 사용하여 배포를 진행했었는데, 한 번 wsgi가 무엇인지 자세하게 알아보겠습니다.
이에 대해서 알기 위해선 web의 역사에 대하여 잠깐 알아볼 필요가 있습니다.
웹이 처음 만들어 졌을 때, web은 ‘정적 데이터’만 전달할 수 있었습니다. 그래서 web server의 역할은 static file을 반환하는 것이었습니다. 일반적으로 아래와 같은 과정은 거쳐 request와 response가 이루어졌습니다.
위 과정만 살펴보아도 웹은 ‘웹페이지’라는 파일을 전송하고 client가 그 화면을 볼 수만 있는 구조라는 것을 쉽게 알 수 있습니다. 하지만 이것만 가지고는 할 수 있는 일이 제한적이었기에, 개발자들은 유저로부터 입력을 받거나 하는 html form을 만들고 싶었습니다.
그래서 기존에 static page만 전달하는 것을 넘어서, request가 들어오면 미리 저장해둔 스크립트를 실행해보자는 아이디어가 나왔습니다. 예를 들면, python 스크립트인 form.py를 실행시켜 request의 정보를 전달하고 스크립트의 모든 결과를 response로 전달하는 것입니다. 이를 활용하여 사용자에게 입력을 받아서 웹서버에서 원하는 작업을 처리하는 스크립트를 실행하고 그 결과를 response로 전달할 수 있게 되었습니다.

스크립트를 실행하기 위해 web server는 fork를 이용했습니다. fork를 하게 되면 부모 process로 부터 많은 정보를 상속받을 수 있는데, 그 중 가장 중요한 정보는 아무래도 request에 대한 환경 변수였습니다. 웹 서버는 request 구문을 분석해서 환경변수를 만들어 낸 이후 이를 script process에 넘겨주었습니다. 이 정보를 활용하여 script는 db에 데이터를 저장한다던지 하는 작업을 수행할 수 있었습니다.
이렇게 만들어진 과거의 동적 웹 프로그래밍은 아래와 같은 순서를 따릅니다.
이렇게 웹 동적 프로그래밍은 실현되었으나, 한 가지 문제에 봉착했습니다. web server의 종류는 한 가지만 있는 것이 아니었기에, request를 분석해서 환경변수로 만드는 것은 web server 개발자마다 달랐습니다. 환경변수의 이름도 모두 다를 것이고, 어떤 정보를 전달할 지도 web server의 정책마다 다를 것입니다. 개발자들은 서로 다른 web server에서 실행되는 script를 만드는 데에 너무나 어려움을 겪었습니다.
이식성이 높은 script를 만들기 위해서는 web server가 만드는 환경 변수의 표준이 있으면 될 것입니다. 그래야 web server의 종류와 관계 없이 항상 동일한 정보를 script에 제공할 것이기 때문입니다.
cgi는 common gateway interface의 줄임말입니다. 웹 서버에서 외부 스크립트로 전달되는 환경 변수의 이름과 목적에 대한 표준을 정한 것으로, 1997년에 만들어졌습니다. 아래는 환경변수의 일부분을 발췌한 내용입니다. query string과 같은 익숙한 정보들도 이것의 일부입니다.

python community 에서는 스크립트가 호출되는 방식 또한 표준화 했습니다.
def application(environ, start_response):
status = "200 OK"
headers = [("Content-Type", "text/plain")]
start_response(status, headers)
return [b"Hello, WSGI!"]
위는 그 형태로, 웹서버에서 호출되는 모든 python script는 위의 함수를 가지고 있어야 합니다. 웹서버가 이 함수를 호출하는 것으로 script를 호출하게 됩니다. environ에 cgi 환경변수를 dictionary 형태로 넣어야하고, start_response라는 함수를 제공해야합니다. start_response 함수는 script 종료 전에 반드시 한 번은 불려야 하고, response에 대한 상태가 담긴다 정도로 이해하면 좋을 것 같습니다.
문제라면, web server가 스크립트를 동작시키는 방식이 문제가 된다는 것입니다. fork를 진행하고 새로운 process를 실행하기 위해서 disk에서 script를 읽어오는 것은 전체 과정에서 엄청 긴 시간을 차지합니다. 그도 그럴 수밖에 없는게, 하드디스크는 cpu와 memory에 비해 현저히 느리기 때문입니다. 매 request마다 이런 문제가 발생한다면 대용량 서버에서는 엄청난 병목이 발생할 것입니다.

이 병목을 해결하고자 pre-forking 방식이 등장했습니다. web server가 idle 한 상황이나 처음에 시작될 때 미리 script를 fork해 두는 것입니다. 웹 서버는 worker에 script를 통해 만들어진 application을 대기시켜놓고 클라이언트의 요청이 들어오면 이를 처리하게 했습니다. 이러면 fork할 때 드는 overhead를 매 request마다 감수하지 않아도 되고, worker로 여러개로 구성하여 병렬적으로 요청을 처리해 병목 현상을 줄일 수 있습니다. pre-forking은 자식 process를 대기 시켜놓을 수 있어야 하기 때문에 response를 처리하더라도 process가 죽지 않습니다. 그래서 fork를 여러번 할 필요가 없는 것입니다.

고전적으로 많이 사용해왔던 apache web server에서는 pre forking을 지원합니다. django와 같은 python application을 실행하기 위해 mod_wsgi를 지원하여 web server 내부에 application을 실행할 수 있게 도와줍니다. 하지만 nginx와 같은 web server는 application을 실행해주는 기능은 없습니다. nginx는 대신에 자신에게 들어온 request를 다른 web server에 전송하여 처리하도록 할 수 있습니다. 여기서 말하는 ‘다른 web server’가 바로 우리가 아는 gunicorn 같은 wsgi server입니다.

wsgi server는 http request를 이해하기 때문에 일종의 web server입니다. 거기에 static file을 전송할 수도 있고 pre fork를 통한 application 실행도 가능합니다.
잘 생각해보면 wsgi server는 요즘 흔히 말하는 web application server의 토대가 되고 그 안에 소프트웨어로 실행되는 프레임워크 프로그램이 django인 형태라고 볼 수 있습니다. 물론 다른 프레임워크, 다른 언어 환경에서도 이와 비슷한 것들이 존재합니다. 이것이 다른 진영의 프레임워크에서는 어떻게 적용되는지 아래 표로 표현해 보았습니다.
| 항목 | Python (Flask/Django) | Spring (Spring Boot) | Node.js |
|---|---|---|---|
| 웹 서버 인터페이스 | WSGI | Servlet API (HttpServletRequest) | ❌ 없음 (내장 HTTP 모듈 사용) |
| 애플리케이션 서버 | uWSGI, Gunicorn | Tomcat, Jetty (보통 내장됨) | Node.js 자체가 서버 역할 수행 |
| 웹 프레임워크 | Flask, Django | Spring, Spring Boot | Express, Koa |
| HTTP 요청 처리 방식 | WSGI 서버가 요청 수신 후 앱에 전달 | Servlet Container가 요청 수신 후 앱에 전달 | Node.js에서 직접 요청 수신 |
| 실행 구조 | 웹 서버 별도 실행 + 애플리케이션 | java -jar 실행 시 웹 서버 포함 실행 | node app.js로 서버 실행 |
| WSGI에 해당하는 것 | WSGI 스펙 (PEP 3333) | Java Servlet API | http.createServer 등 API |
ASGI는 Python 웹 애플리케이션과 서버 간의 비동기 인터페이스 표준으로, WSGI(Web Server Gateway Interface)의 한계를 극복하기 위해 만들어졌습니다. wsgi는 성공적으로 서버의 역할을 수행할 수 있었지만, 비동기처리를 지원할 수 없어 대규모 트래픽을 처리해야하는 server에서 한계를 들어냈기 때문입니다.
| 항목 | 설명 |
|---|---|
| ✅ 비동기 지원 | asyncio 기반의 비동기 처리 지원 |
| ✅ 웹소켓 지원 | WebSocket, HTTP/2, SSE 등 지원 |
| ✅ 실시간 통신 | 채팅, 게임, 알림 등 실시간 애플리케이션 구현 가능 |
| ✅ 이벤트 기반 처리 | 요청/응답을 이벤트 루프에서 핸들링 |
| ✅ 멀티 프로토콜 지원 | HTTP뿐 아니라 WebSocket 등 다양한 프로토콜 가능 |