# Django 템플릿에서 URL 꺼내기: `request.path` vs `path_info` vs `get_full_path` vs `build_absolute_uri` Django 템플릿에서 현재 URL이 필요할 때가 많습니다. * 네비게이션 “현재 메뉴 활성화” * 로그인 후 “원래 보던 페이지로 돌아가기(next)” * canonical URL / 공유 링크 만들기 * 쿼리스트링 포함해서 “현재 페이지 그대로” 링크 만들기 그런데 `{{ request.path }}` 말고도 비슷한 애들이 여럿이라 헷갈리죠. 오늘은 헷갈리기 쉬운 아래의 4개만 깔끔하게 비교합니다. * `request.path` * `request.path_info` * `request.get_full_path()` * `request.build_absolute_uri()` --- ## `{{ request }}`는 대체 어디서 오는가 {#sec-e3874691b2fc} 템플릿에서 `{{ request }}`가 보인다는 건, 보통 **request context processor**가 켜져 있다는 뜻입니다. * `django.template.context_processors.request`가 **템플릿 컨텍스트에 `request`를 넣어주는 역할**을 합니다. * 이 context processor는 `TEMPLATES` 설정의 `OPTIONS['context_processors']`에 등록될 때 동작합니다. 즉, Django는 settings.TEMPLATES 에 request를 글로벌 컨텍스트로 넣어주도록 기본으로 설정되어있기 때문에 개발자가 고의로 이 설정을 지우지 않는 이상 Django 사용자는 아무런 불편함없이 당연하듯 사용하게 됩니다. 그리고 `request` 객체 자체는 Django가 요청을 받을 때 `HttpRequest`(실제로는 WSGI/ASGI request subclass)를 만들어서 뷰까지 넘겨주는 그 객체입니다. 이 객체에 `path`, `path_info` 같은 속성이 들어있어요. ![Django라는 이름의 마술사가 request모자에서 도구를 꺼내는 이미지](/media/editor_temp/6/c112847e-a9ca-4d58-9a3f-076a20bfbef7.png) --- ## 한 줄 결론: 4개를 이렇게 외우면 편하다 {#sec-0f722c9bd67c} * **`path`**: “도메인 제외, 경로만” * **`path_info`**: “앱이 보는 진짜 경로(스크립트 prefix 제외)” * **`get_full_path()`**: “`path` + 쿼리스트링” * **`build_absolute_uri()`**: “스킴+호스트+경로까지 전부(완전한 URL)” 이제 각각을 정확히 보죠. --- ## 1) `request.path` : 도메인 없는 “경로(path)” {#sec-26a629a2d24e} `request.path`는 **도메인/스킴 없이** 요청 경로만 줍니다. 예시는 이런 형태예요. * `/music/bands/the_beatles/` ### 언제 유용한가 {#sec-b307f735685f} * 메뉴 활성화 같은 단순 비교 ```django {% if request.path == "/settings/" %}active{% endif %} ``` * “이 페이지는 어디 카테고리?” 같이 prefix 비교 ```django {% if request.path|slice:":5" == "/api/" %}...{% endif %} ``` --- ## 2) `request.path_info` : 배포환경에 덜 흔들리는 “실제 경로” {#sec-fd1e8e14948f} Django 문서에서 핵심 포인트는 이거예요. * 어떤 서버 설정에서는 URL 경로가 **스크립트 prefix(SCRIPT_NAME)** 와 **path info** 로 나뉘는데, * `path_info`는 그중 **path info 부분을 항상 담는다** (즉, 환경에 덜 의존) 쉽게 말해, 앱이 `/app` 같은 prefix 아래에 마운트되는 구성(리버스 프록시, 서브패스 배포 등)에서 `path`와 `path_info`가 달라질 수 있습니다. ### 언제 유용한가 {#sec-2985f0c9cd7c} * 서브패스 배포(예: `/app/` 아래로 서비스) 같은 환경을 고려해 “앱 기준 경로”로 판단하고 싶을 때 * 테스트/운영 서버에서 prefix가 달라질 수 있을 때 > 평소 단일 도메인 루트(`/`)에서만 돌면 `path`와 `path_info`가 같아서 차이를 못 느끼는 경우가 많습니다. > 하지만 환경이 바뀌면 그때부터 차이가 의미 있어집니다. --- ## 3) `request.get_full_path()` : `path` + 쿼리스트링까지 {#sec-1364e4e65388} `get_full_path()`는 **`path`에 쿼리스트링을 붙인 값**을 반환합니다. 예시는 이런 형태입니다. * `/music/bands/the_beatles/?print=true` ### 언제 유용한가 {#sec-0ac89fe1b7cd} * “현재 페이지 그대로 공유/리프레시/되돌아가기” 링크가 필요할 때 ```django 새로고침 링크 ``` * 로그인/권한 체크 후 원래 페이지로 돌아가게 `next` 만들 때 ```django 로그인 ``` > 참고로 Django에는 `get_full_path()` 말고 `get_full_path_info()`도 있는데, > 후자는 `path_info` 기반으로 동작합니다. (오늘 비교 대상은 아니지만 차이를 알고 있으면 좋습니다.) --- ## 4) `request.build_absolute_uri()` : 스킴+도메인까지 포함한 “완전한 URL” {#sec-0ba80b4c4700} `build_absolute_uri()`는 현재 요청을 기준으로 **절대 URL(absolute URI)** 을 만들어 줍니다. 대충 이런 형태가 됩니다. * `https://example.com/music/bands/the_beatles/?print=true` ### 언제 유용한가 {#sec-428d3d6b9613} * 이메일/메신저 공유 링크처럼 **도메인이 반드시 필요한 경우** * canonical URL, og:url 같은 메타 태그 * 외부 시스템에 콜백 URL 전달 ### 주의해야할 점 {#sec-b24dccc132d6} `build_absolute_uri()`는 호스트를 만들 때 요청의 Host 정보에 의존합니다(내부적으로 `get_host()`를 사용). **실제 요청이 들어온 url이 아닙니다.** 대부분의 평범한 상황에서는 get_host()의 값과 실제 url도메인이 일치할 수도 있지만, Nginx 가 리버스 프록시 혹은 Django 미들웨어에서 개발자가 의도적으로 get_host() 값고 다른 tenant host를 사용하도록 로직을 만들 경우, `{{ request.build_absolute_uri }} ` 의 값이 항상 실제 브라우저의 url과 일치한다고 볼 수는 없습니다. 따라서, nginx든 Django앱 내부에서든 어떠한 도메인관련 로직을 구현하셨다면, 예상과 다른 도메인/스킴으로 만들어질 수 있으니 배포 설정도 같이 점검하는 게 좋습니다. --- ## 결론 및 요약 {#sec-bd7ea169a316} * **템플릿에서 메뉴 active 처리** → `request.path` * **서브패스 배포/프록시 환경까지 안정적으로 비교** → `request.path_info` * **쿼리스트링까지 포함해서 “현재 페이지 그대로”** → `request.get_full_path()` * **외부로 나가는 완전한 링크(도메인 포함)** → `request.build_absolute_uri()` --- **관련글 보기**: - [리버스 프록시란? 포워드 프록시와의 차이, 목적, 사용 시나리오 한눈에 정리](/ko/whitedec/2025/12/10/reverse-proxy-forward-proxy-differences/) - [Django를 처음부터 다시 배운다면: HTTP에서 시작하는 학습 로드맵](/ko/whitedec/2025/12/22/django-first-learning-roadmap/)