ASGI CGI WSGI 비교 분석

이 기술들은 웹 서버와 애플리케이션 간의 통신 방식을 정의하는 인터페이스 규격.
시대 순으로 발전 과정을 이해하면 좋을 것 같다.

ASGI CGI WSGI 비교

특징CGIWSGIASGI
등장 시기1990년대 초반2003년 (PEP 333)2016년
처리 방식프로세스 기반동기식비동기식
성능매 요청마다 새 프로세스 생성 (낮음)프로세스 재사용 (중간)비동기 처리로 높은 성능
프로토콜 지원HTTP/1.0HTTP/1.1HTTP/1.1, HTTP/2, WebSocket
구현 복잡도단순함중간상대적으로 복잡함
메모리 사용높음 (프로세스당)중간효율적
동시성 처리프로세스 기반스레드/프로세스 기반이벤트 루프 기반
주요 사용 사례레거시 시스템전통적인 웹 애플리케이션현대적 웹 애플리케이션
환경 변수 처리시스템 환경 변수environ 딕셔너리scope 딕셔너리
스트리밍 지원제한적이터레이터 기반네이티브 지원
프레임워크 예시직접 구현Django, FlaskFastAPI, Starlette
서버 예시Apachegunicorn, uWSGIuvicorn, daphne
오류 처리제한적표준화된 방식포괄적 지원
설정 복잡도간단중간상대적으로 복잡
확장성제한적중간높음

각 기술의 기본적인 구현 예제

CGI (Common Gateway Interface):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/python
import os

print("Content-Type: text/html\n")
print("<html><body>")
print("<h1>Hello from CGI!</h1>")
print("<p>Environment Variables:</p>")
for key, value in os.environ.items():
    print(f"{key}: {value}<br>")
print("</body></html>")

WSGI (Web Server Gateway Interface):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def simple_wsgi_app(environ, start_response):
    """간단한 WSGI 애플리케이션"""
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)
    
    # 환경 변수에서 요청 정보 읽기
    request_method = environ.get('REQUEST_METHOD')
    path_info = environ.get('PATH_INFO')
    
    return [f"Method: {request_method}\nPath: {path_info}".encode()]

ASGI (Asynchronous Server Gateway Interface):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
async def simple_asgi_app(scope, receive, send):
    """간단한 ASGI 애플리케이션"""
    assert scope['type'] == 'http'
    
    # 클라이언트로부터 요청 받기
    await receive()
    
    # 응답 보내기
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ],
    })
    
    await send({
        'type': 'http.response.body',
        'body': b'Hello from ASGI!',
    })

각 기술의 주요 사용 시나리오

파일 업로드 처리

CGI:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/python
import cgi, os

form = cgi.FieldStorage()
fileitem = form['filename']

if fileitem.filename:
    fn = os.path.basename(fileitem.filename)
    open('/tmp/' + fn, 'wb').write(fileitem.file.read())
    message = '파일이 업로드되었습니다'
else:
    message = '업로드 실패'

print("Content-Type: text/html\n")
print(message)

WSGI:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def file_upload_app(environ, start_response):
    from wsgiref.util import FileWrapper
    
    status = '200 OK'
    headers = [('Content-Type', 'text/html')]
    start_response(status, headers)
    
    if environ['REQUEST_METHOD'] == 'POST':
        post_env = environ.copy()
        post_env['QUERY_STRING'] = ''
        
        # 파일 처리 로직
        return [b"File uploaded successfully"]
    return [b"Please upload a file"]

ASGI:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
async def file_upload_app(scope, receive, send):
    if scope['type'] == 'http':
        # 요청 본문 받기
        body = b''
        more_body = True
        
        while more_body:
            message = await receive()
            body += message.get('body', b'')
            more_body = message.get('more_body', False)
            
        # 파일 처리 로직
        await send({
            'type': 'http.response.start',
            'status': 200,
            'headers': [
                [b'content-type', b'text/plain'],
            ],
        })
        
        await send({
            'type': 'http.response.body',
            'body': b'File processed asynchronously',
        })

참고 및 출처