웹 애플리케이션 개발에서 빠르고 효율적인 서버는 필수적입니다. 파이썬 생태계에서 Uvicorn은 ASGI(Asynchronous Server Gateway Interface) 서버로서 비동기 웹 애플리케이션을 위한 강력한 솔루션을 제공합니다. 이 글에서는 Uvicorn의 설치부터 사용법, ASGI 인터페이스와의 관계, 그리고 실제 MCP(Model Context Protocol) 서버 구축 사례까지 상세히 알아보겠습니다.
Uvicorn이란?
Uvicorn은 파이썬으로 작성된 초고속 ASGI 서버 구현체입니다. 비동기 프레임워크를 지원하도록 설계되었으며, uvloop와 httptools를 기반으로 하여 놀라운 성능을 제공합니다. Uvicorn이라는 이름은 ‘Unicorn Velociraptor’에서 유래되었으며, 이는 속도와 강력함을 상징합니다.

Uvicorn 설치 방법
Uvicorn은 pip를 통해 쉽게 설치할 수 있습니다. 기본 설치와 추가 의존성을 포함한 설치 방법은 다음과 같습니다:
1. 기본 설치
pip install uvicorn
2. 표준 의존성 포함 설치 (권장)
pip install uvicorn[standard]
표준 설치는 uvloop(리눅스에서 성능 향상), httptools(HTTP 파싱 성능 향상), websockets(WebSocket 지원) 등의 추가 의존성을 포함합니다.
Uvicorn 사용법
Uvicorn은 명령줄 인터페이스(CLI)와 프로그래밍 방식 두 가지로 사용할 수 있습니다.
1. 명령줄에서 실행
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
여기서 `main:app`은 main.py 파일의 app 객체를 의미합니다. 주요 옵션은 다음과 같습니다:
- –host: 바인딩할 호스트 (기본값: 127.0.0.1)
- –port: 사용할 포트 (기본값: 8000)
- –reload: 코드 변경 시 자동 재시작 (개발 환경에서 유용)
- –workers: 워커 프로세스 수
- –log-level: 로그 레벨 설정 (critical, error, warning, info, debug)
상세 옵션
Usage: uvicorn [OPTIONS] APP
Options:
--host TEXT Bind socket to this host. [default:
127.0.0.1]
--port INTEGER Bind socket to this port. If 0, an available
port will be picked. [default: 8000]
--uds TEXT Bind to a UNIX domain socket.
--fd INTEGER Bind to socket from this file descriptor.
--reload Enable auto-reload.
--reload-dir PATH Set reload directories explicitly, instead
of using the current working directory.
--reload-include TEXT Set glob patterns to include while watching
for files. Includes '*.py' by default; these
defaults can be overridden with `--reload-
exclude`. This option has no effect unless
watchfiles is installed.
--reload-exclude TEXT Set glob patterns to exclude while watching
for files. Includes '.*, .py[cod], .sw.*,
~*' by default; these defaults can be
overridden with `--reload-include`. This
option has no effect unless watchfiles is
installed.
--reload-delay FLOAT Delay between previous and next check if
application needs to be. Defaults to 0.25s.
[default: 0.25]
--workers INTEGER Number of worker processes. Defaults to the
$WEB_CONCURRENCY environment variable if
available, or 1. Not valid with --reload.
--loop [auto|asyncio|uvloop] Event loop implementation. [default: auto]
--http [auto|h11|httptools] HTTP protocol implementation. [default:
auto]
--ws [auto|none|websockets|wsproto]
WebSocket protocol implementation.
[default: auto]
--ws-max-size INTEGER WebSocket max size message in bytes
[default: 16777216]
--ws-max-queue INTEGER The maximum length of the WebSocket message
queue. [default: 32]
--ws-ping-interval FLOAT WebSocket ping interval in seconds.
[default: 20.0]
--ws-ping-timeout FLOAT WebSocket ping timeout in seconds.
[default: 20.0]
--ws-per-message-deflate BOOLEAN
WebSocket per-message-deflate compression
[default: True]
--lifespan [auto|on|off] Lifespan implementation. [default: auto]
--interface [auto|asgi3|asgi2|wsgi]
Select ASGI3, ASGI2, or WSGI as the
application interface. [default: auto]
--env-file PATH Environment configuration file.
--log-config PATH Logging configuration file. Supported
formats: .ini, .json, .yaml.
--log-level [critical|error|warning|info|debug|trace]
Log level. [default: info]
--access-log / --no-access-log Enable/Disable access log.
--use-colors / --no-use-colors Enable/Disable colorized logging.
--proxy-headers / --no-proxy-headers
Enable/Disable X-Forwarded-Proto,
X-Forwarded-For, X-Forwarded-Port to
populate remote address info.
--server-header / --no-server-header
Enable/Disable default Server header.
--date-header / --no-date-header
Enable/Disable default Date header.
--forwarded-allow-ips TEXT Comma separated list of IP Addresses, IP
Networks, or literals (e.g. UNIX Socket
path) to trust with proxy headers. Defaults
to the $FORWARDED_ALLOW_IPS environment
variable if available, or '127.0.0.1'. The
literal '*' means trust everything.
--root-path TEXT Set the ASGI 'root_path' for applications
submounted below a given URL path.
--limit-concurrency INTEGER Maximum number of concurrent connections or
tasks to allow, before issuing HTTP 503
responses.
--backlog INTEGER Maximum number of connections to hold in
backlog
--limit-max-requests INTEGER Maximum number of requests to service before
terminating the process.
--timeout-keep-alive INTEGER Close Keep-Alive connections if no new data
is received within this timeout. [default:
5]
--timeout-graceful-shutdown INTEGER
Maximum number of seconds to wait for
graceful shutdown.
--ssl-keyfile TEXT SSL key file
--ssl-certfile TEXT SSL certificate file
--ssl-keyfile-password TEXT SSL keyfile password
--ssl-version INTEGER SSL version to use (see stdlib ssl module's)
[default: 17]
--ssl-cert-reqs INTEGER Whether client certificate is required (see
stdlib ssl module's) [default: 0]
--ssl-ca-certs TEXT CA certificates file
--ssl-ciphers TEXT Ciphers to use (see stdlib ssl module's)
[default: TLSv1]
--header TEXT Specify custom default HTTP response headers
as a Name:Value pair
--version Display the uvicorn version and exit.
--app-dir TEXT Look for APP in the specified directory, by
adding this to the PYTHONPATH. Defaults to
the current working directory. [default:
""]
--h11-max-incomplete-event-size INTEGER
For h11, the maximum number of bytes to
buffer of an incomplete event.
--factory Treat APP as an application factory, i.e. a
() -> <ASGI app> callable.
--help Show this message and exit.
2. 프로그래밍 방식으로 실행
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
log_level="info"
)
간단한 ASGI 애플리케이션 예제
# main.py
async def app(scope, receive, send):
assert scope['type'] == 'http'
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
],
})
await send({
'type': 'http.response.body',
'body': b'Hello, World!',
})
ASGI 인터페이스 이해하기
ASGI(Asynchronous Server Gateway Interface)는 WSGI(Web Server Gateway Interface)의 비동기 버전으로, 파이썬 웹 서버와 애플리케이션 간의 표준 인터페이스를 정의합니다.
ASGI의 주요 특징:
- 비동기 처리 지원 (async/await)
- WebSocket과 같은 장기 연결 지원
- HTTP/2 및 기타 프로토콜 지원
- 동시성 처리 향상
ASGI 애플리케이션은 다음 세 가지 매개변수를 받는 호출 가능한 객체입니다:
- scope: 요청 정보를 담은 딕셔너리
- receive: 이벤트를 수신하는 비동기 함수
- send: 이벤트를 전송하는 비동기 함수
Uvicorn과 관련 프레임워크
Uvicorn은 다양한 ASGI 호환 프레임워크와 함께 사용할 수 있습니다:
1. FastAPI
FastAPI는 현대적이고 빠른 웹 프레임워크로, Uvicorn과 함께 사용할 때 최고의 성능을 발휘합니다.
# fastapi_example.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
# 실행: uvicorn fastapi_example:app --reload
2. Starlette
Starlette는 가볍고 유연한 ASGI 프레임워크로, FastAPI의 기반이 되는 라이브러리입니다.
# starlette_example.py
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def homepage(request):
return JSONResponse({"message": "Hello World"})
routes = [
Route("/", endpoint=homepage)
]
app = Starlette(routes=routes)
# 실행: uvicorn starlette_example:app --reload
3. Django
Django 3.0부터 ASGI를 지원하므로 Uvicorn으로 실행할 수 있습니다.
# Django 프로젝트의 asgi.py 파일
# 실행: uvicorn myproject.asgi:application
4. Quart
Flask와 유사한 API를 가진 비동기 웹 프레임워크입니다.
# quart_example.py
from quart import Quart
app = Quart(__name__)
@app.route("/")
async def hello():
return {"hello": "world"}
# 실행: uvicorn quart_example:app --reload
MCP(Model Context Protocol) 서버 구축 사례
MCP 서버는 여러 채널(HTTP, WebSocket, 메시징 큐 등)을 통해 데이터를 처리하는 서버를 의미합니다. Uvicorn은 이러한 MCP 서버 구축에 이상적인 선택입니다.
실시간 데이터 처리 MCP 서버 예제
# mcp_server.py
from fastapi import FastAPI, WebSocket, BackgroundTasks
from fastapi.responses import JSONResponse
import asyncio
import aioredis
import json
app = FastAPI()
# 연결된 WebSocket 클라이언트 저장
connected_clients = []
# Redis 연결 설정
redis = None
@app.on_event("startup")
async def startup_event():
global redis
redis = await aioredis.create_redis_pool("redis://localhost")
# 백그라운드 작업 시작
asyncio.create_task(process_messages())
@app.on_event("shutdown")
async def shutdown_event():
# Redis 연결 종료
if redis is not None:
redis.close()
await redis.wait_closed()
# WebSocket 연결 처리
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
connected_clients.append(websocket)
try:
while True:
# 클라이언트로부터 메시지 수신
data = await websocket.receive_text()
# 메시지 처리 및 Redis에 저장
await redis.publish("messages", data)
except Exception as e:
print(f"WebSocket error: {e}")
finally:
connected_clients.remove(websocket)
# HTTP API 엔드포인트
@app.post("/api/message")
async def post_message(message: dict, background_tasks: BackgroundTasks):
# 메시지를 Redis에 발행
background_tasks.add_task(publish_message, json.dumps(message))
return JSONResponse({"status": "Message sent"})
async def publish_message(message: str):
await redis.publish("messages", message)
# Redis 구독 및 메시지 처리
async def process_messages():
channel = (await redis.subscribe("messages"))[0]
while await channel.wait_message():
message = await channel.get()
# 모든 WebSocket 클라이언트에 메시지 브로드캐스트
for client in connected_clients:
try:
await client.send_text(message.decode())
except Exception as e:
print(f"Error sending to client: {e}")
# 실행: uvicorn mcp_server:app --host 0.0.0.0 --port 8000 --workers 4
이 예제는 다음과 같은 MCP 서버의 핵심 기능을 보여줍니다:
- HTTP API를 통한 메시지 수신
- WebSocket을 통한 실시간 양방향 통신
- Redis를 사용한 메시지 큐 및 발행-구독 패턴
- 비동기 백그라운드 작업 처리
- 여러 클라이언트에 메시지 브로드캐스팅
Uvicorn의 성능 최적화 팁
실제 프로덕션 환경에서 Uvicorn의 성능을 최대화하기 위한 팁:
- 워커 수 최적화: CPU 코어 수 * 2 + 1 공식을 시작점으로 사용
- Gunicorn과 함께 사용: 프로세스 관리를 위해 Gunicorn의 워커 클래스로 Uvicorn 사용
- uvloop 활성화: Linux 환경에서 성능 향상을 위해 uvloop 사용
- 로그 레벨 조정: 프로덕션 환경에서는 warning 또는 error 레벨 사용
- 프록시 서버 사용: Nginx나 Traefik과 같은 프록시 서버 뒤에서 Uvicorn 실행
Gunicorn과 함께 Uvicorn 사용하기
# 설치
pip install gunicorn uvicorn
# 실행
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
결론
Uvicorn은 현대적인 파이썬 웹 애플리케이션을 위한 강력하고 빠른 ASGI 서버입니다. FastAPI, Starlette와 같은 비동기 프레임워크와 함께 사용할 때 최고의 성능을 발휘하며, MCP 서버와 같은 복잡한 시스템 구축에도 이상적입니다. 비동기 프로그래밍의 장점을 최대한 활용하고 싶다면, Uvicorn은 필수적인 도구가 될 것입니다.
파이썬 비동기 웹서버에서 Uvicorn은 중요한 서버 도구입니다. 이 글이 여러분의 ASGI 서버 구축에 도움이 되길 바랍니다!
답글 남기기