[Python] REST API 개발로 알아보는 WSGI, ASGI

반응형

REST API 서버를 개발하기 위해서는 이제 특정 언어를 배워야 할 필요 없이 원하는 언어로 자유롭게 개발할 수 있는 세상이 오게 되었습니다.

 

Python 언어 또한 다양한 프레임워크를 제공함으로써 REST API 서버를 쉽게 개발할 수 있는데요. 이를 제공하는 다양한 프레임워크가 많이 존재하지만 그 중 가장 보편화 되어 알려진 프레임워크는 아래의 두 가지 일 것입니다.

 

  • Django
  • Flask

Django는 MTV 패턴을 제공하여 API 뿐만 아니라 템플릿을 이용하여 정적 HTML 파일을 생성해 뷰를 만들 수 있도록 제공하여 빠른 개발을 통해 높은 생산성을 자랑하는 프레임워크입니다. 반면 Flask는 오직 API만을 쉽게 제공하여 빠른 API 서버만을 만들 수 있도록 제공하는 것이 특징이죠.

 

Spring 계열에서는 이렇게 만들어진 웹 서비스를 Tomcat 등의 WAS로 배포를 하게 되는데요. 공교롭게도 Python에서는 이러한 WAS를 제공해주지 않습니다. 그렇다면 어떤 방법으로 웹 서버와 Python 애플리케이션을 연결할 수 있을까요?

 

 

 

 

CGI와 FastCGI

외부 애플리케이션과 웹 서버(nginx, Apache 등)와 연결할 떄 사용하는 인터페이스로 보통 CGI를 사용합니다 CGI는 Common Gateway Interface의 약자로 웹 서버와 외부 프로그램을 연결해주는 표준화된 프로토콜입니다.

 

이러한 모형은 웹 서비스의 가장 일반적인 모습이지만 과거에는 정적 HTML 파일 하나를 가지고 웹 서비스를 하였기 때문에 이러한 요소(CGI)가 필요하지 않았습니다. 웹 서버가 정적 파일 등을 사용자에게 다운로드 방식으로 제공하는 것이 전부인데, 웹에 대한 수요가 증가함에 따라서 웹 서버가 처리할 수 없는 정보가 요청되었을 경우 그 처리를 외부 애플리케이션이 할 수 있도록 호출함으로써 중계 역할을 해줍니다.

 

그러나 CGI는 클라이언트의 요청이 발생할 때마다 프로세스를 추가로 생성하고 삭제하게 됩니다. 예를 들어 위 그림과 같이 다수의 사용자가 동시에 요청할 경우 새로운 프로세스를 생성하고, 삭제하는 것이 빈번해지는데 이는 커널 리소스를 계속 생성/삭제하기 때문에 오버헤드가 심해지고, 성능 저하의 원인이 되기도 합니다.

 

현대의 웹 서비스처럼 빈번하게 REST API가 호출 되고, 요청과 응답을 수시로 반복하여 처리하는 곳에서 이러한 아키텍처는 맞지 않습니다. 그래서 생긴 것이 바로 FastCGI 입니다.

 

FastCGI는 몇 번의 요청이 들어와도 하나의 프로세스만을 가지고 처리하게 됩니다. 즉 메모리에 단 하나의 프로그램만을 적재하여 계속 재활용하기 때문에 CGI에 비하여 오버헤드가 월등하게 감소합니다.

 

Java의 Tomcat 또한 Web Server + FastCGI를 채택한 형태로 다수의 사용자 접속에 유리합니다. 우리는 JVM 계열의 언어로 서버 프로그램을 작성하면 JBoss, Tomcat 등의 웹 애플리케이션 서버를 사용하는 게 보편적이지만 이 외에도 우리가 CGI를 수동으로 붙여서 사용할 수 있는 방법도 있었습니다.

 

그러나 Python에서는 이러한 WAS가 별도로 존재하지 않으며 결국 우리가 CGI, FastCGI 등을 이용해서 원하는 WAS 형태를 만들어 사용해야 하는데, 실제로 Python에서는 Python만의 게이트웨이 인터페이스가 존재합니다.

 

 

 

 

WSGI

Python 애플리케이션, 스크립트가 웹 서버와 통신하기 위한 인터페이스로써 CGI 디자인 패턴을 모태로 하여 만들어진 것이지만 실제 CGI와는 다소 차이가 있습니다.

 

Python Web App을 MTV 패턴과 같이 뷰가 동시에 적용되는 프레임워크를 사용한다고 가정했을 떄 모습을 같이 그려봤습니다. WSGI는 CGI와 동일하게 웹 서버와 애플리케이션 중간에 위치하는데, CGI와 다른 점은 CGI는 매 요청마다 프로세스를 생성한다는 점이고, WSGI는 한 프로세스에서 모든 요청을 받는다는 것입니다. 좀 더 디테일하게 설명을 드리자면 CGI는 매 요청마다 Fork 등의 함수를 통해 커널 리소스를 추가/반납하고, WSGI는 많은 요청을 콜백(callback)으로 받아 처리하게 됩니다.

 

