Django URL 라우팅 완전 이해 — urls.py 역할을 3가지 비유로 잡는 법

Django를 처음 배울 때 이런 설명을 본 적이 있을 것이다. “config/urls.py는 프로젝트 최상위 URL 라우팅 파일입니다.” 솔직히 처음엔 무슨 말인지 잘 모르겠었다. “URL 라우팅”이라는 단어 자체가 낯설었고, “최상위”가 뭘 의미하는지도 직관적으로 와닿지 않았다.

그런데 어느 순간 회사 대표 전화번호에 비유하니까 한 번에 이해됐다. Django의 URL 라우팅 구조는 생각보다 단순하다. 오늘은 코드보다 개념 중심으로, 이 구조를 완전히 소화해보자.

URL 요청이란 무엇인가 — 브라우저가 서버에 보내는 신호

URL 라우팅을 이해하려면 먼저 “URL 요청”이 뭔지 알아야 한다. 브라우저 주소창에 이걸 입력하는 순간을 떠올려보자.

https://deepcarpenter.com/django-url-view-template-structure-guide/

이 순간 브라우저는 서버에 이런 신호를 보낸다. “deepcarpenter.com 서버야, /django-url-view-template-structure-guide/ 페이지 줘봐.” 이게 URL 요청이다. 사용자가 주소를 입력하거나, 링크를 클릭하거나, 버튼을 누를 때마다 이 신호가 발생한다.

URL 요청은 목적에 따라 종류가 나뉜다.

종류언제 발생?예시
GET페이지 조회블로그 글 읽기
POST데이터 전송로그인, 댓글 작성
PUT데이터 수정글 수정
DELETE데이터 삭제글 삭제

가장 자주 접하는 건 GET이다. 브라우저 주소창에 URL을 입력하는 행위 자체가 GET 요청이다.

urls.py가 하는 일 — 회사 대표 전화번호의 비유

이제 핵심이다. Django에서 urls.py는 정확히 무슨 역할을 할까.

회사 대표 전화번호를 떠올려보자.

고객이 대표번호로 전화
        ↓
교환원: "영업 문의세요? → 영업부 연결해드릴게요"
교환원: "AS 문의세요?  → 고객센터 연결해드릴게요"
교환원: "결제 문의세요? → 회계팀 연결해드릴게요"

여기서 대표 전화번호 = urls.py다. 모든 외부 연락은 반드시 이 번호를 거친다. 대표 전화를 건너뛰고 특정 부서로 바로 연결되는 일은 없다. urls.py도 마찬가지다. 사용자의 모든 URL 요청은 반드시 이 파일을 먼저 통과한다.

그래서 이 파일을 “전체 URL 진입점”이라고 부른다. “전체”의 의미는 도메인(deepcarpenter.com 같은 주소)을 가리키는 게 아니다. 프로젝트 안에 있는 모든 URL 라우팅을 이 파일이 총괄한다는 뜻이다.

URL 라우팅이란 — 교환원이 부서를 연결하는 행위

**URL 라우팅(Routing)**은 “요청을 올바른 목적지로 길 안내하는 것”이다.

대표 전화 비유로 다시 보면, 교환원이 고객의 용건을 듣고 해당 부서로 연결하는 행위 자체가 URL 라우팅이다. Django 흐름으로 옮겨보면 이렇다.

사용자 요청: /blog/post/1/
        ↓
config/urls.py (진입점에서 URL 라우팅 판단)
"blog/로 시작하면 blog 앱 urls.py로 넘겨"
        ↓
blog/urls.py → PostDetailView 실행
        ↓
해당 페이지 응답 반환

여기서 두 개념이 명확히 구분된다.

용어의미대표전화 비유
진입점요청이 처음 도착하는 곳대표 전화번호
URL 라우팅어디로 보낼지 결정하는 행위교환원이 부서 연결
config/urls.py진입점 + 라우팅을 모두 담당대표 전화 + 교환원

Django URL 라우팅 — urls.py 역할과 진입점 구조 설명

config/urls.py가 “최상위”인 이유

Django 프로젝트에는 urls.py가 여러 개 존재할 수 있다.

myproject/
  ├── config/
  │     └── urls.py  ← 최상위 (프로젝트 레벨)
  ├── blog/
  │     └── urls.py  ← 앱 레벨
  └── shop/
        └── urls.py  ← 앱 레벨

최상위란 계층 구조에서 가장 위에 있다는 뜻이다. 대표 전화를 거쳐야 각 부서로 연결되듯이, config/urls.py를 거쳐야 각 앱의 urls.py로 도달할 수 있다. Django는 settings.py에서 이 파일을 공식 진입점으로 지정한다.

# settings.py
ROOT_URLCONF = 'config.urls'  # 이 파일이 진입점이야

이 한 줄 덕분에 Django는 요청이 들어오면 무조건 config/urls.py부터 확인한다. URL 라우팅의 시작점이 여기서 고정되는 것이다.

전체 흐름을 한 번에 정리하면

URL 라우팅의 전체 흐름을 단계별로 보면 이렇다.

단계내용
사용자가 URL 입력
브라우저가 서버로 URL 요청 전송
Django가 요청 수신
config/urls.py (진입점)에서 URL 라우팅 판단
해당 앱의 urls.py로 전달
뷰(View) 실행 → 페이지 응답 반환

어렵게 느껴졌던 “전체 URL 진입점”과 “URL 라우팅”이 사실은 이 흐름 안에서 각자의 역할을 하고 있을 뿐이다.

핵심 요약

  • URL 요청 = 브라우저가 서버에 “이 페이지 줘봐”라고 보내는 신호
  • urls.py = 모든 URL 요청이 처음 도착하는 전체 진입점 (대표 전화번호)
  • URL라우팅 = 도착한 요청을 올바른 앱으로 연결하는 행위 (교환원의 역할)
  • config/urls.py = 진입점 + URL 라우팅을 동시에 담당하는 프로젝트 최상위 파일
  • Django를 처음 배울 때 이 구조를 확실히 잡아두면 View·Template 흐름도 수월하게 이어진다

[링크 제안]

URL라우팅 개념이 잡혔다면, 요청이 View와 Template으로 이어지는 전체 Django 구조를 함께 확인해두자.

include()로 앱별 URL을 연결하는 실제 설정 방법은 이 글에서 바로 이어진다.

엔지니어라면 한 번쯤 — 영화 F1이 주는 Inspiration

FAQ

아니다. Django 프로젝트에는 config/urls.py(최상위)와 각 앱별 urls.py가 따로 존재한다. 최상위 파일이 URL라우팅의 진입점 역할을 하고, 앱별 파일이 세부 라우팅을 담당한다.

Django가 어느 파일이 진입점인지 알 수 없어 모든 URL 요청이 오류를 반환한다. 프로젝트 생성 시 자동으로 설정되므로 실제로 빠뜨리는 경우는 드물지만, 이 설정이 URL라우팅 전체의 시작점이라는 사실은 알아두자.

앱별 urls.py를 만들고도 config/urls.py에 include()로 등록하지 않는 경우다. 대표 전화에 부서 내선 번호를 등록하지 않은 것과 같다. 아무리 앱 내부 URL 라우팅을 잘 짜도 진입점에 연결이 안 되면 페이지가 열리지 않는다.

관련 글 보기