1. 캐시의 필요성과 원리
동적 웹 애플리케이션에서 모든 사용자 요청은 일반적으로 데이터베이스 조회, 로직 처리, 템플릿 렌더링을 거칩니다. 방문자가 급증하면 서버 리소스 소모가 급격히 늘어나며 응답 지연이 발생할 수 있습니다. 이를 해결하기 위해 자주 접근하는 데이터를 메모리나 외부 저장소에 임시 보관하는 캐싱 기법을 사용합니다. 캐시가 유효한 동안에는 실제 비즈니스 로직을 실행하지 않고 미리 저장된 결과를 반환함으로써 성능을 크게 향상시킬 수 있습니다.
2. Django에서 지원하는 캐시 백엔드
- 개발용 더미 캐시: DEBUG 모드에서 캐시 동작을 시뮬레이션
- 메모리 기반 캐시: 프로세스 내 메모리 사용 (간단하지만 공유 불가)
- 파일 시스템 캐시: 지정 디렉터리에 파일 형태로 저장
- 데이터베이스 캐시: 별도 테이블에 캐시 레코드 저장
- Memcached + python-memcached
- Memcached + pylibmc: 고성능 바이너리 프로토콜 지원
실제 운영 환경에서는 Memcached 또는 Redis 기반 솔루션이 가장 일반적입니다.
3. 캐시 적용 범위: 세 가지 레벨
Django는 유연한 캐시 레이어를 제공하며, 다음과 같은 범위로 설정할 수 있습니다:
- 전체 사이트 캐싱: 모든 응답에 대해 자동 캐싱
- 뷰 단위 캐싱: 특정 URL 패턴에 대한 응답 전체를 캐시
- 템플릿 조각 캐싱: 페이지 내 일부 영역만 캐시
4. 파일 기반 캐시 실습 예제
전체 사이트 캐싱 설정
# settings.py
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware', # 반드시 최상단
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware', # 반드시 최하단
]
CACHE_MIDDLEWARE_SECONDS = 60
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/tmp/django_cache',
'TIMEOUT': 300,
'OPTIONS': {
'MAX_ENTRIES': 500,
'CULL_FREQUENCY': 2 # 만료 시 절반 제거
}
}
}
특정 뷰 캐싱
# views.py
from django.views.decorators.cache import cache_page
from django.shortcuts import render
import time
@cache_page(30) # 30초 캐시
def home_view(request):
context = {'server_time': time.strftime('%H:%M:%S')}
return render(request, 'home.html', context)
템플릿 내 부분 캐싱
<!-- home.html -->
<p>실시간 시간: {{ server_time }}</p>
{% load cache %}
{% cache 10 "sidebar_content" %}
<div class="sidebar">
<p>자주 변하지 않는 사이드바 정보</p>
<p>캐시된 시간: {{ server_time }}</p>
</div>
{% endcache %}
5. API 기반 아키텍처에서의 캐시 전략
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.core.cache import cache
from .models import Product
from .serializers import ProductSerializer
class ProductListView(APIView):
def get(self, request):
cache_key = 'product_list_json'
cached_data = cache.get(cache_key)
if cached_data:
print("Cache hit")
return Response(cached_data)
queryset = Product.objects.filter(active=True)
serializer = ProductSerializer(queryset, many=True)
cache.set(cache_key, serializer.data, timeout=60*5) # 5분
print("Cache miss - data stored")
return Response(serializer.data)
6. Django 시그널 시스템
시그널은 특정 이벤트 발생 시 사전에 등록된 핸들러 함수를 자동으로 호출하는 옵저버 패턴 구현입니다. 주요 내장 시그널은 다음과 같습니다:
핵심 시그널 종류
- 모델 관련: pre_save, post_save, pre_delete, post_delete, m2m_changed
- 요청 주기: request_started, request_finished
- 마이그레이션: pre_migrate, post_migrate
- 설정 변경: setting_changed
시그널 등록 방법
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import UserProfile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
# 수동 연결 방식
from django.core.signals import request_finished
def log_request_finish(sender, **kwargs):
print("HTTP 요청 처리 완료")
request_finished.connect(log_request_finish)
7. 실무 적용 시나리오
- 감사 로그: 사용자 계정 생성/삭제 시 기록
- 외부 서비스 연동: 회원 가입 시 메일 발송 트리거
- 캐시 무효화: 데이터 갱신 후 관련 캐시 삭제
- 분석 데이터 수집: 특정 행동 추적
시그널은 관심사 분리를 가능하게 하지만 남용 시 디버깅이 어려워질 수 있으므로, 핵심 비즈니스 로직보다는 부수 작업에 적합합니다.