← 블로그 홈

Django Ninja 백엔드 서버의 한계와 실제 경험

Django Ninja는 FastAPI에서 영감을 받아 Django에 현대적인 API 개발 경험을 제공하는 프레임워크입니다. 하지만 실제 프로덕션 환경에서 사용하다 보면 여러 한계점들을 발견하게 됩니다. 이 글에서는 Django Ninja의 주요 한계점들과 실제 경험을...

Django Ninja는 FastAPI에서 영감을 받아 Django에 현대적인 API 개발 경험을 제공하는 프레임워크입니다. 하지만 실제 프로덕션 환경에서 사용하다 보면 여러 한계점들을 발견하게 됩니다. 이 글에서는 Django Ninja의 주요 한계점들과 실제 경험을 바탕으로 한 해결책을 살펴보겠습니다.

🎯 Django Ninja란?

Django Ninja는 Django를 기반으로 한 고성능 웹 API 프레임워크로, FastAPI의 장점을 Django 생태계에 도입한 것이 특징입니다.

주요 특징:

  • 자동 API 문서 생성 (OpenAPI/Swagger)
  • 타입 힌트 기반 데이터 검증
  • 높은 성능 (FastAPI 대비 비슷한 성능)
  • Django ORM과의 완벽한 호환성

⚠️ Django Ninja의 주요 한계점

1. 생태계 성숙도 부족

문제점:

# Django Ninja - 한정된 플러그인과 확장성
from ninja import NinjaAPI
from ninja.security import HttpBearer

api = NinjaAPI()

# 써드파티 플러그인이 부족함
# 많은 기능을 직접 구현해야 함

FastAPI와 비교:

# FastAPI - 풍부한 생태계
from fastapi import FastAPI, Depends
from fastapi.security import HTTPBearer
from fastapi_users import FastAPIUsers  # 풍부한 플러그인
from fastapi_cache import FastAPICache   # 다양한 확장

app = FastAPI()

실제 경험:

  • 인증/인가 시스템을 거의 처음부터 구축해야 함
  • 캐싱, 레이트 리미팅 등 미들웨어가 부족
  • 커뮤니티 솔루션이 제한적

2. Django의 무거운 기본 구조

문제점:

# Django Ninja는 여전히 Django의 무거운 구조를 사용
INSTALLED_APPS = [
    'django.contrib.admin',      # API만 사용할 때는 불필요
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',   # Stateless API에서는 불필요
    'django.contrib.messages',   # API에서는 사용하지 않음
    'django.contrib.staticfiles',
    'your_api_app',
]

# 메모리 사용량이 순수 FastAPI보다 높음

성능 비교 (실제 측정):

# 메모리 사용량 비교 (유사한 기능의 API)
FastAPI: ~45MB
Django Ninja: ~85MB
Django REST Framework: ~95MB

3. 비동기 처리의 한계

문제점:

# Django Ninja - 제한적인 비동기 지원
from ninja import NinjaAPI
from asgiref.sync import sync_to_async

api = NinjaAPI()

@api.post("/upload")
async def upload_file(request, file: UploadedFile):
    # Django ORM은 여전히 동기적
    # 복잡한 sync_to_async 래핑이 필요
    user = await sync_to_async(User.objects.get)(id=1)
    
    # 파일 처리도 동기적 코드가 많음
    return {"status": "uploaded"}

FastAPI 비교:

# FastAPI - 네이티브 비동기 지원
from fastapi import FastAPI, UploadFile
import aiofiles

app = FastAPI()

@app.post("/upload")
async def upload_file(file: UploadFile):
    # 네이티브 비동기 처리
    async with aiofiles.open(f"uploads/{file.filename}", "wb") as f:
        content = await file.read()
        await f.write(content)
    
    return {"status": "uploaded"}

4. 테스팅 복잡성

문제점:

# Django Ninja 테스트 - Django의 복잡한 테스트 구조
from django.test import TestCase
from ninja.testing import TestClient

class APITestCase(TestCase):
    def setUp(self):
        # Django의 무거운 테스트 셋업
        self.client = TestClient(api)
        # 데이터베이스 마이그레이션 필요
        # 테스트 데이터베이스 생성
    
    def test_api_endpoint(self):
        response = self.client.post("/api/test", json={"data": "test"})
        self.assertEqual(response.status_code, 200)

FastAPI 테스트:

# FastAPI - 간단하고 빠른 테스트
from fastapi.testclient import TestClient
import pytest

