Django
를 사용하여 개발을 시작한 초기에는 구현하는 기능이나 사용하는 리소스들이 제한적이었습니다. 하지만 점차 구현할 기능들도 다양해지고, 이를 구현하기 위해 Django
의 구조와 동작을 공부하며 새롭게 배우게 되며 Django
라는 프레임워크를 바라보는 시각 또한 많이 달라진 것 같습니다. 이러한 경험을 바탕으로 Django
의 구조와 동작 방식 등을 되짚어보고 Django
를 사용하여 개발을 하시는 분들에게 저의 관점이나 배운 것들을 공유하고자 조금이나마 제가 아는 부분을 정리해보고자합니다.
Django
로 개발을 공부중이신 분들이라면 아래의 그림을 알아두는 것이 좋을것 같습니다. 아래의 그림은 Django
가 어떻게 클라이언트로부터 받은 Request
를 처리하고, Response
를 반환하는지 간략하게 추상화해놓은 그림입니다.
Django
는 사용자의 요청을 처리하고 응답을 전송하기 위해 HTTP 프로토콜을 기본으로 사용합니다. WSGI
는 웹서버와 웹프레임워크를 연결하기 위한 도구입니다. 서버는 Django
애플리케이션을 실행하면서 콜백 함수를 애플리케이션에게 전달합니다. Django
애플리케이션은 서버로 전달받은 요청을 처리한 후, 콜백함수를 통해 응답을 반환합니다. 즉, WSGI
는 Nginx
, Apache
와 같은 웹서버와 Django
애플리케이션의 인터페이스 역할을 수행하게 됩니다.
settings.py
파일과 django의 예외 클래스들을 임포트합니다.settings.py
에 정의된 middleware 클래스들을 찾아 로드합니다.request
, view
, response
, exception
를 다루는 4가지 리스트)view
함수를 실행합니다.WSGI
를 통해 전달된 request
는 Middleware
로 전달되게 됩니다. 각 middleware
에는 요청, 응답, view, 예외를 처리하는 4가지 메소드중 1개 이상이 정의되어있습니다. 각 미들웨어에 정의된 해당 메소드들은 settings.py
에 정의된 순서나 역순으로 실행되며, django는
기본적으로 몇가지 미들웨어들을 제공하게 됩니다. WSGI
를 통해 전달받은 request
는 middleware
를 통해 url dispatcher/url router
, view
로 전달된 후 view
에서 반환되는 응답이 다시 middleware
를 통해 WSGI
로 전달되게 됩니다.
URL Router
는 미들웨어를 통해 전달받은 request
에서 url path
정보를 통해 알맞는 view
로 전달하는 역할을 합니다. url pattern
에는 정규 표현식이 사용될 수 있으며, Django
에 정의된 url
패턴과 매칭되는 view
는 인자로서 request
를 전달받게 됩니다.
view
는 라우터를 통해 요청을 전달받게 됩니다. 이 레이어를 비즈니스 로직 레이어라고 부르며, 아마Django
를 입문하시는 분들은 개발 시간의 대부분을 이부분에 할애할것입니다. 개발자들은 이 레이어에서 서비스가 제공하는 비즈니스 로직을 구현하게 되며, html template, json과 같은 다양한 응답을 생성하여 반환하게 됩니다.
반환된 응답 혹은 발생한 예외는 다시 middleware
의 process_response
메소드나 process_exception
메소드를 거쳐 WSGI
로 전달되게 됩니다. 응답 미들웨어에서는 request
를 처리하고 최종적으로 클라이언트에게 전달하기 전에 헤더나 데이터 정보를 추가하거나 수정하게 됩니다.
위에서 request가 middleware, view, 다시 middleware 를 통해 응답으로 반환되는 과정에서 각 단계에 원하는 미들웨어를 구현하기 위해서는 구현해야하는 메소드드들이 존재합니다. 즉, 원하는 기능의 미들웨어를 원하는 단계에 구현하기 위해서는 각 단계에 맞는 메소드를 구현해야하고, 구현된 middleware메소드는 WSGI 핸들러에 의해 수집되어 등록된 순서되로 실행되게 됩니다.
view
에 request
를 전달하기 전에 request를 처리 : process_request()
view
가 실행되기 전 : process_view()
view
에서 반환된 request
, response
처리 : process_response()
view
에서 발생한 exception 처리 : process_exception()
미들웨어를 구현하기 위해서는 위의 4가지 메소드들 중 1가지 이상을 구현해야합니다. 각각의 메소드들은 WSGI 핸들러에의해 수집되어 리스트에 등록되며, 등록된 순서 혹은 역순대로 호출됩니다.
view
로 request
를 전달하기 전에 request
객체를 처리할 메소드입니다. settings.py
의 MIDDLEWARES
에 정의된 미들웨어의 process_reqeust()
메소드들을 정의된 순서대로 등록, 호출하여 request
객체를 처리하여 최종적으로 view
함수의 인자로 전달되게 됩니다.
process_request
메소드는 None
혹은 HttpResponse
객체를 반환할 수 있습니다. None
을 반환하게 되는 경우 WSGI Handler
에 등록된 process_request
들을 계속해서 정상적으로 실행하여 view
로 request
를 전달하게 됩니다. 그러나 HttpResponse
객체를 반환하게 되는 경우, 즉각적으로 process_response
메소드들을 호출하여 view
로 request
를 전달하지 않고 즉각적으로 process_response
사이클을 실행하게 됩니다.
request
는 HttpRequest
객체이며, 호출할 view
함수와 view
함수에 전달할 인자들은 view_args
,view_kwargs
입니다. process_view
메소드들은 view
함수를 실행하여 응답을 생성하기 전에 호출되며, process_request
와 마찬기자로 None
이나 HttpResponse
를 응답으로 반환해야합니다.
None
을 반환하는 경우에는 등록된 다른 process_view
메소드들을 실행하고 view
함수를 최종적으로 실행하여 응답을 생성하지만, HttpResponse
객체를 반환하는 경우에는 view
함수를 실행하지 않고 반환된 HttpResponse
객체를 처리하여 즉각적으로 응답으로 반환하게 됩니다.
process_reponse
메소드에서는view
나 process_view
가 반환되는 HttpResponse
객체를 다루게 됩니다. process_response
메소드들은 settings.py
에 등록된 MIDDLEWARE
의 역순으로 실행되며, 응답의 데이터를 마지막으로 수정할 수 있는 단계입니다.
view
에서 예외가 발생하게 되면, WSGI Handler
는 등록된 process_exception
메소드들을 순차적으로 실행하여 예외를 처리하게 됩니다. process_exception
메소드들은 process_response
와 마찬가지로 settings.py
에 등록된 MIDDLEWARE
의 역순으로 실행됩니다.
process_exception
메소드는 None
혹은 HttpResponse
객체를 반환해야합니다. HttpResponse
객체를 반환하게 되는 경우, 이후에 실행되는 다른 process_exception
메소드들은 실행되지 않고 응답을 처리하여 반환하게 됩니다. 이와 달리 None
을 반환하는 경우에는 django
의 exception handling
가 실행됩니다.
REST API 서버를 개발하며 서버 내부에서 에러가 발생하게 되는 경우, 500 응답을 반환하는 ErrorHandlerMiddleware
를 구현해보겠습니다.
class ErrorHandlerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 요청이 들어오게 되면 view 함수가 호출되기 전에 실행되는 코드 영역입니다.
response = self.get_response(request) # view 함수가 실행되어 응답이 생성되는 부분입니다.
# view 함수가 실행된 후, 실행되는 코드 부분입니다.
return response
def process_exception(self, request, exception):
if not settings.DEBUG: # 개발환경에서는 기존의 에러 html template를 그대로 브라우저에 렌더링합니다.
return HttpResponse(
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
content_type='application/json',
content = json.dumps({
'status_code': status.HTTP_500_INTERNAL_SERVER_ERROR,
'message': 'Internal Server Error',
'data': {
'message': '서버 내부 오류입니다. 관리자에게 문의 부탁드립니다.'
}
})
)
process_exception
는 view
에서 에러가 발생하게 되면 실행됩니다. None
을 반환하게되면 등록된 다른 process_exception
메소드들을 실행하고 Django
의 기본 예외 핸들러가 개입하여 우리가 흔하게 마주하게되는 아래와 같은 html 템플릿을 렌더링하게 됩니다. 하지만 HttpResponse
객체를 반환하면 다른 process_exception
메소드들은 실행하지 않고 생성된 응답을 반환합니다.
{
"status_code": 500,
"message": "Internal Server Error",
"data": {
"message": "서버 내부 오류입니다. 관리자에게 문의 부탁드립니다."
}
}
아마 django로 처음 개발을 하시는 분들은 대부분 views.py에 서비스의 로직을 구현하고, urls.py를 통해 구현한 로직을 url에 연동하는 작업을 하시는게 대부분이실 것으로 예상합니다. 하지만 Django가 지원하는 기능은 그보다 훨씬 다양하며, 단순한 client-server 모델뿐만 아니라 다양한 비동기 프로토콜도 지원하고 있습니다. 이러한 다양한 기능들을 사용하기 위해서는 Django의 구조와 구성 요소들이 무엇이며, 어떤 역할을 수행하며 다른 요소들로는 무엇이 있는지 알아야 다양한 기능 요구들을 충족할 수 있을 것이라고 생각합니다.