WSGI를 알아보자

hjboom·2025년 4월 8일
post-thumbnail

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

..ㄱ..그럼 어떻게 배포를 해야하는거지..? 찾아보니 wsgi server를 이용해서 서버를 배포해야 하는 것 같았습니다. 당시에는 다들 이렇게 하니까 저도 wsgi server중 하나인 gunicorn을 사용하여 배포를 진행했었는데, 한 번 wsgi가 무엇인지 자세하게 알아보겠습니다.

이에 대해서 알기 위해선 web의 역사에 대하여 잠깐 알아볼 필요가 있습니다.


웹의 역사

web은 원래 정적이었다

웹이 처음 만들어 졌을 때, web은 ‘정적 데이터’만 전달할 수 있었습니다. 그래서 web server의 역할은 static file을 반환하는 것이었습니다. 일반적으로 아래와 같은 과정은 거쳐 request와 response가 이루어졌습니다.

  1. request로 A.html file을 달라고 요청이 들어옴
  2. disk에서 A.html file을 찾음
  3. A.html을 읽고 response에 반환

위 과정만 살펴보아도 웹은 ‘웹페이지’라는 파일을 전송하고 client가 그 화면을 볼 수만 있는 구조라는 것을 쉽게 알 수 있습니다. 하지만 이것만 가지고는 할 수 있는 일이 제한적이었기에, 개발자들은 유저로부터 입력을 받거나 하는 html form을 만들고 싶었습니다.

스크립트를 실행해보자

그래서 기존에 static page만 전달하는 것을 넘어서, request가 들어오면 미리 저장해둔 스크립트를 실행해보자는 아이디어가 나왔습니다. 예를 들면, python 스크립트인 form.py를 실행시켜 request의 정보를 전달하고 스크립트의 모든 결과를 response로 전달하는 것입니다. 이를 활용하여 사용자에게 입력을 받아서 웹서버에서 원하는 작업을 처리하는 스크립트를 실행하고 그 결과를 response로 전달할 수 있게 되었습니다.

스크립트를 실행하기 위해 web server는 fork를 이용했습니다. fork를 하게 되면 부모 process로 부터 많은 정보를 상속받을 수 있는데, 그 중 가장 중요한 정보는 아무래도 request에 대한 환경 변수였습니다. 웹 서버는 request 구문을 분석해서 환경변수를 만들어 낸 이후 이를 script process에 넘겨주었습니다. 이 정보를 활용하여 script는 db에 데이터를 저장한다던지 하는 작업을 수행할 수 있었습니다.

이렇게 만들어진 과거의 동적 웹 프로그래밍은 아래와 같은 순서를 따릅니다.

  1. 개발자가 처리해야할 스크립트 경로가 포함된 양식을 만들어 유저에게 전송
  2. 유저가 양식을 채워서 web server로 전송
  3. web server가 request를 분석하여 외부 스크립트가 포함되어 있음을 감지
  4. web server가 request에 대한 정보를 환경변수로 만듦
  5. 외부 script를 fork로 실행하여 환경변수를 전달해 response를 보냄

script 종속성

이렇게 웹 동적 프로그래밍은 실현되었으나, 한 가지 문제에 봉착했습니다. web server의 종류는 한 가지만 있는 것이 아니었기에, request를 분석해서 환경변수로 만드는 것은 web server 개발자마다 달랐습니다. 환경변수의 이름도 모두 다를 것이고, 어떤 정보를 전달할 지도 web server의 정책마다 다를 것입니다. 개발자들은 서로 다른 web server에서 실행되는 script를 만드는 데에 너무나 어려움을 겪었습니다.

이식성이 높은 script를 만들기 위해서는 web server가 만드는 환경 변수의 표준이 있으면 될 것입니다. 그래야 web server의 종류와 관계 없이 항상 동일한 정보를 script에 제공할 것이기 때문입니다.

CGI의 탄생

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 작동방식 문제

문제라면, web server가 스크립트를 동작시키는 방식이 문제가 된다는 것입니다. fork를 진행하고 새로운 process를 실행하기 위해서 disk에서 script를 읽어오는 것은 전체 과정에서 엄청 긴 시간을 차지합니다. 그도 그럴 수밖에 없는게, 하드디스크는 cpu와 memory에 비해 현저히 느리기 때문입니다. 매 request마다 이런 문제가 발생한다면 대용량 서버에서는 엄청난 병목이 발생할 것입니다.

Pre-Forking

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

Wsgi Server

고전적으로 많이 사용해왔던 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
웹 서버 인터페이스WSGIServlet API (HttpServletRequest)❌ 없음 (내장 HTTP 모듈 사용)
애플리케이션 서버uWSGI, GunicornTomcat, Jetty (보통 내장됨)Node.js 자체가 서버 역할 수행
웹 프레임워크Flask, DjangoSpring, Spring BootExpress, Koa
HTTP 요청 처리 방식WSGI 서버가 요청 수신 후 앱에 전달Servlet Container가 요청 수신 후 앱에 전달Node.js에서 직접 요청 수신
실행 구조웹 서버 별도 실행 + 애플리케이션java -jar 실행 시 웹 서버 포함 실행node app.js로 서버 실행
WSGI에 해당하는 것WSGI 스펙 (PEP 3333)Java Servlet APIhttp.createServer 등 API

Asgi

ASGI는 Python 웹 애플리케이션과 서버 간의 비동기 인터페이스 표준으로, WSGI(Web Server Gateway Interface)의 한계를 극복하기 위해 만들어졌습니다. wsgi는 성공적으로 서버의 역할을 수행할 수 있었지만, 비동기처리를 지원할 수 없어 대규모 트래픽을 처리해야하는 server에서 한계를 들어냈기 때문입니다.

  • WSGI는 동기(Synchronous) 방식만 지원
  • ASGI는 비동기(Async) + 동기(Sync) 모두 지원
항목설명
✅ 비동기 지원asyncio 기반의 비동기 처리 지원
✅ 웹소켓 지원WebSocket, HTTP/2, SSE 등 지원
✅ 실시간 통신채팅, 게임, 알림 등 실시간 애플리케이션 구현 가능
✅ 이벤트 기반 처리요청/응답을 이벤트 루프에서 핸들링
✅ 멀티 프로토콜 지원HTTP뿐 아니라 WebSocket 등 다양한 프로토콜 가능

참고문헌

https://www.youtube.com/watch?v=UklcIDgHtwQ

0개의 댓글