CGI: 프로세스 기반의 원시적 통신 규약
CGI는 웹 서버와 외부 프로그램 간의 최초 표준화된 상호작용 규약이다. 클라이언트의 HTTP 요청이 도착할 때마다 웹 서버는 새로운 프로세스를 생성하고, 해당 프로세스의 환경변수와 표준입력(stdin)을 통해 요청 정보를 전달한다. 스크립트가 처리를 마치면 표준출력(stdout)으로 결과를 반환하고 프로세스는 즉시 소멸된다.
이 방식의 치명적 한계는 요청마다 fork() + exec() 오버헤드가 발생한다는 점이다. 수십 동시 접속만으로도 시스템 리소스가 고갈되므로 현대 웹 환경에서는 역사적 유물로 전락했다.
FastCGI: 프로세스 풀을 통한 지속적 연결
FastCGI는 CGI의 근본적 비효율을 해소하기 위해 프로세스 풀(Pool) 개념을 도입했다. 마스터 프로세스가 워커 프로세스들을 미리 생성해두고, 요청이 들어올 때마다 유휴 워커에 할당하는 방식이다.
# Nginx에서 FastCGI 연동 예시
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
프로세스 재사용 외에도 TCP/Unix 소켓을 통한 원격 배포가 가능하며, PHP-FPM 등 다양한 언어 생태계에서 여전히 표준으로 사용된다.
WSGI: 파이썬 생태계의 표준 게이트웨이
WSGI는 네트워크 프로토콜이 아닌 파이썬 함수 시그니처 규약이다. application(environ, start_response)라는 단일 호출 가능 객체(Callable)를 정의함으로써 프레임워크와 서버 간의 결합도를 낮춘다.
# WSGI 규약을 직접 구현한 최소 애플리케이션
def simple_app(env, start_response):
status = '200 OK'
headers = [('Content-Type', 'text/html; charset=utf-8')]
start_response(status, headers)
path = env.get('PATH_INFO', '/')
body = f"<h1>요청 경로: {path}</h1>"
return [body.encode('utf-8')]
# 내장 서버로 직접 실행
from wsgiref.simple_server import make_server
httpd = make_server('localhost', 8080, simple_app)
httpd.serve_forever()
Django의 wsgi.py나 Flask의 app 객체 모두 이 규약을 따른다. 핵심은 서버가 프레임워크를 직접 임포트하여 호출한다는 점이며, 이로 인해 둘은 반드시 같은 프로세스 공간에 존재해야 한다.
uwsgi 프로토콜: 바이너리 레벨의 고속 통신
소문자 uwsgi는 uWSGI 프로젝트가 정의한 바이너리 전송 프로토콜이다. HTTP나 FastCGI 같은 텍스트 기반 프로토콜과 달리, 패킷 헤더와 바디를 바이너리 형태로 직렬화하여 파싱 오버헤드를 최소화한다.
| 특성 | HTTP | FastCGI | uwsgi |
|---|---|---|---|
| 전송 형식 | 텍스트 | 바이너리 | 바이너리 |
| 헤더 오버헤드 | 큼 | 중간 | 최소화 |
| 메타데이터 전달 | 제한적 | 표준화 | 확장 가능 |
| 파이썬 특화 | 아님 | 아님 | 최적화 |
이 프로토콜은 엄밀히 WSGI와 무관하며, 단순히 Nginx와 uWSGI 간의 전송 계층 역할을 수행한다.
uWSGI 서버: 생산 환경의 통합 실행 엔진
대문자 uWSGI는 C로 작성된 고성능 애플리케이션 서버다. WSGI 규약 구현 외에도 다음 기능을 내장한다:
- 마스터-워커 프로세스 아키텍처:
--workers 4로 다중 프로세스 운용 - 프리포크(preload): 애플리케이션 코드를 fork() 전에 메모리에 적재
- 해론(Harakiri): 응답 지연 워커의 강제 종료 및 재생성
- 요청 로드 밸런싱:
--cheaper-algo busyness로 동적 스케일링
; uWSGI 구성 파일 예시 (app.ini)
[uwsgi]
module = myproject:create_app()
master = true
processes = 4
socket = /run/uwsgi/app.sock
chmod-socket = 666
vacuum = true
die-on-term = true
실제 운영 아키텍처의 데이터 흐름
현대 파이썬 웹 서비스의 전형적인 요청 처리 경로는 다음과 같다:
- 클라이언트가
https://api.example.com/users로 요청 전송 - Nginx가 TLS 종료 및 호스트 기반 라우팅 수행
- Nginx가 Unix 소켓을 통해
uwsgi프로토콜로 패킷 전달 - uWSGI 서버가 WSGI
environ딕셔너리 구성 후 파이썬 애플리케이션 호출 - Flask/Django가 비즈니스 로직 처리 및 응답 반환
- 역순으로 응답이 전달되어 클라이언트에 도달
이 구조에서 Nginx는 정적 파일 서빙, SSL/TLS, rate limiting, 캐싱 등 엣지 기능을 담당하고, uWSGI는 파이썬 인터프리터의 다중화와 안정적 실행을 책임진다. 각 계층의 분리는 장애 격리와 독립적 스케일링을 가능하게 한다.