Django URL Dispatcher 란?
Django는 웹 프레임워크로, 웹 애플리케이션 개발을 단순화하고 유지보수를 쉽게 만든다. 그중 URL Dispatcher는 사용자가 웹사이트에서 특정 URL을 요청했을 때, 해당 요청을 적절한 뷰(view)와 연결하는 Django의 핵심 기능이다. 오늘은 Django의 URL Dispatcher가 어떻게 동작하는지, 왜 중요한지, 그리고 효과적으로 사용하는 방법을 Django 공식 문서를 기반으로 정리해 볼 예정이다.
Django의 URL Dispatcher는 사용자가 웹사이트에서 특정 URL을 요청했을 때, 해당 요청을 처리할 적절한 뷰(view)와 연결해주는 시스템이다. 웹 애플리케이션의 URL 관리는 개발자가 코드를 관리하기 쉽게 만드는 중요한 요소이기 때문에, Django는 이를 효과적으로 처리할 수 있는 URL 디스패처를 제공한다.
Django의 요청 처리 방식
Django는 사용자가 특정 페이지를 요청할 때 다음과 같은 순서로 요청을 처리한다.
1. 루트 URLconf 모듈 결정
- Django는 요청이 들어오면 먼저 사용할 URLconf 모듈을 결정한다.
- 기본적으로 settings.py 파일의 ROOT_URLCONF 설정 값을 사용한다. 예를 들어, ROOT_URLCONF = 'mysite.urls'로 설정되어 있으면 Django는 mysite/urls.py를 URLconf로 사용한다.
- 하지만 요청의 HttpRequest 객체에 urlconf 속성이 설정되어 있는 경우(ex. 미들웨어에서 설정), 이 값을 우선으로 URLconf 모듈로 사용한다. (이 경우는 미들웨어 설정을 따로 하지 않는 이상 기본적으로 ROOT_URLCONF 설정 값을 기반으로 URLconf 모듈을 결정한다.)
2. URLconf 모듈 로드 및 urlpatterns 확인
- Django는 결정된 URLconf 모듈을 로드하고, 해당 모듈에서 urlpatterns 변수를 찾는다.
- urlpatterns는 django.urls.path() 또는 django.urls.re_path()를 사용하는 URL 패턴들의 리스트로 이뤄져야 한다.
3. URL 패턴 매칭
- Django는 urlpatterns에 정의된 URL 패턴을 순차적으로 검사한다.
- 요청된 URL과 path_info 속성을 기준으로 패턴을 비교하며, 첫 번째로 일치하는 패턴을 찾으면 멈춘다.
4. 뷰 호출
- 일치하는 URL 패턴을 찾으면, Django는 해당 뷰를 호출한다. 뷰는 Python 함수 또는 클래스 기반 뷰(FBV/CBV)일 수 있다.
- 뷰 함수에 다음과 같은 인자를 전달받는다.
- HttpRequest 객체: 사용자의 요청에 대한 모든 정보를 포함한다.
- 위치 인자(Positional Arguments): 정규 표현식 그룹에서 추출된 값들 (이름 없는 경우)
- 키워드 인자(Keyword Arguments): URL 패턴에서 이름이 지정된 그룹 (ex. <int:year>)이나 추가로 전달된 kwargs 값
5. 오류 처리
- 만약 urlpatterns에서 일치하는 URL 패턴을 찾지 못하거나, 뷰 실행 중 예외가 발생하면 Django는 오류 처리 뷰를 호출한다.
- 기본적으로 다음과 같은 오류 처리 뷰가 제공된다.
- 404 오류: URL이 일치하지 않을 때 호출
- 500 오류: 서버 내부 오류 발생 시 호출
- 필요에 따라 handler404와 handler500 등을 커스터마이징할 수 있다.
URLconf란?
Django에서 URL을 관리하는 파일은 URLconf(URL Configuration)이다. urls.py라는 파일에 URL과 뷰를 매핑하는 방식으로 구성되며, Python 코드로 작성된다. 공식 문서의 예시를 가져왔다.
(URL dispatcher를 "길 안내 시스템"이라고 생각하면, URLconf는 "지도"라고 할 수 있다. 지도(=URLconf)에 경로와 목적지(=뷰)를 설정하면, 길 안내 시스템(=URL dispatcher)이 사용자 요청에 따라 올바른 경로를 찾아 목적지에 도달하게 해준다.)
from django.urls import path
from . import views
urlpatterns = [
path("articles/2023/", views.special_case_2023),
path("articles/<int:year>/", views.year_archive),
path("articles/<int:year>/<int:month>/", views.month_archive),
path("articles/<int:year>/<int:month>/<slug:slug>/", views.article_detail),
]
URL 패턴 정의
Django에서는 path() 함수를 사용하여 URL 패턴을 정의한다. URL 패턴은 고정된 경로와 동적으로 값을 캡처하는 경로를 모두 지원한다.
고정된 경로
path("articles/2023/", views.special_case_2023)
동적 값
path("articles/<int:year>/", views.year_archive)
- <int:year> → year 값을 정수로 캡처한다.
- <slug:slug> → 슬러그 형태(영문, 숫자, 하이픈 포함)를 캡처한다.
Path Converters
Path Converters는 Django의 path() 함수에서 URL 경로의 특정 부분을 매칭하고 변환하는 데 사용되는 도구이다. URL 경로를 정의할 때 <converter:variable> 형식으로 사용되며, URL 경로에서 특정 데이터 유형(str, int ...) 매칭, 변환된 데이터를 뷰에 전달하는 기능을 제공한다. 기본적으로 Django에서 제공하는 Path Converters는 다음과 같다.
- str: 모든 비어있지 않은 문자열 (/ 제외)
- ex) <str:username> → username="john_doe"
- int: 0 이상의 정수
- ex) <int:year> → year=2023
- slug: 영문, 숫자, 하이픈, 언더스코어로 구성된 모든 문자열
- ex) <slug:article_slug> → article_slug="django-tutorial"
- uuid: UUID 형식(8-4-4-4-12의 32자리 문자열)
- ex) <uuid:unique_id> → unique_id=UUID('12345678-1234-5678-1234-567812345678')
- path: 전체 URL 경로 포함
- ex) <path:file_path> → file_path="documents/reports/2023"
Custom Path Converters
더 복잡한 매칭이 필요한 경우 Path Converters를 커스텀할 수 있다.
Custom Path Converter를 구현할 때, 클래스는 regex 속성(정규 표현식), to_python 메서드(값 변환), to_url 메서드(값 반환)로 구성된다. 변환 실패 시 ValueError를 발생시키며, Django는 이를 기반으로 404 오류를 반환하거나 URL 역방향 매칭(reverse)에서 NoReverseMatch 예외를 발생시킨다. 다음 예시를 보면 이해가 빠르게 될 것이다.
class FourDigitYearConverter:
regex = "[0-9]{4}"
def to_python(self, value):
return int(value)
def to_url(self, value):
return f"{value:04d}"
from django.urls import register_converter
register_converter(FourDigitYearConverter, "yyyy")
urlpatterns = [
path("articles/<yyyy:year>/", views.year_archive),
]
Custom Path FourDigitYearConverter는 Django에서 URL 경로에서 4자리 숫자를 처리하기 위해 설계된 Custom Path Converter다. 이 변환기는 클래스 속성인 regex를 [0-9]{4}로 정의하여 4자리 숫자를 매칭할 수 있도록 설정하며, URL에서 매칭된 값을 처리하고 변환하는 데 사용된다.
to_python 메서드는 URL에서 추출된 문자열 값을 Python의 정수(int)로 변환한다. 예를 들어, URL 경로에서 "2023"이라는 문자열이 추출되면 이를 정수 2023으로 변환하여 뷰 함수로 전달한다. 반면, to_url 메서드는 Python에서 사용하는 정수 값을 다시 URL에 삽입 가능한 문자열로 변환한다. 예를 들어, 값이 45라면 이를 "0045"로 변환하여 URL에 적합한 형태로 만든다.
이렇게 커스텀한 Converter를 실제로 사용하려면 register_converter 메서드를 사용하여 Converter를 등록해야 한다. 등록된 Converter는 URL 패턴 정의에서 <yyyy:year>와 같은 형식으로 사용할 수 있다. 이를 통해 URL 경로에서 4자리 연도를 처리하고 뷰에 안전하고 변환된 데이터를 전달할 수 있다.
Custom Path Converter는 복잡한 URL 매칭 요구사항을 충족시키고, 데이터 처리를 단순화하며, 뷰 함수에서 필요한 데이터를 바로 사용할 수 있도록 도와준다. 예를 들어, 특정 데이터 형식(연도, ID 등)을 처리할 때 유용하며, Django URL 설계와 구현을 더 직관적이고 유연하게 만들어준다.
예를 들어, FourDigitYearConverter를 활용하면, /articles/2023/와 같은 URL에서 "2023"을 정수 2023으로 변환하여 뷰 함수에서 바로 사용할 수 있다. 이를 통해 URL 패턴을 간결하게 유지하고, 데이터 처리의 복잡성을 줄일 수 있다. Django의 URLconf에서 이러한 변환기를 활용하면 URL 관리와 데이터 처리가 더욱 효율적으로 이루어진다.
정규 표현식을 사용하는 URL 정의 (Using regular expressions)
Django의 기본 path()와 경로 변환기만으로 복잡한 URL 매칭 조건을 처리하기 어려운 경우, re_path()를 사용하여 정규 표현식을 활용할 수 있다. 이는 더 세부적인 URL 패턴 매칭을 도와준다.
정규 표현식은 Python의 re 모듈과 호환되며, URL 경로를 더 유연하게 정의할 수 있다. 예시는 다음과 같다.
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r"^articles/(?P<year>[0-9]{4})/$", views.year_archive),
re_path(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$", views.month_archive),
re_path(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$", views.article_detail),
]
- (?P<year>[0-9]{4})는 year라는 이름으로 4자리 숫자를 추출한다.
- (?P<month>[0-9]{2})는 month라는 이름으로 2자리 숫자를 추출한다.
- (?P<slug>[\w-]+)는 slug라는 이름으로 단어 문자, 하이픈, 언더스코어를 포함하는 문자열을 추출한다.
정규 표현식을 활용한 URL 정의는 Django에서 복잡한 패턴 매칭을 가능하게 할 수 있도록 도와준다. path()에서는 지원하지 않는 정교한 패턴을 처리하거나 특정 요구사항에 맞는 URL을 설계할 때 유용하다. 예를 들어, (?P<year>[0-9]{4})는 정확히 4자리 숫자만 허용하여 10000과 같은 값은 매칭되지 않도록 제한할 수 있다.
정규 표현식에서 그룹으로 매칭된 값은 뷰 함수에 전달된다. 이 값들은 기본적으로 문자열 형태로 전달되며, 이를 활용해 뷰에서 동적으로 데이터를 처리할 수 있다. 정규 표현식 그룹은 두 가지 형태로 나뉜다. named regular expression groups, unnamed regular expression groups이다.
- named: (?P<name>pattern) 형식으로 그룹에 이름을 부여하여 값이 키워드 인자로 전달된다.
- unnamed: (pattern) 형식으로 값을 위치 인자로 전달하지만, named 그룹 사용이 권장된다.
또한, 정규 표현식은 중첩된 인수(Nested arguments)도 허용한다. 각자 상황에 맞게 활용하면 좋을 것 같다.
path() vs re_path()
path(route, view, kwargs=None, name=None)
re_path(route, view, kwargs=None, name=None)
path()와 re_path()의 함수 구조는 동일하다. 하지만 쓰임과 route에 들어가는 형식이 조금씩 다르다.
path()는 간단하고 직관적인 URL 패턴을 정의하기 위해 사용된다. path()의 route는 URL 경로 표현식으로, <int:section>처럼 경로 변환기를 포함할 수 있다.
re_path()는 정규 표현식을 사용해 복잡한 URL 패턴을 정의할 때 사용된다. re_path()의 route는 정규 표현식 문자열로, Python의 re 모듈과 호환된다. 정규 표현식 그룹에서 추출한 값은 뷰 함수에 위치 또는 키워드 인자로 전달된다.
나머지 인자들을 동일한 역할과 기능을 한다.
- view: 매칭된 요청을 처리할 뷰 함수 또는 클래스.
- kwargs: 추가 인자를 딕셔너리 형태로 전달.
- name: URL 패턴의 이름으로, 프로젝트 내에서 참조할 때 사용된다.
What the URLconf searches agains
공식 문서를 보다가 추가로 알게 된 신기한 점은 Django의 URLconf가 HTTP 요청을 처리하는 방식(URLconf가 검색하는 것)이다. Django의 URLconf는 요청된 URL에서 경로(path) 부분만 검색한다. 이는 URL의 GET/POST 파라미터나 도메인 이름은 매칭 과정에서 무시된다는 의미다.
추가로 신기한 점은 Django의 URLconf가 HTTP 요청을 처리하는 방식(URLconf가 검색하는 것)이다. Django의 URLconf는 요청된 URL에서 경로(path) 부분만 검색한다고 한다. 이는 URL의 GET/POST 파라미터나 도메인 이름은 매칭 과정에서 무시된다는 의미다. 예를 들어, `https://www.example.com/myapp/`에서 URLconf는 `myapp/`만 확인하며, `https://www.example.com/myapp/?page=3`에서도 동일하게 `myapp/`만 확인한다. 이는 URL 매칭 과정에서 필요하지 않은 정보를 배제하고 경로 자체에만 집중할 수 있도록 설계된 방식이다.
근데 또 생각해보면 당연하다. 이는 Django의 View가 요청의 세부 사항을 처리할 수 있도록 의도적으로 역할을 분리한 결과다. 예를 들어, View 함수 내부에서 if request.method == 'GET':처럼 요청 메서드를 구분하거나, request.GET과 request.POST를 활용해 필요한 파라미터를 처리하기 때문이다. 이러한 구조 덕분에 URLconf는 오직 경로 매칭에만 집중할 수 있고, View는 요청 데이터를 다루는 데 전념할 수 있어 애플리케이션의 유지보수성과 가독성을 높일 수 있는 것이라 생각한다.
Reverse resolution of URLs
Django 프로젝트를 개발하다 보면 최종적으로 생성된 URL을 동적으로 다뤄야 하는 상황이 자주 발생한다. 이는 사용자에게 표시할 링크를 생성하거나, 뷰에서 사용되는 특정 리소스(image, css 등)의 경로를 지정하거나, 서버 측에서 리다이렉션과 같은 흐름을 처리할 때 필요하다.
Reverse resolution of URLs의 필요성
URL을 직접 하드코딩하는 것(예: www.example.com/myapp/1)은 비효율적이고 유지보수가 어렵다. 만약 URL 설계가 변경되면, 프로젝트 전반의 소스 코드에서 이를 일일이 찾아 수정해야 하며, 이는 오류 발생 가능성을 높인다.
Django는 이러한 문제를 해결하기 위해 URLconf를 통해 URL 설계를 중앙에서 관리할 수 있는 방식을 제공한다. URLconf를 활용하면 URL 변경 시 단 한 곳에서만 수정하면 되며, 이를 참조하는 모든 곳에서 자동으로 반영되므로 유지보수가 훨씬 간편하고 오류가 발생할 이유도 사라진다.
Django의 URL 역방향 매핑
Django의 URL 매핑은 다음 두 가지 방향(양방향)으로 동작한다. 이 말은 Django의 URL 매핑 기능이 두 가지 주요 기능을 수행한다는 뜻이다. 조금 더 쉽게 풀어서 설명하자면, Django의 URL 매핑은 사용자가 요청한 URL을 처리하는 과정(정방향)과 코드에서 URL을 동적으로 생성하는 과정(역방향)이라는 두 가지 역할을 한다는 것이다.
1. 요청된 URL로 뷰 호출
사용자가 요청한 URL을 기반으로 urlpatterns에서 적합한 패턴을 찾아 해당 요청을 처리할 뷰를 호출한다. 이 과정에서 URL에 포함된 값은 뷰 함수에 필요한 인자로 전달된다.
from django.urls import path
from . import views
urlpatterns = [
path("articles/<int:year>/", views.article_archive, name="article-archive"),
]
# View
def article_archive(request, year):
return HttpResponse(f"Articles from year {year}")
URL /articles/2023/ 요청 시, Django는 views.article_archive(request, year=2023)을 호출한다.
2. 뷰 정보를 기반으로 URL 생성
reverse() 함수를 사용해 뷰 이름과 인자 값을 기반으로 URL을 동적으로 생성할 수 있다.
from django.urls import reverse
url = reverse("article-archive", args=[2023])
print(url) # 출력: /articles/2023/
위 코드에서 article-archive라는 이름의 뷰와 인자 값 2023을 사용해 /articles/2023/라는 URL을 생성한다.
각각의 방향은 서로 보완적이다. URL 역방향 매핑이라는 용어는 주로 두 번째 기능, 즉 뷰를 기준으로 URL을 생성하는 기능을 의미한다.
tools for performing URL reversing
Django는 URL 역방향 매핑(reverse resolution)을 다양한 방식으로 활용할 수 있도록 도구들을 제공한다.
- 템플릿에서 url 태그 사용
<a href="{% url 'news-year-archive' 2025 %}">2025 아카이브</a>
- Python 코드에서 reverse() 함수 사용
from django.http import HttpResponseRedirect
from django.urls import reverse
def redirect_to_year(request):
# ...
year = 2025
# ...
return HttpResponseRedirect(reverse("news-year-archive", args=(year,)))
- 모델 인스턴스의 URL 처리: 모델의 get_absolute_url() 메서드를 활용하여 객체와 연관된 URL을 반환할 수 있다.
Naming URL patterns
URL 네이밍은 Django에서 URL 역방향 매핑을 하기 위한 필수적인 요소이다. 충돌로 인한 오류를 방지하고 명확성을 높이기 위해 고유하고 직관적인 이름을 사용해야 한다. 이를 잘 활용한다면 유지보수성과 가독성이 높은 URL 설계를 할 수 있다.
URL namespaces
Django는 네임스페이스를 통해 URL 역방향 매핑을 잘 활용하도록 도와준다. 네임스페이스는 URL 이름 충돌을 방지하고, 동일한 애플리케이션을 여러 인스턴스로 배포할 수 있도록 도와준다. 네임스페이스를 지정하는 방식은 크게 2가지로 볼 수 있다.
1. 애플리케이션 네임스페이스 (Application Namespace)
애플리케이션 이름으로 설정되며, 애플리케이션 내에 공통적으로 적용된다.
# polls/urls.py
from django.urls import path
from . import views
app_name = "polls" # 애플리케이션 네임스페이스
urlpatterns = [
path("", views.index, name="index"),
path("<int:pk>/", views.detail, name="detail"),
]
2. 인스턴스 네임스페이스 (Instance Namespace)
특정 애플리케이션 인스턴스(프로젝트 레벨)로 구분한다. 하지만 기본적으로 인스턴스 네임스페이스는 애플리케이션 네임스페이스와 동일하게 설정된다.
# 프로젝트의 urls.py
from django.urls import include, path
urlpatterns = [
path("authors-polls/", include(("polls.urls", "polls"), namespace="authors-polls")),
path("publishers-polls/", include(("polls.urls", "polls"), namespace="publishers-polls")),
]
이렇게 네임스페이스가 포함된 URL을 역방향 매핑을 하려면 다음과 같이 활용하면 된다.
# 템플릿에서 사용
<a href="{% url 'polls:index' %}">설문조사로 이동</a>
이처럼 Django의 URL 디스패처는 유연하고, 웹 애플리케이션의 URL 관리를 단순하고 효율적으로 만들어준다. 공식 문서를 잘 읽고 URLconf가 주는 여러 기능들을 적절히 활용해서 유지보수하기 쉬운 웹 애플리케이션을 만들어 보면 좋을 것 같다.
추가로 이 글에서 다루진 않았지만 공식 문서에서는 URL과 관련된 예외가 발생하면 Django는 django.conf.urls.handler400, 403, 404, 500과 같은 오류 처리 뷰를 호출하는 것, include를 활용한 Including other URLconfs, View 함수에 Passing extra options과 같은 URL Dispatcher와 관련된 내용을 다루고 있다. 추가로 django.urls, django.conf.urls functions for use in URLconfs 공식문서를 본다면 URLconfs에서 사용되는 각각의 함수들을 더 잘 활용할 수 있을 것이니 한 번씩 읽어보는 걸 추천한다!!
'Django' 카테고리의 다른 글
[Django] Django Middleware (Middleware Custom 해보기) (0) | 2025.01.26 |
---|---|
[Django] Django 프로세스, Request-Response Lifecycle (Web Server, WSGI/ASGI, Middlewares, Django) (1) | 2025.01.24 |
[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 |