본문을 시작하기 앞서, 초창기 Web 설계 구조와 CGI에 대해 먼저 알아보도록 한다.
Web Server(이하 WS)는 client로부터 request를 받아,
정적 컨텐츠만을 response하는 것을 바탕으로 하였다.
즉, Server의 disk에 저장되어 있는 것 그대로 response할 뿐이었다.
이러한 구조로 설계되었기 때문에,
form 따위로 사용자로부터 입력을 받아
그에 따른 로직을 server에서 처리하고 response하는 것은 불가능 했다. (동적 컨텐츠)
이를 가능하게 하기 위해
그러한 request들에 대해서, WS는 외부 스크립트를 호출하는 방식을 사용하였다.
(예를 들어, 외부 스크립트로써 Python file을 호출하는 격이다.)
그리하여 그 스크립트가 실행되고서 return 된 것을 response하는 것이다.
WS는, fork를 통해 외부 스크립트를 실행하였다.
외부 스크립트를 실행하기 위해 process를 fork할 때,
WS로부터 환경 변수를 상속받는다.
WS는, request를 parsing한 후에
해당 요청을 나타내는 환경 변수를 만들고,
그러고 나서 외부 스크립트를 실행한다.
그러면 해당 스크립트는 환경 변수로부터 request에 대한 정보를 알 수 있게 된다.
Common Gateway Interface
하지만 그 당시 초기에, WS의 종류가 워낙 다양했고
이에 따라 환경 변수 형태도 제각각 달랐기 때문에,
외부 스크립트가 모든 WS에 대응하여 실행될 수 있기 위한 표준이 필요했다.
그렇게 탄생한 것이 CGI이다.
즉, CGI는 WS와 외부 script에 있어서 환경 변수의 이름과 목적에 대한 표준이다.
Web Server Gateway Interface
Python community에서는 CGI 개념을 더 확장하였다. (WSGI)
단순히 환경 변수를 표준화하는 것에서 더 나아가,
스크립트가 호출되는 방식을 표준화하였다.
WS에서 호출되는 스크립트는 함수를 가져야 한다는 것으로 말이다.
(참고로, 함수가 아니라 callable이 더 정확하다.
__call__
메서드를 갖는 객체여야 한다는 말이다.)
이런 방식을 통해, WS측에서 Python script를 호출하는 것은
그리 까다로운 일이 아니게 된다.
왜냐면 이 당시 모든 WS가 CGI script로 동작하고 있었고,
다른 한편으로 개발자들은 WSGI의 간단한 규칙만을 준수하면서도
django와 같은 복잡한 framework를 호출할 수 있었기 때문이다.
WSGI의 목적은 웹 서버에서 외부 스크립트가 실행되는 방식을 표준화하는 것이다.
웹 서버에서 쉽게 호출하거나 모든 웹 서버와 쉽게 통합되도록 설계되었으며
모든 웹 프레임워크를 실행할 수 있을 만큼 유연하다.
gunicorn이나 uWSGI와 같은 application server(WSGI server)에서
어떻게 이런 것(serve WSGI)들을 해주는 지에 앞서,
CGI에 대해 한 가지 더 짚고 넘어가야 할 부분이 있다.
CGI sript는 외부 스크립트를 load하는 데에 너무 많은 시간이 걸린다는 문제가 있다.
즉, fork를 하는 것 자체가 병목이라는 것이다.
이에 대한 해결책으로써 pre-fork 방식이 있다.
WS가 idle(유휴 상태)일 때
fork 혹은 외부 스크립트 인터프리터와 dependency들을 시작하는 것이다.
심지어 이 때에는 여러 번 fork 할 수도 있다.
예를 들어, Python 인터프리터를 3번 fork 하면 3개의 WS worker가 생성된다.
이러한 worker는, Python 인터프리터와 이를 실행하는 데 필요한 dependency들의
in-memory 인스턴스이다.
이런 식으로 하면, request가 왔을 때,
WS에서의 처리 시간(request를 parse하고 환경 변수를 생성하는 작업)과
외부 스크립트가 처리되는 시간(request에 따라)만 걸릴 뿐이다.
pre-fork는 WS에서 사용하는 것으로,
WSGI application(application written in python)을 pre-fork 할 수 있다.
다른 말로, WS가 python application을 실행할 수 있다는 말이다.
단, 이는 Apache HTTP Server에 해당되는 것이다. Nginx는 다르다.
Nginx는 WSGI application을 pre-fork 할 수 없다.
대신, WSGI를 application을 pre-fork 할 수 있는 다른 WS로
request를 전달해줄 수 있다.
바로 그 WS 격으로는, gunicorn 혹은 uWSGI와 같은 WSGI server가 해당된다.
이는 HTTP를 이해할 수 있으며, pre-fork 방식으로 WSGI app을 호출할 수 있다.
(일반적으로는 WSGI server와 WS를 따로 구분하지만,
uWSGI의 경우에는 static file을 serve 할 수 있다는 점 등에서
이를 비슷하게 취급해볼 수 있는 여지가 있다.
물론 세부적인 역할과 기능은 크게 다를 수 있지만 말이다.
https://uwsgi-docs.readthedocs.io/en/latest/StaticFiles.html
https://lincolnloop.com/blog/serving-static-files-uwsgi/)
따라서, python application(Python Web framework 포함)을 serve하기 위한
WSGI에 호환되는 WS(내지는 WSGI Server)로는,
gunicorn, uWSGI, Apache HTTP Server, gevent 등이 있다.
WSGI이라는 표준에 의해, 양측에 어떤 것을 사용하더라도 서로 호환 가능하다.
(WSGI를, 일종의 protocol 같은 것이라고 이해해도 좋다.)
Web Server
WSGI servers have HTTP servers built-in.
WSGI servers happen to have HTTP servers ~