Django Middleware 순서 문제와 해결 방법
Django middleware의 실행 순서를 이해하고, 순서로 인한 버그를 진단하고 해결하는 방법을 알아봅니다.
Django Middleware 순서 문제와 해결 방법
Django의 middleware는 request/response 처리 과정에서 중요한 역할을 합니다. 하지만 middleware의 실행 순서를 잘못 이해하면 예상치 못한 버그가 발생할 수 있습니다.
Environment
$ python --version
Python 3.11.5
$ pip show django
Name: Django
Version: 4.2.5
$ cat /etc/os-release | head -3
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"Problem: Custom Middleware가 인증되지 않은 요청을 통과시킴
다음과 같은 상황을 가정해봅시다:
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'myapp.middleware.AuthenticationMiddleware', # Custom middleware
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]# myapp/middleware.py
class AuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Check if user is authenticated
if not hasattr(request, 'user') or request.user.is_anonymous:
# Redirect to login
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('login'))
response = self.get_response(request)
return response실행 결과:
$ python manage.py runserver
$ curl -v http://localhost:8000/dashboard/
> GET /dashboard/ HTTP/1.1
> Host: localhost:8000
< HTTP/1.1 302 Found
< Location: /accounts/login/인증된 사용자도 로그인 페이지로 리다이렉트되는 문제가 발생합니다.
Analysis: Middleware 실행 순서 이해
Django middleware는 request와 response 두 가지 시점에서 실행됩니다:
# middleware execution order visualization
"""
Request phase (top to bottom):
1. SecurityMiddleware.__call__ → get_response(request)
2. SessionMiddleware.__call__ → get_response(request)
3. AuthenticationMiddleware.__call__ → get_response(request)
↓
View executes here
↓
Response phase (bottom to top):
3. AuthenticationMiddleware processes response
2. SessionMiddleware processes response
1. SecurityMiddleware processes response
"""문제를 진단하기 위해 logging을 추가합니다:
# myapp/middleware.py
import logging
logger = logging.getLogger(__name__)
class AuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
logger.debug(f"Auth middleware: Processing request to {request.path}")
logger.debug(f"Auth middleware: Has user attr: {hasattr(request, 'user')}")
if hasattr(request, 'user'):
logger.debug(f"Auth middleware: User: {request.user}, Is anonymous: {request.user.is_anonymous}")
response = self.get_response(request)
logger.debug(f"Auth middleware: Response status: {response.status_code}")
return response로그 출력:
DEBUG:myapp.middleware:Auth middleware: Processing request to /dashboard/
DEBUG:myapp.middleware:Auth middleware: Has user attr: Falserequest.user 속성이 설정되기 전에 custom middleware가 실행되고 있음을 확인할 수 있습니다.
Solution: Middleware 순서 수정 및 개선
방법 1: Middleware 순서 올바르게 설정
# settings.py - 올바른 순서
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', # 먼저 실행
'django.contrib.messages.middleware.MessageMiddleware',
'myapp.middleware.CustomAuthenticationMiddleware', # 그 다음 실행
]방법 2: AuthenticationMiddleware 상속
# myapp/middleware.py
from django.contrib.auth.middleware import AuthenticationMiddleware as DjangoAuthMiddleware
class CustomAuthenticationMiddleware(DjangoAuthMiddleware):
def __call__(self, request):
# Call parent first to set request.user
response = super().__call__(request)
# Now check authentication
if request.user.is_anonymous:
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('login'))
return response방법 3: process_view 사용
# myapp/middleware.py
class AuthenticationMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# This runs after URL resolution but before view execution
# request.user is available here
if request.user.is_anonymous:
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('login'))
return None # Continue to viewAdvanced: Conditional Middleware Application
# middleware.py
class ConditionalMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Skip certain paths
skip_paths = ['/api/health', '/static/', '/admin/']
if any(request.path.startswith(path) for path in skip_paths):
return self.get_response(request)
# Process request...
response = self.get_response(request)
return response
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'myapp.middleware.ConditionalMiddleware', # After auth middleware
]Lessons Learned
Middleware 순서가 중요합니다: Django는 MIDDLEWARE 리스트의 순서대로 middleware를 실행합니다. 인증 관련 middleware는 항상 Django의 AuthenticationMiddleware 다음에 와야 합니다.
Request/response 두 단계: 각 middleware는 request 처리와 response 처리 두 단계를 거칩니다. 순서를 혼동하면 버그가 발생합니다.
process_view 활용: view 실행 전에 request.user를 확인해야 한다면 process_view 메서드를 사용하는 것이 더 안전합니다.
로깅으로 디버깅: middleware 순서 문제는 로깅을 통해 쉽게 진단할 수 있습니다.
Third-party middleware 주의: Django REST Framework나 CORS 관련 middleware도 순서에 민감합니다.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.