ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django] Django의 동작 원리와 과정 파헤치기
    파이썬/Django 2021. 10. 20. 09:38

    Django 동작과정

    1. WSGI Application 생성
    2. settings에서 정의한 settings.MIDDLEWARE 로드
    3. WSGI(uwsgi, gunicorn)에서 applcation call
    4.  WSGI environ(request data를 담은 딕셔너리)과 함께 WSGIRequest 인스턴스 생성
    5. 미들웨어 체인에 WSGIRequest 인스턴스와 함께 call
    6. 미들웨어를 모두 거쳐간 후 최종적으로 view에 request가 전달되어 개발자가 정의한 로직을 거친 후 최종적으로 Response 반환

    상세 보기

    django.core.wsgi

    • 우리가 만든 장고 프로젝트 wsgi.py의 application 변수에는  callable한 WSGIHandler의 인스턴스가 반환된다.
    • 이때 settings에 정의한 미들웨어를 로드한다.
    • WSGi가 해당 application 을 call하게되면 __call__ 메서드가 실행된다.

     

    django.core.handlers.wsgi

    • WSGI로부터 받은 요청(environ에 담긴) WSGIRequest 인스턴스를 생성한다. in __call__
    • WSGIRequestget_response 에 넘겨준다. in __call__

     

     

    • middleware_chain 메서드에 request객체를 넘겨주고 stack구조인 middleware는 순서에 따라 각각 처리가 된후 response 가 반환된다. in get_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

    댓글

Designed by Tistory.