이러한 WSGI로 대표적인 두 가지 방법이 있습니다.

 

  1. nginx, apache에서 내장 모듈로 제공하는 server-often high profile 방식 (ex: mod-wsgi, mod-python 등)
  2. Python 코드로 작성된 Web App Server (ex: gunicorn, uvicorn, cherrypy 등)

결국 WSGI는 웹 서버와 애플리케이션 사이에 미들웨어 역할을 하며 기술적으로는 웹 서버도 WSGI에 대한 작동 코드가 필요하고, 애플리케이션 또한 WSGI에 대한 작동 코드가 필요한 Client-Server Model을 응용한 것이라고 보면 됩니다. 좀 더 간단히 설명해보자면, 웹 서버가 애플리케이션의 코드를 직접적으로 읽을 수 없으므로 중간의 미들웨어가 해당 코드를 읽어서 결과를 대신 반환해주는 녀석이라고 보면 됩니다.

 

 

 

ASGI

WSGI만으로도 우리는 충분히 클라이언트의 요청을 웹 서버가 받아 내 애플리케이션으로 올 수 있도록 구성할 수 있었습니다. 하지만 공교롭게도 이것만으로는 현대 웹 서비스의 대용량 트래픽 처리를 유연하게 하기 위한 만족된 조건을 충족하기 어려운 점이 있습니다.

 

Python에서는 asyncio, coroutine과 같은 비동기 처리를 지원합니다. 그러나 WSGI는 동기 함수 처리만을 지원하여 여러 작업을 동시에 처리하는 것에 한계가 있습니다. 가령 현대 웹 서비스에서는 웹 소켓 등을 사용한 실시간 채팅 서비스 등을 할 수도 있는데, WSGI로는 이러한 서비스를 구현하는 데 어려움이 있는 것이죠.

 

물론 안되는 것은 아닙니다. 비동기 큐와 같은 Celery를 잘만 활용한다면 대용량 트래픽 처리를 요구하는 서비스 구현이 불가능한 것은 아니지만 트레이싱 등과 같은 유지 보수, 기본적인 구현이 쉽지 않다는 점이 있습니다.

 

따라서 최근에는 Django 3.0 뿐만 아니라 FastAPI 등의 프레임워크에서도 ASGI 인터페이스를 적용하였으며 뒤따라 Falcon 프레임워크도 3.0부터 ASGI 개발에 들어갔습니다.

 

운영 아키텍처로 봤을 때는 크게 다르지 않습니다. 그러나 WSGI와 다르게 ASGI는 기본적으로 요청을 비동기로 처리한다는 점이 큽니다. 따라서 WSGI에서는 지원되지 않는 Websocket 프로토콜과 HTTP 2.0을 지원합니다. 

 

 

Uvicorn

The lightning-fast ASGI server. Introduction Uvicorn is a lightning-fast ASGI server implementation, using uvloop and httptools. Until recently Python has lacked a minimal low-level server/application interface for asyncio frameworks. The ASGI specificatio

www.uvicorn.org

이러한 대표적인 ASGI Web App에는 uvicorn이 있습니다. uvicorn은 ASGI 기반의 웹 애플리케이션 서버로써 그 내장 모듈로 uvloop을 사용합니다. 아마 들어보신 분들은 아실 수도 있겠지만 uvloop에서 uv는 libuv 즉, Javascript V8에서 사용되는 비동기 모듈을 사용한 것입니다. ASGI는 Cython 기반으로 C++ 언어로 작성되어 매우 빠른 속도를 제공한다는 것이 특징인데다가 libuv를 사용하여 비동기 처리를 하니 Node.js와 같은 비동기 처리 속도를 어느 정도 누릴 수 있다는 장점이 있습니다.

 

 

 

 

마치며...

Python을 이용하여 웹 애플리케이션을 배포할 수 있는 다양한 방법에 대해 알아봤습니다. 

 

Python에서도 Java와 마찬가지로 비동기 처리에 대해 관심을 보이고 있으며 그에 대안으로 WSGI의 상위인 ASGI를 개발하였고, 이미 몇몇 프레임워크에서 지원을 하고 있습니다.

 

어쩌다보니 Python 진영에 와서 백엔드 서버 개발을 맡게 되었지만 Python도 Java와 마찬가지로 여러가지 신경써줘야 할 부분들이 많았습니다. 그렇다보니 이것저것 공부를 하게 되었고, 이렇게 글을 쓰게 되었는데요. 차후 Python 포스트로 REST API 애플리케이션을 개발한 삽질기에 대해 여러 글을 써보고자 합니다. 

 

 

 

 

 

 

반응형