client = TestClient(app)

def test_api_endpoint():
    response = client.post("/test", json={"data": "test"})
    assert response.status_code == 200

5. 배포와 스케일링의 복잡성

문제점:

# Django Ninja - Django 특유의 배포 복잡성

# settings.py에서 복잡한 설정 관리
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        # ... 복잡한 설정들
    }
}

# 정적 파일 처리 (API에는 불필요하지만 설정 필요)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

# 미들웨어 스택이 무거움
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

🔧 실제 해결 방안과 대안

1. 하이브리드 접근법

Django Ninja + FastAPI 조합:

# 복잡한 비즈니스 로직: Django Ninja
# 고성능이 필요한 부분: FastAPI

# Django Ninja (main_api.py)
from ninja import NinjaAPI
from django.contrib.auth.models import User

django_api = NinjaAPI(urls_namespace="django")

@django_api.post("/users")
def create_user(request, data: UserSchema):
    # Django ORM의 강력한 기능 활용
    user = User.objects.create_user(**data.dict())
    return {"id": user.id}

# FastAPI (fast_api.py)  
from fastapi import FastAPI

fast_api = FastAPI()

@fast_api.post("/process-data")
async def process_large_data(data: dict):
    # 고성능 비동기 처리
    result = await heavy_computation(data)
    return result

2. 점진적 마이그레이션 전략

단계별 접근:

# 1단계: Django Ninja로 빠른 프로토타이핑
from ninja import NinjaAPI

api = NinjaAPI()

@api.get("/users/{user_id}")
def get_user(request, user_id: int):
    # 빠른 개발과 Django 생태계 활용
    pass

# 2단계: 성능이 중요한 부분을 FastAPI로 이전
from fastapi import FastAPI

performance_api = FastAPI()

@performance_api.get("/analytics/realtime")
async def realtime_analytics():
    # 고성능이 필요한 부분만 FastAPI 사용
    pass

3. 최적화된 Django Ninja 구성

경량화 설정:

# 최소한의 Django 설정
INSTALLED_APPS = [
    'django.contrib.contenttypes',
    'django.contrib.auth',
    'your_api_app',  # 필요한 앱만 포함
]

# 불필요한 미들웨어 제거
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
]

# API 전용 설정
USE_TZ = True
APPEND_SLASH = False  # API에서는 불필요
ROOT_URLCONF = 'api.urls'  # API 전용 URL 구성

📊 성능 비교 및 선택 가이드

실제 벤치마크 결과

# 동일한 CRUD API 구현 성능 비교 (1000 requests, 10 concurrent)

FastAPI:
- Requests per second: 2,847.62
- Time per request: 3.511ms
- Memory usage: 45MB

Django Ninja:
- Requests per second: 1,923.44
- Time per request: 5.200ms  
- Memory usage: 85MB

Django REST Framework:
- Requests per second: 1,445.23
- Time per request: 6.921ms
- Memory usage: 95MB

언제 Django Ninja를 선택해야 할까?

✅ Django Ninja가 적합한 경우:

  • 기존 Django 프로젝트에 API 추가
  • Django ORM과 생태계를 적극 활용하는 경우
  • 팀이 Django에 익숙한 경우
  • 복잡한 비즈니스 로직이 많은 경우

❌ Django Ninja가 부적합한 경우:

  • 마이크로서비스 아키텍처
  • 극도의 성능이 요구되는 경우
  • 실시간 처리가 많은 API
  • 순수 API 서버 (웹 프론트엔드 없음)

🚀 결론과 권장사항

Django Ninja는 Django 개발자들에게 현대적인 API 개발 경험을 제공하지만, 여전히 Django의 구조적 한계를 벗어나지 못합니다.

권장 전략:

  1. 프로토타이핑: Django Ninja로 빠른 개발
  2. 성능 최적화: 병목 지점을 FastAPI로 분리
  3. 점진적 개선: 필요에 따라 서비스별로 기술 스택 분리

최종 선택 기준:

  • 팀의 기술 스택 친숙도
  • 성능 요구사항
  • 기존 시스템과의 호환성
  • 장기적인 확장성 계획

Django Ninja는 Django 생태계 내에서는 훌륭한 선택이지만, 성능과 확장성이 중요하다면 FastAPI나 다른 대안을 고려하는 것이 좋습니다.


참고 자료:

이 글을 공유해보세요!