Django의 WSGI 진입점부터 뷰 응답까지의 완전한 호출 구조와 뷰 선택 과정을 상세히 분석합니다.
# django/core/wsgi.py
from django.core.handlers.wsgi import WSGIHandler
def get_wsgi_application():
django.setup(set_prefix=False)
return WSGIHandler()
# django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __call__(self, environ, start_response):
# WSGI 스펙에 따른 호출 가능 객체
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request) # BaseHandler의 메서드 호출
# 응답 처리
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase)
start_response(status, response.items(), response.get('wsgi.file_wrapper'))
return response
# django/core/handlers/base.py
class BaseHandler:
def get_response(self, request):
"""메인 요청 처리 로직"""
set_urlconf(settings.ROOT_URLCONF)
response = self._get_response(request)
# 응답 후처리
response = self.apply_response_fixes(request, response)
return response
def _get_response(self, request):
"""실제 요청 처리 흐름"""
response = None
callback = None
callback_args = None
callback_kwargs = None
try:
# URL 해결 (URLconf 처리) -> 뷰 선택
resolver = get_resolver(get_urlconf())
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
# 미들웨어 체인 실행 (process_request + process_view)
response = self._middleware_chain(request)
except Exception as e:
# 예외 처리 미들웨어 실행
response = self.process_exception_by_middleware(e, request)
if response is None:
raise
return response
# django/core/handlers/base.py
def _get_response(self, request):
response = None
try:
# 뷰 선택
resolver = get_resolver(get_urlconf())
resolver_match = resolver.resolve(request.path_info) # 뷰 결정
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
# 미들웨어 process_view 실행 (뷰는 이미 선택된 상태)
response = self.process_view_middleware(
request, callback, callback_args, callback_kwargs
)
# 실제 뷰 실행은 나중에
if response is None:
response = callback(request, *callback_args, **callback_kwargs)
중요: 뷰 선택은 미들웨어 실행보다 먼저 일어납니다.
# django/urls/resolvers.py
class URLResolver:
def resolve(self, path):
path = str(path)
tried = []
# 1단계: 현재 패턴과 매치 확인
match = self.pattern.match(path)
if match:
new_path, args, captured_kwargs = match
# 2단계: 하위 패턴들을 순회하며 매칭 시도
for pattern in self.url_patterns:
try:
sub_match = pattern.resolve(new_path)
except Resolver404:
# 매치 실패 시 다음 패턴 시도
continue
else:
# 3단계: 매치 성공 시 ResolverMatch 생성
return ResolverMatch(
sub_match.func, # 실제 뷰 함수/클래스
args + sub_match.args, # URL 위치 인자
{**captured_kwargs, **sub_match.kwargs}, # 키워드 인자
sub_match.url_name, # URL 이름
sub_match.app_names, # 앱 이름들
sub_match.namespaces, # 네임스페이스들
)
# 매치 실패 시 404 예외
raise Resolver404({'tried': tried, 'path': new_path})