Django를 통해 개발을 처음 접하다 보면 종종 Django로 만든 프로젝트가 바로 사용자와 직접 통신하며 모든 요청을 처리한다고 생각한다. 하지만 실제 배포 환경은 단순하지 않다. Django는 단독으로 모든 요청을 처리하기에 적합한 구조가 아니며, 웹 서버(Web Server)와 WSGI 서버 같은 중간 계층이 필요하다. 이러한 계층들은 요청을 효율적으로 처리하고, 애플리케이션의 성능과 안정성을 보장하며, 확장성을 제공하는 중요한 역할을 한다.
이 글에서는 Django와 HTTP Request-Response Lifecycle을 통해 요청이 Django 내부에서 어떻게 처리되고 응답으로 이어지는지, 그리고 이 과정에서 웹 서버와 WSGI 서버가 어떤 역할을 하는지를 살펴보려 한다. 이러한 원리를 이해하면 단순히 개발 단계를 넘어서, 실제 배포 환경에서 안정적이고 확장 가능한 애플리케이션을 설계할 수 있을 것이다.
Django와 HTTP Request-Response Lifecycle
Django는 클라이언트(웹 브라우저 또는 모바일 앱)로부터 요청을 받아 이를 처리하고 적절한 응답을 생성하는 과정을 거친다. 이 과정은 여러 계층과 단계를 통해 이루어지며, 이를 Request-Response Lifecycle이라고 한다. 이 라이프사이클은 Django 애플리케이션이 요청을 처리하고 응답을 반환하는 기본 원리로, Web Server, WSGI, Django의 내부 처리 과정이 모두 포함된다.
1. 클라이언트
요청은 클라이언트에서 시작된다. 클라이언트는 사용자가 상호작용하는 웹 브라우저, 모바일 애플리케이션, 또는 API 호출 도구(Postman, cURL 등)일 수 있다. 클라이언트는 HTTP/HTTPS 프로토콜을 통해 서버로 요청을 보내며, 요청은 URL, HTTP 메서드(GET, POST, PUT 등), 헤더, 쿠키, 본문 데이터를 포함한다.
클라이언트의 주요 역할
- 요청 생성: 사용자가 특정 URL에 접근하거나 버튼을 클릭하면 HTTP 요청이 생성된다.
- 데이터 전송: 로그인 폼 데이터 또는 파일 업로드와 같은 데이터가 요청 본문에 포함된다.
- 응답 처리: 서버로부터 반환된 HTML 페이지, JSON 데이터 등을 받아 사용자에게 보여준다.
클라이언트에서 보낸 요청은 첫 번째 관문인 웹 서버에 도달한다.
2. 웹 서버
웹 서버는 클라이언트로부터 HTTP 요청을 받아 정적 파일(HTML, CSS, JS 등)을 직접 제공하거나, 동적 요청을 WSGI 서버로 전달하는 역할을 한다. (ex. Nginx, Apache) Nginx와 Web Server에 대한 글을 이전에 포스팅했기 때문에 생략하도록 하겠다.
3. WSGI/ASGI 서버
WSGI(Web Server Gateway Interface)와 ASGI(Asynchronous Server Gateway Interface)는 Python 웹 애플리케이션과 웹 서버 간의 인터페이스 표준이다. Django는 이 두 인터페이스 중 하나를 통해 클라이언트 요청을 처리하고 응답을 반환한다. 이것은 Spring의 서블릿 컨테이너(Tomcat, Jetty 등)와 유사한 역할을 한다고 생각하면 된다. 하지만 WSGI와 ASGI는 Python에 특화된 표준으로, Django 애플리케이션의 실행과 요청 처리를 책임진다. 두 인터페이스는 각각 다른 방식으로 동작하며, 지원 기능과 사용 목적에 따라 선택적으로 사용된다.
WSGI는 동기 요청(HTTP/HTTPS)을 처리하는 데 최적화된 표준이다. 이는 기존의 Django 애플리케이션과 같은 대부분의 동기 기반 웹 애플리케이션에 사용된다. WSGI는 한 번에 하나의 요청을 처리하며, 실시간 통신(WebSocket)이나 HTTP/2와 같은 비동기 작업을 지원하지 않는다. 대표적으로 Gunicorn, uWSGI, mod_wsgi 등이 있다.
ASGI는 동기와 비동기 요청을 모두 처리할 수 있는 표준이다. Django 3.0 이상에서 지원하며, WebSocket, HTTP/2, 서버 간 스트리밍 등 실시간 작업에 적합하다. ASGI는 더 유연하며 현대적인 웹 애플리케이션에 적합하지만, Django 애플리케이션이 ASGI를 최대한 활용하려면 비동기 코드를 작성해야 한다. 대표적으로 Daphne, Uvicorn 등이 있다.
기본 runserver 실행 시
python manage.py runserver 명령어를 실행하면 WSGI 서버가 실행된다. 이는 개발 환경에서 기본적으로 WSGI를 사용하는 구조 때문이다. ASGI 서버를 실행하려면 Uvicorn 또는 Daphne와 같은 ASGI 서버를 명시적으로 사용해줘야 한다.
WSGI와 ASGI, 두 인터페이스의 동시 사용?
WSGI와 ASGI는 하나의 요청에서 동시에 사용될 수 없다. 두 인터페이스는 서로 다른 프로토콜과 처리 방식을 기반으로 동작하기 때문에, 동일한 요청이 두 인터페이스를 거쳐 처리되는 것은 불가능하다.
여기서 헷갈리면 안될 것이 하나의 프로젝트에서 두 인터페이스를 사용하는 것이 불가능하다는 것이 아니다. 하나의 프로젝트에서 두 인터페이스를 모두 구현하는 것은 가능하다. 예를 들어, WSGI는 동기 HTTP 요청을 처리하고, ASGI는 WebSocket과 같은 비동기 요청을 처리하도록 구성할 수 있다. 이렇게 구성하면 WSGI와 ASGI가 각각의 역할을 수행하며 서로 독립적으로 작동하게 된다.
이를 위해서는 WSGI 서버와 ASGI 서버를 별도로 실행하거나, ASGI 서버를 사용하면서 WSGI 요청도 처리할 수 있도록 호환성을 지원하는 ASGI 지원 서버를 사용하는 방식으로 프로젝트를 배포할 수 있다.
WSGI나 ASGI가 없다면?
만약 WSGI나 ASGI 서버가 없다면, Django 애플리케이션은 클라이언트 요청을 받을 수 없다. WSGI와 ASGI는 Django가 외부에서 요청을 받을 수 있도록 중간에서 요청을 해석하고 애플리케이션과 연결하는 필수적인 역할을 담당하기 때문이다. 결론적으로 WSGI나 ASGI 서버 없이 Django는 외부 요청을 처리할 수 없다.
4. Django의 내부 처리
클라이언트의 요청이 WSGI 또는 ASGI 서버를 거쳐 Django 애플리케이션에 도달하면, Django는 이를 처리하기 위해 내부적으로 여러 단계를 거친다. 이러한 단계들은 각각 특정한 역할을 수행하며, 서로 유기적으로 연결되어 있다.
4.1. Request 미들웨어
Django에서 클라이언트의 요청은 가장 먼저 미들웨어 계층에 도달한다. 이 단계에서는 요청에 대한 사전 처리가 이루어진다. 예를 들어, 보안 검사를 수행하거나 요청에 인증 정보와 세션 데이터를 추가하는 작업이 포함된다. 요청 미들웨어는 Django가 제공하는 기본 미들웨어를 사용할 수도 있고, 특정 요구사항에 맞게 직접 작성한 커스텀 미들웨어를 추가할 수도 있다.
예를 들어, SessionMiddleware는 클라이언트의 세션 데이터를 확인하여 요청 객체에 추가하며, CsrfViewMiddleware는 CSRF 공격 방지를 위해 요청을 검증한다. 만약 요청 미들웨어가 없다면, 클라이언트의 요청이 사전 검증 없이 URL 라우터와 뷰로 바로 전달되어 보안 위험이 커지고, 세션 데이터 활용이나 CSRF 보호 같은 기본 기능이 작동하지 않게 될 것이다. 또한, 요청 유효성 검사가 불가능해져 잘못된 데이터가 처리될 가능성이 높아지고, 공통 작업을 처리할 수 없어 코드 중복과 유지보수 어려움이 발생할 것이다. 결과적으로, 애플리케이션의 안정성과 신뢰성이 크게 저하될 수 있다.
4.2. URL 라우터
미들웨어 단계를 통과한 요청은 URL 라우터로 전달된다. 이 단계에서 Django는 요청의 URL 경로를 분석하여, 해당 요청을 처리할 적절한 뷰(View)를 찾는다. Django는 프로젝트의 urls.py에 정의된 URL 패턴을 기반으로 요청 URL과 매칭되는 항목을 검색한다. 만약 URL이 적절한 뷰와 매칭되지 않으면, Django는 에러를 반환한다.
URL 라우터는 단순히 요청을 뷰와 연결하는 데 그치지 않고, 복잡한 URL 구조를 체계적으로 관리하고 모듈화하는 역할도 수행한다. 대규모 프로젝트에서는 이를 통해 URL 관리의 효율성을 높일 수 있다. 관련 내용은 URL Dispatcher 포스팅을 확인하면 좋을 것이다.
4.3. 뷰(Views)
URL 라우팅 단계에서 매칭된 요청은 해당 URL에 연결된 뷰로 전달된다. 뷰는 Django의 핵심 계층 중 하나로, 요청을 처리하고 응답을 생성하는 역할을 담당한다. 클라이언트가 보낸 요청 데이터(GET, POST 등)를 기반으로 필요한 작업을 수행하며, 데이터베이스와 상호작용하거나 비즈니스 로직을 실행한다.
뷰는 크게 두 가지 방식으로 작성할 수 있다. 하나는 간단한 함수 형태로 작성되는 FBV(Function-Based View)이고, 다른 하나는 객체 지향적인 방식으로 작성되는 CBV(Class-Based View)이다. 예를 들어, 사용자가 입력한 데이터를 저장하거나, 데이터베이스에서 특정 데이터를 조회한 후 이를 템플릿으로 전달하는 작업은 뷰에서 처리된다.
4.4. 템플릿 렌더링
뷰에서 데이터를 처리한 후, 요청에 대한 응답을 생성하기 위해 템플릿 렌더링 단계가 진행된다. 이 단계에서는 템플릿 파일과 뷰에서 전달된 데이터(Context)가 결합되어 최종 HTML 문서가 생성된다. Django 템플릿 언어(DTL)는 동적인 HTML 콘텐츠를 생성하기 위해 사용되며, 이를 통해 적절한 데이터를 표시할 수 있다.
템플릿 렌더링은 HTML 페이지를 생성하는 단순한 작업을 넘어, 데이터 표현을 효과적으로 관리하는 역할을 한다. 템플릿 상속을 활용하면 반복되는 HTML 구조를 줄이고, 유지보수를 용이하게 만들 수 있다. 또한, 템플릿 태그와 필터를 사용해 복잡한 데이터 변환 및 표현도 가능하다.
4.5. Response 미들웨어
템플릿 렌더링을 통해 생성된 응답은 다시 미들웨어 계층으로 전달된다. 응답 미들웨어는 최종 응답을 클라이언트로 반환하기 전에 응답 데이터를 추가적으로 처리하거나 수정할 수 있는 단계다. 예를 들어, 응답 본문을 압축하거나, 보안 관련 헤더를 추가하는 작업이 이 단계에서 이루어진다.
응답 미들웨어가 없다면, 클라이언트로 전달되는 응답이 충분히 최적화되지 않거나 보안 요구사항을 충족하지 못할 수 있다. 예를 들어, GZipMiddleware는 응답 본문을 압축해 클라이언트로 전송되는 데이터 양을 줄이며, XFrameOptionsMiddleware는 클릭재킹 방지를 위해 응답에 보안 헤더를 추가한다.
보통 Django의 흐름이라고 하면 MTV 패턴의 구조나 Django 내부에서의 요청 처리 과정 정도만 알고 넘어가는 경우가 많다. 하지만 Web Server, WSGI/ASGI, Middleware와 같은 전체적인 요청-응답 사이클을 이해하면, 실제 배포 환경까지 고려한 설계를 할 수 있을 것이다.
이 과정을 알면 성능 최적화를 위한 구조 설계, 실시간 작업(WebSocket 등)을 처리하기 위한 ASGI 활용, 보안 강화(CSRF, 인증 등), 디버깅 능력 향상, 그리고 확장성을 고려한 시스템 구축까지 가능하다. 이런 원리를 기반으로 각 상황에 맞는 프로젝트의 아키텍처를 설계하고 확장해보면 좋을 것 같다.
'Django' 카테고리의 다른 글
[Django] Django Middleware (Middleware Custom 해보기) (0) | 2025.01.26 |
---|---|
[Django] URL dispatcher 공식 문서 파헤치기 (1) | 2025.01.18 |
[Django] settings.py 완전 정복 (..파일 분리까지) (0) | 2025.01.11 |
[Django] User Model, Custom User (Extending User) (0) | 2025.01.11 |
[Django] Django 마이그레이션과 MySQL 활용 (0) | 2025.01.10 |