개발 삽질 일지

[Linux] (번외) FastAPI + Django + Uvicorn 본문

Linux 학습 로드맵/3단계: 고급 - 우당탕탕 서비스 운영

[Linux] (번외) FastAPI + Django + Uvicorn

그낙이 2025. 5. 7. 23:42
반응형

지난 글에서 Django를 Uvicorn을 통한 ASGI 환경으로 배포하는 과정을 정리했습니다. Django에서도 uvicorn project.asgi:application 명령으로 서버를 띄울 수 있었고, systemd 설정까지 마쳐 서버 재부팅 시 자동 실행되는 구조까지 구성했습니다. 하지만 비동기 코드를 사용하게 되면 오류가 발생했고, 직접 삽질해보니 Django 만으로는 완전한 비동기 처리가 불가능에 가깝다는 결론을 내렸습니다. 비동기 처리를 하기 위해서 결국에 외부 프레임워크나 라이브러리를 사용해야 했습니다. 

 

그래서 저는 Django에서 비동기를 억지로 다루기보다는, FastAPI로 점진적으로 리그레이션하는 방향을 선택했습니다. 다만 FastAPI는 Django처럼 템플릿을 제공하지 않고, 오직 API 중심으로 설계된 프레임워크이기 때문에, React나 Vue 같은 프론트엔드 프레임워크와의 연동이 전제되어야 했습니다. 이에 따라 저는 전체를 한 번에 전환하기보다는, 비동기 처리가 꼭 필요한 부분부터 점진적으로 FastAPI로 분리하는 전략을 택했습니다.

 

이번 글에서는 Django에서 FastAPI로 API를 호출하고, 서버에 배포하는 간단한 과정을 정리해보려 합니다. 

 

폴더 구조 

some_async_project/
│
├── django_backend/
│   ├── manage.py
│   ├── myproject/
│   │   ├── settings.py
│   │   ├── urls.py
│   │   ├── wsgi.py
│   │   └── asgi.py
│   └── django_apps/
│       └── (your Django apps...)
│
├── fastapi_service/
│   ├── main.py                          # FastAPI 진입점
│   ├── database.py                      # DB 연결 설정
│   ├── requirements.txt
│
│   ├── crud/
│   │   ├── __init__.py
│   │   └── example_crud.py             # DB 접근 로직
│
│   ├── models/
│   │   ├── __init__.py
│   │   └── example_model.py            # SQLAlchemy 모델
│
│   ├── routers/
│   │   ├── __init__.py
│   │   └── example_router.py           # 라우터 정의 (엔드포인트)
│
│   ├── schemas/
│   │   ├── __init__.py
│   │   └── example_schema.py           # Pydantic 모델
│
├── .env                                 # 공통 환경 설정
└── README.md

 

Django와 FastAPI를 둘 다 사용하고 있기 때문에(언젠가는 다 FastAPI로 전환 할 수도 있겠지만), 위와 같은 폴더 구조를 채택하게 되었습니다. 이제부터 Django에서 FastAPI에 API를 호출해 데이터를 받아오고, 이를 DB에 저장하는 과정을 구현한 뒤, 서버에 배포까지 진행해봅시다.

 

우선 FastAPI의 진입점인 main.py입니다. main.py는 FastAPI 서버의 출발점입니다. 앱을 만들고, 미들웨어 붙이고, 라우터 붙이고 DB 연결까지 구성합니다. 

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from database import Base, engine  # SQLAlchemy 기반
from models import some_db_table
from routers import some_router  # 기존 라우터

app = FastAPI()

# CORS 설정 Django를 8000번 포트, FastAPI를 8001번 포트를 사용하기 위해 설정했습니다. 
origins = [
    "http://127.0.0.1:8000",
    "http://localhost:8000",
    "https://some_domain.com"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# DB 테이블 자동 생성 (초기 개발 시만 사용, 운영 시 Alembic 권장)
Base.metadata.create_all(bind=engine)

# 라우터 등록
app.include_router(some_router.router, prefix="/fastapi")

 

다음은 라우팅을 도와줄 routers/some_routers.py입니다.

from fastapi import APIRouter

router = APIRouter()

@router.post("/some_endpoint")
def end_point():
    return {"message": "FastAPI 연결 성공!"}

 

기존 Django JavaScript 코드입니다. 위에 main.py에서 prefix를 /fastapi로 설정해 두었기 때문에 fastapi/some_endpoint로 호출을 보내면 됩니다. 다만 서버에 배포된 경우에는 그대로  fetch("/fastapi/some_endpoint")를 사용하면 되지만, 로컬의 경우에는 8001번 포트로 직접 호출을 보내야 합니다. 이 때문에, BASE_API_URL를 로컬과 배포 환경에 차이를 두고 API 호출을 진행했습니다.  

const isLocal = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";

const BASE_API_URL = isLocal
  ? "http://localhost:8001/fastapi"     // 로컬 FastAPI
  : "https://some_domain.com/fastapi";  // 운영 서버 FastAPI
const response = await fetch(
      `${BASE_API_URL}/some_endpoint/`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ some_body: once_told_me }),
      }
    );

 

