Python ASGI vs WSGI
ASGI vs WSGI
- Python 웹 프레임워크인 Django나 Flask에서는 웹 서버와의 통신을 효율적으로 처리하고, 서버의 요청을 애플리케이션으로 전달하기 위해 WSGI 또는 ASGI라는 인터페이스를 사용합니다.
- 이 인터페이스는 Python 웹 애플리케이션의 표준 방식으로, 웹 서버와 Python 애플리케이션 사이에서 중간 역할을 합니다.
WSGI (Web Server Gateway Interface)
- WSGI는 동기적인 웹 애플리케이션과 웹 서버 간의 인터페이스를 정의한 Python의 표준입니다. WSGI는 Python 웹 애플리케이션이 웹 서버와 소통할 수 있도록 도와주며, 동작 방식은 매우 단순합니다.
- 동기 처리: WSGI는 기본적으로 동기 방식을 지원합니다. 즉, 요청이 들어오면 해당 요청이 완전히 처리될 때까지 다른 요청을 처리할 수 없습니다. 이는 전통적인 웹 애플리케이션에서 흔히 사용되는 방식입니다.
- Django와 Flask: Django와 Flask 모두 처음에는 WSGI를 사용하여 동작했습니다. 이 방식은 CPU 집약적인 작업이 많지 않거나 I/O 작업이 복잡하지 않은 웹 애플리케이션에서 충분히 효율적입니다. 그러나 동시성이나 비동기 처리가 필요한 경우에는 성능에 한계가 있습니다.
WSGI 미들웨어
WSGI 미들웨어는 서버와 애플리케이션 사이에 위치하여 요청을 가로채고 수정하거나, 애플리케이션의 응답을 조작할 수 있는 역할을 합니다. 미들웨어는 인증, 로깅, 세션 관리 등의 기능을 추가하는 데 유용합니다
WSGI의 장점
- 프레임워크 독립성: WSGI를 사용하면 Django, Flask, Pyramid 등 다양한 Python 웹 프레임워크를 사용할 수 있습니다. 즉, 애플리케이션은 특정 서버에 종속되지 않으며 WSGI를 지원하는 모든 서버에서 실행할 수 있습니다.
- 서버 독립성: WSGI를 사용하면 Gunicorn, uWSGI, Nginx와 같은 다양한 서버에서 동일한 애플리케이션을 실행할 수 있습니다.
## WSGI의 한계
WSGI는 동기적 요청 처리를 기반으로 하고 있습니다. 이는 실시간 웹 소켓 통신이나 장기 연결(long-polling)과 같은 비동기 작업에 적합하지 않다는 한계가 있습니다. 이러한 문제를 해결하기 위해서는 ASGI(Asynchronous Server Gateway Interface)와 같은 대안이 등장하게 되었습니다.
Django 기반 WSGI 예제
Django는 기본적으로 WSGI를 사용하여 웹 애플리케이션을 실행할 수 있습니다. Django 프로젝트는 기본적으로 WSGI 인터페이스를 제공하며, 이를 통해 다양한 WSGI 서버에서 실행할 수 있습니다.
다음은 Django 기반 WSGI 웹 서버를 구성하는 방법을 단계별로 설명하고, 이를 위한 기본 코드를 제공하겠습니다.
1. Django 프로젝트 생성
우선, Django 프로젝트를 생성해야 합니다. 이를 위해 Python과 Django가 설치되어 있어야 합니다.
필수 패키지 설치
pip install django
Django 프로젝트 생성
django-admin startproject myproject
이 명령어는 myproject
라는 Django 프로젝트 디렉터리를 생성합니다. 이 디렉터리 안에 myproject
폴더와 여러 파일이 생성됩니다.
2. Django 기본 설정
프로젝트 폴더 안에 있는 settings.py
파일에서 기본 설정을 할 수 있습니다. 예를 들어, 데이터베이스 설정이나 미들웨어 설정 등이 여기에 포함됩니다.
기본 설정 예시 (중요 부분만 발췌)
# myproject/settings.py
# 웹 애플리케이션의 타임존 설정
TIME_ZONE = 'UTC'
# DEBUG 모드: 개발 중에는 True로 설정하고 배포할 때는 False로 설정
DEBUG = True
# 애플리케이션에서 사용할 허용된 호스트
ALLOWED_HOSTS = ['*'] # '*'로 설정하면 모든 호스트를 허용합니다.
3. WSGI 파일 설정
Django는 기본적으로 WSGI 설정 파일을 제공합니다. myproject/wsgi.py
파일을 수정하여 WSGI 서버가 Django 애플리케이션을 어떻게 실행할지 정의할 수 있습니다.
기본 WSGI 파일 (자동 생성됨)
# myproject/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
# Django의 설정 파일을 환경 변수로 설정
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# WSGI 애플리케이션 객체 생성
application = get_wsgi_application()
이 파일은 Django의 WSGI 애플리케이션을 정의하며, 웹 서버(Gunicorn, uWSGI 등)에서 application
객체를 사용하여 요청을 처리합니다.
4. Django 앱 생성
프로젝트 내에 하나의 간단한 Django 앱을 생성하고, 이를 통해 WSGI 서버에서 실행될 페이지를 구성할 수 있습니다.
python manage.py startapp myapp
views.py
에 간단한 페이지 정의
# myapp/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, WSGI World!")
URL 패턴 설정
앱의 urls.py
파일에 URL 패턴을 설정합니다.
# myproject/urls.py
from django.contrib import admin
from django.urls import path
from myapp import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index, name='index'), # 기본 페이지
]
5. WSGI 서버 실행
Django 프로젝트는 자체적으로 개발 서버를 제공하지만, 실제 WSGI 서버를 사용하여 애플리케이션을 실행하려면 Gunicorn이나 uWSGI와 같은 서버를 사용해야 합니다.
Gunicorn 설치 및 실행
먼저 Gunicorn을 설치합니다.
pip install gunicorn
이제 Gunicorn을 사용하여 WSGI 애플리케이션을 실행할 수 있습니다.
gunicorn myproject.wsgi:application
이 명령은 myproject/wsgi.py
파일의 application
객체를 Gunicorn이 실행하게 합니다.
6. 결과 확인
웹 브라우저에서 http://localhost:8000/
에 접속하면 다음과 같은 결과가 출력됩니다.
전체 디렉토리 구조
프로젝트 디렉토리는 다음과 같은 구조를 가집니다.
myproject/
│
├── manage.py
├── myproject/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
│
└── myapp/
├── __init__.py
├── views.py
└── ...
ASGI (Asynchronous Server Gateway Interface)
- ASGI는 비동기 및 동기 처리를 모두 지원하는 확장된 표준 인터페이스입니다. ASGI는 WSGI의 한계를 극복하기 위해 설계되었으며, 특히 비동기 작업이 필요한 환경에서 효율적입니다.
- ASGI는 웹 소켓, 비동기 요청, 실시간 통신 등과 같은 현대적인 웹 애플리케이션의 요구 사항을 충족하기 위해 만들어졌습니다.
- 비동기 처리: ASGI는 비동기 방식으로 요청을 처리할 수 있어, 동시에 여러 요청을 처리하거나 I/O 작업을 효율적으로 수행할 수 있습니다. 이를 통해 높은 동시성을 요구하는 애플리케이션(예: 채팅 애플리케이션, 실시간 데이터 스트리밍 등)에서 성능을 크게 향상시킬 수 있습니다.
- Django의 ASGI 지원: Django는 기본적으로 WSGI 기반이었으나, Django 3.0부터 ASGI를 공식적으로 지원하게 되었습니다. 이를 통해 비동기 웹 애플리케이션을 구현할 수 있으며, 실시간 기능을 처리하는 애플리케이션에서도 Django를 사용할 수 있게 되었습니다.
- Flask의 비동기 처리: Flask는 본래 WSGI 기반이지만, 최근에는 비동기 작업을 처리할 수 있는 기능이 일부 추가되었습니다. 다만, Flask는 기본적으로 동기적인 웹 프레임워크로 설계되었기 때문에, 복잡한 비동기 처리를 하려면 ASGI 서버와 함께 다른 비동기 라이브러리를 사용하는 것이 일반적입니다.
Django 기반 ASGI 예제
Django에서 ASGI(Asynchronous Server Gateway Interface)
를 이용하여 웹소켓 API를 제공하는 웹 서버를 구축하려면, Django 기본 설정에 추가적으로 Django Channels를 사용해야 합니다. Django Channels는 Django에 비동기 기능을 추가하여 웹소켓과 같은 실시간 통신 기능을 제공할 수 있게 해줍니다.
다음은 ASGI 기반 웹소켓 API를 제공하는 웹 서버를 구축하는 방법을 단계별로 설명하겠습니다.
1. 필수 패키지 설치
웹소켓을 지원하기 위해 Django 프로젝트에 필요한 패키지를 설치합니다.
pip install django channels
channels
패키지는 Django에 ASGI를 추가하여 비동기 작업과 웹소켓 지원을 가능하게 해줍니다.
2. Django 프로젝트 생성
Django 프로젝트를 생성하고 기본 설정을 구성합니다.
django-admin startproject myproject
3. ASGI 설정 파일 구성
Django 프로젝트가 ASGI를 사용할 수 있도록 기본 ASGI 설정을 추가해야 합니다. myproject/asgi.py
파일을 수정하여 ASGI 설정을 구성합니다.
asgi.py
설정
# myproject/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import myapp.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# ASGI 설정: HTTP와 WebSocket을 처리할 수 있도록 라우팅
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
myapp.routing.websocket_urlpatterns
)
),
})
여기서는 ProtocolTypeRouter
를 사용하여 HTTP와 웹소켓을 처리할 수 있게 라우팅합니다. 웹소켓 연결을 처리하는 부분은 AuthMiddlewareStack
을 통해 인증 정보를 관리하고, URLRouter
를 통해 웹소켓 URL 패턴을 설정합니다.
4. settings.py
에 Channels 설정 추가
settings.py
파일에 다음과 같은 설정을 추가하여 Channels를 활성화합니다.
# myproject/settings.py
# Channels를 위한 ASGI 설정
ASGI_APPLICATION = 'myproject.asgi.application'
# Redis 등 비동기 작업을 위한 채널 레이어 설정 (옵션)
# 여기서는 In-Memory 채널 레이어를 사용
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
},
}
ASGI_APPLICATION
은 프로젝트에서 ASGI를 사용할 수 있도록 지정하고, CHANNEL_LAYERS
는 비동기 작업을 위한 채널 설정입니다. In-Memory 채널 레이어는 간단한 테스트 환경에서 사용하기에 적합하며, 실제 배포에서는 Redis와 같은 백엔드를 사용하는 것이 좋습니다.
5. 웹소켓 라우팅 구성
이제 웹소켓에 대한 라우팅을 설정해야 합니다. 이를 위해 Django 앱을 생성하고, 해당 앱에서 웹소켓에 대한 경로를 설정합니다.
python manage.py startapp myapp
웹소켓 URL 라우팅
myapp/routing.py
파일을 생성하여 웹소켓에 대한 URL 라우팅을 설정합니다.
# myapp/routing.py
from django.urls import path
from . import consumers
websocket_urlpatterns = [
path('ws/chat/', consumers.ChatConsumer.as_asgi()), # 웹소켓 경로
]
이 파일에서는 /ws/chat/
경로로 들어오는 웹소켓 연결을 ChatConsumer
에서 처리하도록 라우팅합니다.
6. 웹소켓 Consumer 구현
Consumer
는 Django Channels에서 비동기적으로 웹소켓 이벤트를 처리하는 역할을 합니다. myapp/consumers.py
파일을 생성하여 웹소켓 메시지를 처리하는 클래스를 정의합니다.
# myapp/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
# 웹소켓 연결이 수립될 때 호출됨
await self.accept()
async def disconnect(self, close_code):
# 웹소켓 연결이 종료될 때 호출됨
pass
async def receive(self, text_data):
# 클라이언트로부터 메시지를 받을 때 호출됨
text_data_json = json.loads(text_data)
message = text_data_json['message']
# 받은 메시지를 다시 클라이언트로 전송
await self.send(text_data=json.dumps({
'message': message
}))
위 코드는 간단한 ChatConsumer를 구현한 예입니다. 클라이언트가 연결되면 connect()
메서드가 호출되어 연결이 수락되고, 클라이언트로부터 메시지가 수신되면 receive()
메서드가 실행되어 받은 메시지를 다시 클라이언트로 돌려줍니다.
7. Django URL 설정
urls.py
에서 기본 URL 설정을 수정하여 웹소켓 API에 연결할 수 있도록 만듭니다.
# myproject/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
웹소켓에 대한 URL 라우팅은 routing.py
에서 설정했으므로 여기에서는 기본 URL 라우팅만 정의하면 됩니다.
8. ASGI 서버 실행
Django Channels를 사용하여 ASGI 서버를 실행하려면 Daphne 또는 Uvicorn과 같은 ASGI 서버를 사용해야 합니다. 여기서는 Uvicorn을 사용해 실행합니다.
Uvicorn 설치 및 실행
pip install uvicorn
uvicorn myproject.asgi:application --reload
이 명령은 myproject/asgi.py
에 정의된 ASGI 애플리케이션을 Uvicorn으로 실행합니다.
9. 결과 확인
웹소켓 클라이언트를 사용하여 서버에 연결하고, /ws/chat/
경로로 메시지를 전송하고 응답을 받을 수 있습니다.
웹소켓 클라이언트 예시 (JavaScript)
let socket = new WebSocket('ws://localhost:8000/ws/chat/');
socket.onopen = function(e) {
console.log("[open] 웹소켓 연결이 수립되었습니다.");
socket.send(JSON.stringify({ message: "Hello, WebSocket!" }));
};
socket.onmessage = function(event) {
let receivedMessage = JSON.parse(event.data).message;
console.log(`[message] 서버로부터 메시지 수신: ${receivedMessage}`);
};
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`[close] 연결이 종료되었습니다.`);
} else {
console.log('[close] 연결이 비정상적으로 종료되었습니다.');
}
};
socket.onerror = function(error) {
console.log(`[error] ${error.message}`);
};
10. 전체 디렉토리 구조
프로젝트 디렉토리는 다음과 같습니다:
myproject/
│
├── manage.py
├── myproject/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│
└── myapp/
├── __init__.py
├── consumers.py
├── routing.py
WSGI/ASGI를 함께 쓰는 이유
- 웹 서버와 애플리케이션 간의 통신 표준화:
- WSGI와 ASGI는 웹 서버(예: Nginx, Apache)와 Python 웹 애플리케이션(Django, Flask 등) 사이에서 통신 방식을 표준화합니다. 이를 통해 다양한 웹 서버와 Python 애플리케이션이 호환될 수 있습니다.
- 성능과 확장성:
- WSGI는 주로 동기 처리에 최적화되어 있으며, 단순한 웹 애플리케이션에서는 충분한 성능을 발휘합니다. 하지만 동시성이 중요한 애플리케이션에서는 성능이 제한됩니다.
- ASGI는 비동기 처리와 동기 처리를 모두 지원하며, 특히 실시간 통신이나 웹소켓과 같은 동시성을 요구하는 애플리케이션에서 성능을 크게 향상시킬 수 있습니다.
- 동기와 비동기 처리 요구사항:
- 전통적인 웹 애플리케이션은 주로 동기적으로 동작했지만, 최근에는 실시간 처리가 필요한 웹 애플리케이션이 늘어남에 따라 비동기 처리의 중요성이 커졌습니다. ASGI는 이러한 요구를 충족하기 위해 도입된 표준입니다.
- 유연성:
- ASGI는 동시성, 웹소켓, HTTP/2와 같은 최신 웹 기술을 지원하기 때문에, 더 복잡하고 다양한 애플리케이션 아키텍처를 설계할 수 있는 유연성을 제공합니다.
결론
Python 웹 프레임워크인 Django와 Flask에서 ASGI나 WSGI를 사용하는 이유는 웹 서버와 애플리케이션 간의 표준화된 통신 방식을 제공하고, 성능과 동시성 요구 사항을 충족시키기 위함입니다. WSGI는 동기 처리에 적합한 표준이며, ASGI는 비동기 및 동기 처리를 모두 지원하여, 현대적인 웹 애플리케이션의 요구 사항을 더 잘 충족시킬 수 있습니다.