-
[Django] Django의 동작 원리와 과정 파헤치기파이썬/Django 2021. 10. 20. 09:38
Django 동작과정
- WSGI Application 생성
- settings에서 정의한 settings.MIDDLEWARE 로드
- WSGI(uwsgi, gunicorn)에서 applcation call
- WSGI environ(request data를 담은 딕셔너리)과 함께 WSGIRequest 인스턴스 생성
- 미들웨어 체인에 WSGIRequest 인스턴스와 함께 call
- 미들웨어를 모두 거쳐간 후 최종적으로 view에 request가 전달되어 개발자가 정의한 로직을 거친 후 최종적으로 Response 반환
상세 보기
- 우리가 만든 장고 프로젝트 wsgi.py의
application
변수에는 callable한WSGIHandler
의 인스턴스가 반환된다. - 이때 settings에 정의한 미들웨어를 로드한다.
- WSGi가 해당
application
을 call하게되면__call__
메서드가 실행된다.
- WSGI로부터 받은 요청(environ에 담긴)
WSGIRequest
인스턴스를 생성한다. in__call__
WSGIRequest
를get_response
에 넘겨준다. in__call__
middleware_chain
메서드에 request객체를 넘겨주고 stack구조인 middleware는 순서에 따라 각각 처리가 된후response
가 반환된다. inget_response
- 최종적으로 wsgi에서
response
를 받을 수 있게 된다.
미들웨어 동작 방식
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' ]
미들웨어는 WSGIHandler가 init 될 때 로드되는데 로드되는 방식은 이렇다.
self.load_middleware()
def load_middleware(self, is_async=False): """ Populate middleware lists from settings.MIDDLEWARE. Must be called after the environment is fixed (see __call__ in subclasses). """ self._view_middleware = [] self._template_response_middleware = [] self._exception_middleware = [] # 장고 3.0 부터 비동기처리가 가능하므로 비동기라면 비동기용 get_response 메서드를 가져옴 # 동기라면 동기용 _get_response 메서드 가져옴 # _get_response 이 메서드 실질적으로 url을 찾아 view에 request 객체를 넘겨주는 부분임 get_response = self._get_response_async if is_async else self._get_response # get_response callable 객체를 넘겨주고 exception 발생시 response 를 받을 수 있도록 래핑해줌 # convert_exception_to_response 메서드는 비동기 또는 동기에 따라 분기됨 handler = convert_exception_to_response(get_response) handler_is_async = is_async # 왜 Middleware를 뒤집나? 가장 처음 정의한 미들웨어를 스택 최상단에 위치하게 하기 위해서 for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) middleware_can_sync = getattr(middleware, 'sync_capable', True) middleware_can_async = getattr(middleware, 'async_capable', False) if not middleware_can_sync and not middleware_can_async: raise RuntimeError( 'Middleware %s must have at least one of ' 'sync_capable/async_capable set to True.' % middleware_path ) elif not handler_is_async and middleware_can_sync: middleware_is_async = False else: middleware_is_async = middleware_can_async try: # 필요시 핸들러(get_response)를 성격(sync, async)에 맞게 바꿔주는 작업을 해준다. adapted_handler = self.adapt_method_mode( middleware_is_async, handler, handler_is_async, debug=settings.DEBUG, name='middleware %s' % middleware_path, ) # 가져온 미들웨어에 적용된 핸들러와 함꼐 넘겨주면서 미들웨어 인스턴스를 생성 # 여기서 stack 처럼 미들웨어가 쌓임 # 왜냐하면 adapted_handler는 이미 밑에서 convert_exception_to_response 메서드가 래핑된 핸들러임 mw_instance = middleware(adapted_handler) except MiddlewareNotUsed as exc: if settings.DEBUG: if str(exc): logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) else: logger.debug('MiddlewareNotUsed: %r', middleware_path) continue else: handler = adapted_handler if mw_instance is None: raise ImproperlyConfigured( 'Middleware factory %s returned None.' % middleware_path ) # 미들웨어 훅들이 존재하면 미들웨어 종류별로 넣어줌 # Q. _view_middleware 리스트만 앞단에 insert 해주는 이유는 무엇일까? if hasattr(mw_instance, 'process_view'): self._view_middleware.insert( 0, self.adapt_method_mode(is_async, mw_instance.process_view), ) if hasattr(mw_instance, 'process_template_response'): self._template_response_middleware.append( self.adapt_method_mode(is_async, mw_instance.process_template_response), ) if hasattr(mw_instance, 'process_exception'): # The exception-handling stack is still always synchronous for # now, so adapt that way. self._exception_middleware.append( self.adapt_method_mode(False, mw_instance.process_exception), ) # 미들웨어 인스턴스도 except 발생시 response 를 받을 수 있도록 래핑해줌 handler = convert_exception_to_response(mw_instance) handler_is_async = middleware_is_async # Adapt the top of the stack, if needed. # 스택의 최상단 핸들러를 is_async에 따라 메서드를 비동기 또는 동기 메서드로 적용한다. handler = self.adapt_method_mode(is_async, handler, handler_is_async) # We only assign to this when initialization is complete as it is used # as a flag for initialization being complete. self._middleware_chain = handler
'파이썬 > Django' 카테고리의 다른 글
pydantic이란? (DRF Serializer를 대체할 수 있을까?) (1) 2021.12.14 Docker + Django에 Gitlab-CI(Pytest) 붙이기 (0) 2021.10.24 Gunicorn 동작 과정 알아보기 (0) 2021.10.10 [Django] GenericForeignKey (0) 2021.05.23 [Django] RestFramework 페이지네이션 종류 (0) 2021.05.05