이제 저희는 Django와 FastAPI를 연동했습니다. 이제 모델을 만들고, DB에 저장해봅시다. Django와 FastAPI는 DB 연동 방식이 다르고, ORM 관리자도 다르기 때문에 새로운 모델을 작성해줍시다. Django와 동일한 DB를 사용하기 때문에 스키마(테이블 구조)는 동일하게 유지해주시면 됩니다. 아래는 간단한 스키마 예시입니다. 

from sqlalchemy import Column, Integer, String, Text
from database import Base

class SomeChatData(Base):
    __tablename__ = "some_chat_data"

    id = Column(Integer, primary_key=True, index=True)
    category_name = Column(String(255), index=True)
    message = Column(Text, nullable=True)

 

이제 DB 연동 부분입니다. 기존에 Django에서 MySQL을 사용했기 때문에 그대로 연동해주시면 됩니다. 

# database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
import socket

# EC2 / 로컬 환경 분기
hostname = socket.gethostname()
if hostname.startswith("ip-"):
    MYSQL_USER = "some_ec2_user"
    MYSQL_PASSWORD = "some_secure_password"
    MYSQL_HOST = "some-ec2-db-endpoint.rds.amazonaws.com"
else:
    MYSQL_USER = "some_local_user"
    MYSQL_PASSWORD = "some_local_password"
    MYSQL_HOST = "127.0.0.1"

MYSQL_DB = "some_database_name"
MYSQL_PORT = 3306

SQLALCHEMY_DATABASE_URL = (
    f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}?charset=utf8mb4"
)

# SQLAlchemy 엔진 및 세션 구성
engine = create_engine(SQLALCHEMY_DATABASE_URL, pool_pre_ping=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# DB 세션 의존성
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

 

마지막은 FastAPI와 DB를 연결해줄 crud입니다. 아래에 작성된 create_some_log 함수를 routers에서 호출해서 DB에 저장하시면 됩니다. 

from sqlalchemy.orm import Session
from datetime import datetime
from models import SomeLog  # 변경된 모델명에 맞춰 import

def create_some_log(db: Session, category: str, version: str, content: str) -> SomeLog:
    log = SomeLog(
        date_time=datetime.now(),
        category_name=category,
        gpt_version=version,
        history=content,
    )
    db.add(log)
    db.commit()
    db.refresh(log)
    return log

 

엄청 간단한 Django + FastAPI 연동이 끝났습니다. 이제 더 간단한 nginx 설정만 해주시면 됩니다.

server {
    listen 80;
    server_name domain.com;

    charset utf-8;
    client_max_body_size 600M;

    location /static/ {
        alias /home/ubuntu/staticfiles/;
        expires 30d;
        access_log off;
    }

    location ^~ /fastapi/ {
        proxy_pass http://127.0.0.1:8001;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 300s;
    }
}

 

/etc/nginx/sites-available/myproject 파일입니다. fastapi 부분에 8001번 포트를 달아주시면 끝입니다. 이제 서비스 유닛 파일 작성만 남았습니다. 기존에 Django 서비스 유닛은 작성되어 있으니 FastAPI만 작성해주고 서버를 실행해봅시다. 서비스 유닛 작성 시 폴더 구조만 작성해주시면 됩니다. 

 

[Linux] Uvicorn

Uvicorn이란? Uvicorn은 ASGI 서버입니다.ASGI(Asynchronous Server Gateway Interface)는 WSGI의 다음 세대로, 비동기 처리를 지원합니다. 즉, FastAPI, Django Channels, 최신 웹 프레임워크를 사용할 때는 ASGI 기반의 서버

gnaaak.tistory.com

 

sudo nano /etc/systemd/system/uvicorn-fastapi.service
[Unit]
Description=Uvicorn daemon for FastAPI service
After=network.target

[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/some_async_project/fastapi_service
Environment="PATH=/home/ubuntu/venv/bin"
ExecStart=/home/ubuntu/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8001

Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable uvicorn-fastapi
sudo systemctl start uvicorn-fastapi
sudo systemctl status uvicorn-fastapi

 

이후 Django + FastAPI 실행 명령어 전체입니다. 

sudo systemctl daemon-reload

# 부팅 시 자동 실행
sudo systemctl enable uvicorn-django
sudo systemctl enable uvicorn-fastapi

# 서비스 시작
sudo systemctl start uvicorn-django
sudo systemctl start uvicorn-fastapi

# 서비스 재시작
sudo systemctl restart uvicorn-django
sudo systemctl restart uvicorn-fastapi

# 상태 확인
sudo systemctl status uvicorn-django
sudo systemctl status uvicorn-fastapi

 

이번 글에서는 Django + FastAPI를 병행 운영하는 구조를 어떻게 구성하고, 실제 서버에서는 어떻게 설정하고 배포하는지에 대해 정리해보았습니다. 단일 프로젝트 안에서 두 프레임워크를 목적에 따라 적절히 나누고, Nginx와 systemd를 이용해 안정적인 서비스 환경을 구성하는 과정을 함께 살펴봤습니다. 

 

언제나처럼 — 시작은 삽질이지만, 끝은 지식입니다.

 

반응형

'Linux 학습 로드맵 > 3단계: 고급 - 우당탕탕 서비스 운영' 카테고리의 다른 글

[Linux] Uvicorn  (0) 2025.05.02
[Linux] Nginx  (0) 2025.05.02