본문으로 건너뛰기
Advertisement

실무 고수 팁

Django 프로덕션 환경에서 성능, 캐싱, 보안을 최적화하는 핵심 패턴입니다.


쿼리 최적화

# 1. 필요한 컬럼만 가져오기
# ❌ 모든 컬럼
products = Product.objects.all()

# ✅ 필요한 컬럼만 (values)
products = Product.objects.values("id", "name", "price")

# ✅ 필요한 컬럼만 (only / defer)
products = Product.objects.only("id", "name", "price") # 이 컬럼만 로드
products = Product.objects.defer("description", "metadata") # 이 컬럼 제외


# 2. exists() vs count() — 존재 여부 확인
# ❌ 비효율: 전체 카운트 후 비교
if Product.objects.filter(owner=user).count() > 0:
pass

# ✅ 효율: LIMIT 1 사용
if Product.objects.filter(owner=user).exists():
pass


# 3. bulk 연산 — DB 왕복 최소화
# ❌ 1000번 INSERT
for i in range(1000):
Product.objects.create(name=f"상품 {i}", price=1000 * i, owner=user)

# ✅ 1번 INSERT
products = [
Product(name=f"상품 {i}", price=1000 * i, owner=user)
for i in range(1000)
]
Product.objects.bulk_create(products, batch_size=100)

# ✅ 1번 UPDATE
Product.objects.filter(category=old_cat).bulk_update(
[Product(id=p.id, category=new_cat) for p in products],
fields=["category"],
)


# 4. iterator() — 대량 데이터 메모리 효율
# ❌ 전체 QuerySet을 메모리에 로드
for product in Product.objects.all(): # 100만 건이면 OOM 위험
process(product)

# ✅ 청크 단위 처리
for product in Product.objects.iterator(chunk_size=1000):
process(product)


# 5. 쿼리 디버깅
from django.db import connection

def query_debug(qs):
list(qs) # 평가 실행
for query in connection.queries[-5:]:
print(f"{query['time']}s: {query['sql'][:100]}")

Redis 캐싱

pip install django-redis
# settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://localhost:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
"TIMEOUT": 300, # 기본 5분
}
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
# 캐시 사용 패턴
from django.core.cache import cache
from django.views.decorators.cache import cache_page
from rest_framework.decorators import api_view
from rest_framework.response import Response


# 1. 저수준 캐시 API
def get_popular_products():
cache_key = "popular_products"
products = cache.get(cache_key)

if products is None:
products = list(
Product.objects.filter(is_active=True)
.order_by("-view_count")
.values("id", "name", "price")[:10]
)
cache.set(cache_key, products, timeout=600) # 10분

return products


# 2. cache_page 데코레이터 (뷰 전체 캐시)
@cache_page(60 * 15) # 15분
def product_list_page(request):
...


# 3. 캐시 무효화 패턴
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver


@receiver([post_save, post_delete], sender=Product)
def invalidate_product_cache(sender, instance, **kwargs):
cache.delete("popular_products")
cache.delete(f"product_{instance.id}")
cache.delete_pattern("product_list_*") # 패턴 삭제


# 4. 캐시 계층화
def get_product_detail(product_id: int) -> dict:
# L1: 로컬 메모리 (가장 빠름)
from django.core.cache import caches
local_cache = caches["local"]
key = f"product_{product_id}"

data = local_cache.get(key)
if data:
return data

# L2: Redis
data = cache.get(key)
if data:
local_cache.set(key, data, timeout=60)
return data

# L3: DB
product = Product.objects.select_related("category").get(pk=product_id)
data = {"id": product.id, "name": product.name, "price": float(product.price)}
cache.set(key, data, timeout=300)
local_cache.set(key, data, timeout=60)
return data

배포 체크리스트

# settings/production.py
import os

DEBUG = False
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] # 환경 변수에서 로드
ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")

# 보안 헤더
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
SECURE_HSTS_SECONDS = 31536000 # 1년
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True

# 정적 파일
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

# 로깅
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "verbose",
},
},
"root": {
"handlers": ["console"],
"level": "WARNING",
},
"loggers": {
"django": {
"handlers": ["console"],
"level": os.getenv("DJANGO_LOG_LEVEL", "WARNING"),
"propagate": False,
},
},
}
# 배포 전 체크
python manage.py check --deploy # 보안 설정 검사
python manage.py collectstatic --noinput # 정적 파일 수집
python manage.py migrate --check # 미적용 마이그레이션 확인

# gunicorn + nginx
pip install gunicorn
gunicorn myproject.wsgi:application \
--workers 4 \
--worker-class gthread \
--threads 2 \
--bind 0.0.0.0:8000 \
--timeout 30

django-debug-toolbar — 개발 최적화 도구

pip install django-debug-toolbar
# settings/development.py
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
INTERNAL_IPS = ["127.0.0.1"]

# urls.py
import debug_toolbar
urlpatterns += [path("__debug__/", include(debug_toolbar.urls))]

# 확인 가능한 정보:
# - 실행된 SQL 쿼리 수와 시간
# - 캐시 히트/미스
# - 템플릿 렌더링 시간
# - HTTP 헤더

정리

최적화 항목방법
N+1 제거select_related, prefetch_related
컬럼 제한only(), defer(), values()
배치 처리bulk_create(), bulk_update()
대량 순회iterator(chunk_size=1000)
자주 조회Redis 캐시 + 무효화 전략
존재 확인exists() (count() 대신)
배포 보안check --deploy + HTTPS 강제
쿼리 분석django-debug-toolbar

Django 성능의 핵심은 N+1 쿼리 제거 + 적절한 캐싱입니다.

Advertisement