{"title":"첫 번째 장고 앱 작성하기, part 3","version":"4.1","locale":"ko","docname":"intro/tutorial03","url":"/ko/4.1/intro/tutorial03/","canonical":"https://djangodocs.dev/ko/4.1/intro/tutorial03/","summary":"이 튜토리얼은 튜토리얼 2장 에서 이어집니다. 이제 Web-poll 애플리케이션에 공용 인터페이스인 “views”를 만드는 데 초점을 맞출 것입니다. 도움을 받을 수 있는 방법 이 튜토리얼을 수행하는 데 문제가 있는 경우 FAQ 섹션인 :doc: ` Getting Help…","html":"<h1>첫 번째 장고 앱 작성하기, part 3<a class=\"heading-anchor\" href=\"#writing-your-first-django-app-part-3\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>이 튜토리얼은 <a class=\"reference internal\" href=\"/ko/4.1/intro/tutorial02/\"><span class=\"doc\">튜토리얼 2장</span></a> 에서 이어집니다. 이제 Web-poll 애플리케이션에 공용 인터페이스인 “views”를 만드는 데 초점을 맞출 것입니다.</p>\n<aside class=\"admonition-where-to-get-help admonition\">\n<p class=\"admonition-title\">도움을 받을 수 있는 방법</p>\n<p>이 튜토리얼을 수행하는 데 문제가 있는 경우 FAQ 섹션인 :doc:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>Getting Help &lt;/faq/help&gt;`으로 이동하십시오.</p>\n</aside>\n<section id=\"overview\">\n<h2>개요<a class=\"heading-anchor\" href=\"#overview\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>view는 일반적으로 특정한 기능을 제공하고 특정한 템플릿을 가진 Django 애플리케이션에 있는 웹 페이지의 “type”입니다. 예를 들어, 블로그 애플리케이션에서 다음과 같은 view를 가질 수 있습니다.</p>\n<ul class=\"simple\">\n<li><p>Blog 홈페이지 – 가장 최근의 항목들을 보여줍니다</p></li>\n<li><p>항목 “세부”(detail) 페이지 – 하나의 항목에 연결하는 영구적인 링크(permalink)를 제공합니다.</p></li>\n<li><p>년도별 축적 페이지 – 주어진 연도의 모든 월별 항목들을 표시합니다.</p></li>\n<li><p>월별 축적 페이지 – 주어진 월의 날짜별 항목들을 표시합니다.</p></li>\n<li><p>날짜별 축적 페이지 – 주어진 날짜의 모든 항목들을 표시합니다.</p></li>\n<li><p>댓글 기능 – 특정 항목의 댓글을 다룰 수 있는 기능</p></li>\n</ul>\n<p>우리가 만드는 poll 어플리케이션에서 다음과 같은 네개의 view 를 만들어 보겠습니다.</p>\n<ul class=\"simple\">\n<li><p>질문 “색인” 페이지 – 최근의 질문들을 표시합니다.</p></li>\n<li><p>질문 “세부” 페이지 – 질문 내용과, 투표할 수 있는 서식을 표시합니다.</p></li>\n<li><p>질문 “결과” 페이지 – 특정 질문에 대한 결과를 표시합니다</p></li>\n<li><p>투표 기능 – 특정 질문에 대해 특정 선택을 할 수 있는 투표 기능을 제공합니다.</p></li>\n</ul>\n<p>Django에서는 웹 페이지와 기타 콘텐츠가 view로 전달됩니다. 각 view는 Python 함수(또는 클래스 기반 view의 경우 메소드)로 표현됩니다. Django는 요청한 URL(더 정확하게 말하면 도메인 이름 뒤의 URL 부분)을 확인하여 보기를 선택합니다.</p>\n<p>지금 웹 상에서 여러분은 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>ME2/Sites/dirmod.htm?sid=&amp;type=gen&amp;mod=Core+Pages&amp;gid=A6CD4967199A42D9B65B1B``와 같은 아름다움을 접했을 것입니다. Django는 우리에게 그것보다 훨씬 더 우아한 <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">*</span></a>URL 패턴*을 허용해줍니다.</p>\n<p>URL 패턴은 URL의 일반적인 형식입니다(예: <code class=\"docutils literal notranslate\"><span class=\"pre\">/newsarchive/&lt;year&gt;/&lt;month&gt;/</span></code>).</p>\n<p>URL로부터 뷰를 얻기 위해, Django는 ‘URLconfs’라는 것을 사용합니다. URLconf는 URL 패턴을 뷰에 연결합니다.</p>\n<p>이 튜토리얼은 URLconfs를 사용하는 기초 지식을 제공하며, 좀 더 자세한 정보는 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/urls/\"><span class=\"doc\">URL dispatcher</span></a>를 참조하세요.</p>\n</section>\n<section id=\"writing-more-views\">\n<h2>뷰 추가하기<a class=\"heading-anchor\" href=\"#writing-more-views\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>이제, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code> 에 뷰를 추가해 봅시다. 이 뷰들은 인수를 받기 때문에 조금 모양이 다릅니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"s2\">&quot;You&#39;re looking at question </span><span class=\"si\">%s</span><span class=\"s2\">.&quot;</span> <span class=\"o\">%</span> <span class=\"n\">question_id</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">results</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;You&#39;re looking at the results of question </span><span class=\"si\">%s</span><span class=\"s2\">.&quot;</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"n\">response</span> <span class=\"o\">%</span> <span class=\"n\">question_id</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">vote</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"s2\">&quot;You&#39;re voting on question </span><span class=\"si\">%s</span><span class=\"s2\">.&quot;</span> <span class=\"o\">%</span> <span class=\"n\">question_id</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>다음의 <a class=\"reference internal\" href=\"/ko/4.1/ref/urls/#django.urls.path\" title=\"django.urls.path\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">path()</span></code></a> 호출을 추가하여 이러한 새로운 뷰를 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls.urls</span></code> 모듈로 연결하세요.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">path</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">views</span>\n\n<span class=\"n\">urlpatterns</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"c1\"># ex: /polls/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">index</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;index&#39;</span><span class=\"p\">),</span>\n    <span class=\"c1\"># ex: /polls/5/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;detail&#39;</span><span class=\"p\">),</span>\n    <span class=\"c1\"># ex: /polls/5/results/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/results/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">results</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;results&#39;</span><span class=\"p\">),</span>\n    <span class=\"c1\"># ex: /polls/5/vote/</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/vote/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">vote</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;vote&#39;</span><span class=\"p\">),</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>브라우저에 “/polls/34/” 를 입력해 보세요. 이 주소에 접속하면 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 함수를 호출하여 URL 에 입력한 ID 를 출력할 것입니다. “/polls/34/results/” 와 “/polls/34/vote/” 도 실행해 보세요. 투표 결과와 투표 페이지의 뼈대가 되는 페이지가 출력될 것입니다.</p>\n<p>사용자가 웹사이트의 페이지를 요청할 때, 예로 “/polls/34/”를 요청했다고 하면, Django는 <code class=\"docutils literal notranslate\"><span class=\"pre\">mysite.urls</span></code> 파이썬 모듈을 불러오게 됩니다. <a class=\"reference internal\" href=\"/ko/4.1/ref/settings/#std-setting-ROOT_URLCONF\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">ROOT_URLCONF</span></code></a> 설정에 의해 해당 모듈을 바라보도록 지정되어 있기 때문입니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">mysite.urls</span></code>에서 <code class=\"docutils literal notranslate\"><span class=\"pre\">urlpatterns</span></code>라는 변수를 찾고, 순서대로 패턴을 따라갑니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">'polls/'</span></code>를 찾은 후엔, 일치하는 텍스트(<code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;polls/&quot;</span></code>)를 버리고, 남은 텍스트인 <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;34/&quot;</span></code>를 ‘polls.urls’ URLconf로 전달하여 남은 처리를 진행합니다. 거기에 <code class=\"docutils literal notranslate\"><span class=\"pre\">'&lt;int:question_id&gt;/'</span></code>와 일치하여, 결과적으로 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 뷰 함수가 호출됩니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"o\">=&lt;</span><span class=\"n\">HttpRequest</span> <span class=\"nb\">object</span><span class=\"o\">&gt;</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"o\">=</span><span class=\"mi\">34</span><span class=\"p\">)</span>\n</code></pre></div>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">question_id=34</span></code> 부분은 <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;int:question_id&gt;</span></code> 에서 왔습니다. 꺾쇠 괄호를 사용하면 URL의 일부가 “캡처”되어 키워드 인수로 view 함수에 전송됩니다. 문자열의 <code class=\"docutils literal notranslate\"><span class=\"pre\">question_id</span></code> 부분은 일치하는 패턴을 식별하는 데 사용할 이름을 정의하고, <code class=\"docutils literal notranslate\"><span class=\"pre\">int</span></code> 부분은 URL 경로의 이 부분과 일치하는 패턴을 결정하는 변환기입니다. 콜론(<code class=\"docutils literal notranslate\"><span class=\"pre\">:</span></code>)은 컨버터와 패턴 이름을 구분합니다.</p>\n</section>\n<section id=\"write-views-that-actually-do-something\">\n<h2>뷰가 실제로 뭔가를 하도록 만들기<a class=\"heading-anchor\" href=\"#write-views-that-actually-do-something\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>각 뷰는 두 가지 중 하나를 하도록 되어 있습니다. 요청된 페이지의 내용이 담긴 <a class=\"reference internal\" href=\"/ko/4.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> 객체를 반환하거나, 혹은 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 같은 예외를 발생하게 해야합니다. 나머지는 당신에게 달렸습니다.</p>\n<p>당신이 작성한 뷰는 데이터베이스의 레코드를 읽을 수도 있습니다. 또한 뷰는 Django나 Python에서 서드파티로 제공되는 템플릿 시스템을 사용할 수도 있습니다. 뷰는 PDF를 생성하거나, XML을 출력하거나, 실시간으로 ZIP 파일을 만들 수 있습니다. 뷰는 당신이 원하는 무엇이든, Python의 어떤 라이브러리라도 사용할 수 있습니다.</p>\n<p>Django에 필요한 것은 <a class=\"reference internal\" href=\"/ko/4.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> 객체 혹은 예외입니다.</p>\n<p>왜냐면, 그렇게 다루는게 편리하기 때문입니다. <a class=\"reference internal\" href=\"/ko/4.1/intro/tutorial02/\"><span class=\"doc\">튜토리얼 2장</span></a>의 예제에서 다룬 Django 자체 데이터베이스 API를 사용해봅시다. 새로운 <code class=\"docutils literal notranslate\"><span class=\"pre\">index()</span></code> 뷰 하나를 호출했을 때, 시스템에 저장된 최소한 5 개의 투표 질문이 콤마로 분리되어, 발행일에 따라 출력됩니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">HttpResponse</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">index</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"n\">latest_question_list</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n    <span class=\"n\">output</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;, &#39;</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">([</span><span class=\"n\">q</span><span class=\"o\">.</span><span class=\"n\">question_text</span> <span class=\"k\">for</span> <span class=\"n\">q</span> <span class=\"ow\">in</span> <span class=\"n\">latest_question_list</span><span class=\"p\">])</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"n\">output</span><span class=\"p\">)</span>\n\n<span class=\"c1\"># Leave the rest of the views (detail, results, vote) unchanged</span>\n</code></pre></figure>\n<p>여기 몇가지 문제가 있습니다. 뷰에서 페이지의 디자인이 하드코딩 되어 있다고 합시다. 만약 페이지가 보여지는 방식을 바꾸고 싶다면, 이 Python 코드를 편집해야만 할 겁니다. 그럼, 뷰에서 사용할 수 있는 템플릿을 작성하여, Python 코드로부터 디자인을 분리하도록 Django의 템플릿 시스템을 사용해 봅시다.</p>\n<p>우선, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 디렉토리에 <code class=\"docutils literal notranslate\"><span class=\"pre\">templates</span></code>라는 디렉토리를 만듭니다. Django는 여기서 템플릿을 찾게 될 것입니다.</p>\n<p>프로젝트의 <a class=\"reference internal\" href=\"/ko/4.1/ref/settings/#std-setting-TEMPLATES\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">TEMPLATES</span></code></a> 설정은 Django가 어떻게 템플릿을 불러오고 렌더링 할 것인지 기술합니다. 기본 설정 파일은 <a class=\"reference internal\" href=\"/ko/4.1/ref/settings/#std-setting-TEMPLATES-APP_DIRS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">APP_DIRS</span></code></a> 옵션이 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>로 설정된 <code class=\"docutils literal notranslate\"><span class=\"pre\">DjangoTemplates</span></code> 백엔드를 구성합니다. 관례에 따라, <code class=\"docutils literal notranslate\"><span class=\"pre\">DjangoTemplates</span></code>은 각 <a class=\"reference internal\" href=\"/ko/4.1/ref/settings/#std-setting-INSTALLED_APPS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">INSTALLED_APPS</span></code></a> 디렉토리의 “templates” 하위 디렉토리를 탐색합니다.</p>\n<p>방금 만든 <code class=\"docutils literal notranslate\"><span class=\"pre\">templates</span></code> 디렉터리 내에 <code class=\"docutils literal notranslate\"><span class=\"pre\">templates</span></code> 라는 다른 디렉터리를 만들고 그 안에 <code class=\"docutils literal notranslate\"><span class=\"pre\">index.html</span></code> 이라는 파일을 만듭니다. 즉, 템플릿은 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html``이어야</span> <span class=\"pre\">합니다.</span> <span class=\"pre\">위에서</span> <span class=\"pre\">설명한</span> <span class=\"pre\">것처럼</span> <span class=\"pre\">``app_directories</span></code> 템플릿 로더가 작동하는 방식 때문에 Django 내에 있는 이 템플릿을 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>polls/index.html``로 지칭할 수 있습니다.</p>\n<aside class=\"admonition-template-namespacing admonition\">\n<p class=\"admonition-title\">템플릿 네임스페이싱</p>\n<p>이제 우리는 템플릿을 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates</span></code> 에 직접 넣는 것에서 벗어날 수 <em>있을지도 모르지만</em> (또 다른 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 하위 디렉토리를 만드는 것보다는) 실제로는 좋은 생각이 아닙니다. Django는 이름이 일치하는 첫 번째 템플릿을 선택하는데 만약 <em>다른</em> 응용 프로그램에 같은 이름의 템플릿이 있으면 Django는 이러한 템플릿을 구별할 수 없습니다. Django에게 정확한 템플릿을 지정하기 위해서 가장 편리한 방법은 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">*</span></a>이름공간*으로 구분짓는 것입니다. 이것은 애플리케이션의 이름으로 된 디렉토리에 이러한 템플릿들을 넣으면 됩니다.</p>\n</aside>\n<p>템플릿에 다음과 같은 코드를 입력합니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"cp\">{%</span> <span class=\"k\">if</span> <span class=\"nv\">latest_question_list</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">for</span> <span class=\"nv\">question</span> <span class=\"k\">in</span> <span class=\"nv\">latest_question_list</span> <span class=\"cp\">%}</span>\n        <span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;/polls/</span><span class=\"cp\">{{</span> <span class=\"nv\">question.id</span> <span class=\"cp\">}}</span><span class=\"s\">/&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">endfor</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;/</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">else</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">p</span><span class=\"p\">&gt;</span>No polls are available.<span class=\"p\">&lt;/</span><span class=\"nt\">p</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endif</span> <span class=\"cp\">%}</span>\n</code></pre></figure>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">참고</p>\n<p>튜토리얼을 짧게 하기 위해 모든 템플릿 예제가 불완전한 HTML을 사용합니다. 자신의 프로젝트에서는 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>complete HTML documents`__를 사용해야 합니다.</p>\n</aside>\n<p>이제, 템플릿을 이용하여 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code>에 <code class=\"docutils literal notranslate\"><span class=\"pre\">index</span></code> 뷰를 업데이트 해보도록 하겠습니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">HttpResponse</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.template</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">loader</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">index</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"n\">latest_question_list</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n    <span class=\"n\">template</span> <span class=\"o\">=</span> <span class=\"n\">loader</span><span class=\"o\">.</span><span class=\"n\">get_template</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls/index.html&#39;</span><span class=\"p\">)</span>\n    <span class=\"n\">context</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n        <span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">:</span> <span class=\"n\">latest_question_list</span><span class=\"p\">,</span>\n    <span class=\"p\">}</span>\n    <span class=\"k\">return</span> <span class=\"n\">HttpResponse</span><span class=\"p\">(</span><span class=\"n\">template</span><span class=\"o\">.</span><span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">context</span><span class=\"p\">,</span> <span class=\"n\">request</span><span class=\"p\">))</span>\n</code></pre></figure>\n<p>이 코드는 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code> 템플릿을 불러온 후, context를 전달합니다. context는 템플릿에서 쓰이는 변수명과 Python 객체를 연결하는 사전형 값입니다.</p>\n<p>브라우저에서 “/polls/” 페이지를 불러오면, <a class=\"reference internal\" href=\"/ko/4.1/intro/tutorial02/\"><span class=\"doc\">튜토리얼 2장</span></a>에서 작성한 “What’s up” 질문이 포함된 리스트가 표시됩니다. 표시된 질문의 링크는 해당 질문에 대한 세부 페이지를 가리킵니다.</p>\n<section id=\"a-shortcut-render\">\n<h3>지름길: <a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.render\" title=\"django.shortcuts.render\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">render()</span></code></a><a class=\"heading-anchor\" href=\"#a-shortcut-render\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>템플릿에 context 를 채워넣어 표현한 결과를 <a class=\"reference internal\" href=\"/ko/4.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> 객체와 함께 돌려주는 구문은 자주 쓰는 용법입니다. 따라서 Django는 이런 표현을 쉽게 표현할 수 있도록 단축 기능(shortcuts)을 제공합니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">index()</span></code> 뷰를 단축 기능으로 작성하면 다음과 같습니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">render</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">index</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"n\">latest_question_list</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n    <span class=\"n\">context</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">:</span> <span class=\"n\">latest_question_list</span><span class=\"p\">}</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s1\">&#39;polls/index.html&#39;</span><span class=\"p\">,</span> <span class=\"n\">context</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>모든 뷰에 적용한다면, 더 이상 <a class=\"reference internal\" href=\"/ko/4.1/topics/templates/#module-django.template.loader\" title=\"django.template.loader\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">loader</span></code></a>와 <a class=\"reference internal\" href=\"/ko/4.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a>를 임포트하지 않아도 됩니다. (만약 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">results</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">vote</span></code>에서 stub 메소드를 가지고 있다면, <code class=\"docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code>를 유지해야 할 것입니다.)</p>\n<p><a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.render\" title=\"django.shortcuts.render\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">render()</span></code></a> 함수는 request 객체를 첫번째 인수로 받고, 템플릿 이름을 두번째 인수로 받으며, context 사전형 객체를 세전째 선택적(optional) 인수로 받습니다. 인수로 지정된 context로 표현된 템플릿의 <a class=\"reference internal\" href=\"/ko/4.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> 객체가 반환됩니다.</p>\n</section>\n</section>\n<section id=\"raising-a-404-error\">\n<h2>404 에러 일으키기<a class=\"heading-anchor\" href=\"#raising-a-404-error\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>이제, 질문의 상세 뷰에 태클을 걸어보겠습니다. 상세 뷰는 지정된 설문조사의 질문 내용을 보여줍니다. 다음과 같습니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Http404</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">render</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n<span class=\"c1\"># ...</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"k\">try</span><span class=\"p\">:</span>\n        <span class=\"n\">question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">question_id</span><span class=\"p\">)</span>\n    <span class=\"k\">except</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">DoesNotExist</span><span class=\"p\">:</span>\n        <span class=\"k\">raise</span> <span class=\"n\">Http404</span><span class=\"p\">(</span><span class=\"s2\">&quot;Question does not exist&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s1\">&#39;polls/detail.html&#39;</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"s1\">&#39;question&#39;</span><span class=\"p\">:</span> <span class=\"n\">question</span><span class=\"p\">})</span>\n</code></pre></figure>\n<p>여기 새로운 내용이 추가되었습니다. 뷰는 요청된 질문의 ID 가 없을 경우 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 예외를 발생시킵니다.</p>\n<p>조금 후에 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/detail.html</span></code> 템플릿에 무엇을 넣을 수 있는지 논의하겠지만, 일단 위의 예제를 동작시키기 위해 아래의 내용이 들어있는 파일을 작성하세요.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/detail.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"cp\">{{</span> <span class=\"nv\">question</span> <span class=\"cp\">}}</span>\n</code></pre></figure>\n<p>이제 시작해도 됩니다.</p>\n<section id=\"a-shortcut-get-object-or-404\">\n<h3>지름길: <a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a><a class=\"heading-anchor\" href=\"#a-shortcut-get-object-or-404\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>만약 객체가 존재하지 않을 때 <a class=\"reference internal\" href=\"/ko/4.1/ref/models/querysets/#django.db.models.query.QuerySet.get\" title=\"django.db.models.query.QuerySet.get\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get()</span></code></a> 을 사용하여 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 예외를 발생시키는것은 자주 쓰이는 용법입니다. Django에서 이 기능에 대한 단축 기능을 제공합니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 뷰를 단축 기능으로 작성하면 다음과 같습니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">,</span> <span class=\"n\">render</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n<span class=\"c1\"># ...</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">detail</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"n\">question</span> <span class=\"o\">=</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">(</span><span class=\"n\">Question</span><span class=\"p\">,</span> <span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">question_id</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s1\">&#39;polls/detail.html&#39;</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"s1\">&#39;question&#39;</span><span class=\"p\">:</span> <span class=\"n\">question</span><span class=\"p\">})</span>\n</code></pre></figure>\n<p><a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a> 함수는 Django 모델을 첫번째 인자로 받고, 몇개의 키워드 인수를 모델 관리자의 <a class=\"reference internal\" href=\"/ko/4.1/ref/models/querysets/#django.db.models.query.QuerySet.get\" title=\"django.db.models.query.QuerySet.get\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get()</span></code></a> 함수에 넘깁니다. 만약 객체가 존재하지 않을 경우, <a class=\"reference internal\" href=\"/ko/4.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 예외가 발생합니다.</p>\n<aside class=\"admonition-philosophy admonition\">\n<p class=\"admonition-title\">철학</p>\n<p>상위 계층에서 <a class=\"reference internal\" href=\"/ko/4.1/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist\" title=\"django.core.exceptions.ObjectDoesNotExist\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ObjectDoesNotExist</span></code></a> 예외를 자동으로 잡아 내는 대신 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a> 도움 함수(helper functoin)를 사용하거나, <a class=\"reference internal\" href=\"/ko/4.1/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist\" title=\"django.core.exceptions.ObjectDoesNotExist\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ObjectDoesNotExist</span></code></a> 예외를 사용하는 대신 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 를 사용하는 이유는 무엇일까요?</p>\n<p>왜냐하면, 모델 계층을 뷰 계층에 연결하는 방법이기 때문입니다. Django의 중요한 설계 목표는, 약결합(loose coupling)을 관리하는 데에 있습니다. 일부 제어된 결합이 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#module-django.shortcuts\" title=\"django.shortcuts: Convenience shortcuts that span multiple levels of Django's MVC stack.\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">django.shortcuts</span></code></a> 모듈에서 도입되었습니다.</p>\n</aside>\n<p>또한, <a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.get_object_or_404\" title=\"django.shortcuts.get_object_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_object_or_404()</span></code></a> 함수처럼 동작하는 <a class=\"reference internal\" href=\"/ko/4.1/topics/http/shortcuts/#django.shortcuts.get_list_or_404\" title=\"django.shortcuts.get_list_or_404\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">get_list_or_404()</span></code></a> 함수가 있습니다. <a class=\"reference internal\" href=\"/ko/4.1/ref/models/querysets/#django.db.models.query.QuerySet.get\" title=\"django.db.models.query.QuerySet.get\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">get()</span></code></a> 대신 <a class=\"reference internal\" href=\"/ko/4.1/ref/models/querysets/#django.db.models.query.QuerySet.filter\" title=\"django.db.models.query.QuerySet.filter\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">filter()</span></code></a> 를 쓴다는 것이 다릅니다. 리스트가 비어있을 경우, <a class=\"reference internal\" href=\"/ko/4.1/topics/http/views/#django.http.Http404\" title=\"django.http.Http404\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">Http404</span></code></a> 예외를 발생시킵니다.</p>\n</section>\n</section>\n<section id=\"use-the-template-system\">\n<h2>템플릿 시스템 사용하기<a class=\"heading-anchor\" href=\"#use-the-template-system\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>투표 어플리케이션의 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> 뷰로 되돌아가 봅시다. context 변수 <code class=\"docutils literal notranslate\"><span class=\"pre\">question</span></code>이 주어졌을때, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/detail.html</span></code>이라는 템플릿이 어떻게 보이는지 봅시다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/detail.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span>\n<span class=\"p\">&lt;</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">for</span> <span class=\"nv\">choice</span> <span class=\"k\">in</span> <span class=\"nv\">question.choice_set.all</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">choice.choice_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endfor</span> <span class=\"cp\">%}</span>\n<span class=\"p\">&lt;/</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>템플릿 시스템은 변수의 속성에 접근하기 위해 점-탐색(dot-lookup) 문법을 사용합니다. 예제의 <code class=\"docutils literal notranslate\"><span class=\"pre\">{{</span> <span class=\"pre\">question.question_text</span> <span class=\"pre\">}}</span></code> 구문을 보면, Django는 먼저 <code class=\"docutils literal notranslate\"><span class=\"pre\">question</span></code> 객체에 대해 사전형으로 탐색합니다. 탐색에 실패하게 되면 속성값으로 탐색합니다. (이 예에서는 속성값에서 탐색이 완료됩니다만) 만약 속성 탐색에도 실패한다면 리스트의 인덱스 탐색을 시도하게 됩니다.</p>\n<p><a class=\"reference internal\" href=\"/ko/4.1/ref/templates/builtins/#std-templatetag-for\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">for</span> <span class=\"pre\">%}</span></code></a> 반복 구문에서 메소드 호출이 일어납니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">question.choice_set.all</span></code>은 Python에서 <code class=\"docutils literal notranslate\"><span class=\"pre\">question.choice_set.all()</span></code> 코드로 해석되는데, 이때 반환된 <code class=\"docutils literal notranslate\"><span class=\"pre\">Choice</span></code> 객체의 반복자는 <a class=\"reference internal\" href=\"/ko/4.1/ref/templates/builtins/#std-templatetag-for\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">for</span> <span class=\"pre\">%}</span></code></a>에서 사용하기 적당합니다.</p>\n<p>템플릿에 대한 더 많은 정보는 <a class=\"reference internal\" href=\"/ko/4.1/topics/templates/\"><span class=\"doc\">템플릿 지침서</span></a>를 참고하세요.</p>\n</section>\n<section id=\"removing-hardcoded-urls-in-templates\">\n<h2>템플릿에서 하드코딩된 URL 제거하기<a class=\"heading-anchor\" href=\"#removing-hardcoded-urls-in-templates\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code> 템플릿에 링크를 적으면, 이 링크는 다음과 같이 부분적으로 하드코딩된다는 것을 기억하세요.</p>\n<div class=\"code-block\" data-language=\"html+django\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;/polls/</span><span class=\"cp\">{{</span> <span class=\"nv\">question.id</span> <span class=\"cp\">}}</span><span class=\"s\">/&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></div>\n<p>이러한 강력하게 결합되고 하드코딩된 접근방식의 문제는 수 많은 템플릿을 가진 프로젝트들의 URL을 바꾸는 게 어려운 일이 된다는 점입니다. 그러나, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls.urls</span></code> 모듈의 <a class=\"reference internal\" href=\"/ko/4.1/ref/urls/#django.urls.path\" title=\"django.urls.path\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">path()</span></code></a> 함수에서 인수의 이름을 정의했으므로, <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">url</span> <span class=\"pre\">%}</span></code> template 태그를 사용하여 url 설정에 정의된 특정한 URL 경로들의 의존성을 제거할 수 있습니다.</p>\n<div class=\"code-block\" data-language=\"html+django\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></div>\n<p>이것이 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls.urls</span></code> 모듈에 서술된 URL 의 정의를 탐색하는 식으로 동작합니다. 다음과 같이 ‘detail’ 이라는 이름의 URL 이 어떻게 정의되어 있는지 확인할 수 있습니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"o\">...</span>\n<span class=\"c1\"># the &#39;name&#39; value as called by the {% url %} template tag</span>\n<span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;detail&#39;</span><span class=\"p\">),</span>\n<span class=\"o\">...</span>\n</code></pre></div>\n<p>만약 상세 뷰의 URL을 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/specifics/12/</span></code>로 바꾸고 싶다면, 템플릿에서 바꾸는 것이 아니라 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code>에서 바꿔야 합니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"o\">...</span>\n<span class=\"c1\"># added the word &#39;specifics&#39;</span>\n<span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;specifics/&lt;int:question_id&gt;/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;detail&#39;</span><span class=\"p\">),</span>\n<span class=\"o\">...</span>\n</code></pre></div>\n</section>\n<section id=\"namespacing-url-names\">\n<h2>URL의 이름공간 정하기<a class=\"heading-anchor\" href=\"#namespacing-url-names\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>튜토리얼의 프로젝트는 <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>라는 앱 하나만 가지고 진행했습니다. 실제 Django 프로젝트는 앱이 몇개라도 올 수 있습니다. Django는 이 앱들의 URL을 어떻게 구별해 낼까요? 예를 들어, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> 앱은 <code class=\"docutils literal notranslate\"><span class=\"pre\">detail</span></code>이라는 뷰를 가지고 있고, 동일한 프로젝트에 블로그를 위한 앱이 있을 수도 있습니다. Django가 <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">url</span> <span class=\"pre\">%}</span></code> 템플릿태그를 사용할 때, 어떤 앱의 뷰에서 URL을 생성할지 알 수 있을까요?</p>\n<p>정답은 URLconf에 이름공간(namespace)을 추가하는 것입니다. <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code> 파일에 <code class=\"docutils literal notranslate\"><span class=\"pre\">app_name</span></code>을 추가하여 어플리케이션의 이름공간을 설정할 수 있습니다.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">path</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">views</span>\n\n<span class=\"n\">app_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls&#39;</span>\n<span class=\"n\">urlpatterns</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">index</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;index&#39;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">detail</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;detail&#39;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/results/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">results</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;results&#39;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/vote/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">vote</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;vote&#39;</span><span class=\"p\">),</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>이제, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/index.html</span></code> 템플릿의 기존 내용을</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>아래와 같이 이름공간으로 나눠진 상세 뷰를 가리키도록 변경하세요.</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/index.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;polls:detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>view를 작성하는 것에 익숙해졌다면, <a class=\"reference internal\" href=\"/ko/4.1/intro/tutorial04/\"><span class=\"doc\">part 4 of this tutorial</span></a> 에서 폼 처리 및 제너릭 뷰에 대한 기본 사항을 알아보십시오.</p>\n</section>","rootId":"writing-your-first-django-app-part-3","toc":[{"title":"개요","anchor":"overview","children":[]},{"title":"뷰 추가하기","anchor":"writing-more-views","children":[]},{"title":"뷰가 실제로 뭔가를 하도록 만들기","anchor":"write-views-that-actually-do-something","children":[{"title":"지름길: render()","anchor":"a-shortcut-render","children":[]}]},{"title":"404 에러 일으키기","anchor":"raising-a-404-error","children":[{"title":"지름길: get_object_or_404()","anchor":"a-shortcut-get-object-or-404","children":[]}]},{"title":"템플릿 시스템 사용하기","anchor":"use-the-template-system","children":[]},{"title":"템플릿에서 하드코딩된 URL 제거하기","anchor":"removing-hardcoded-urls-in-templates","children":[]},{"title":"URL의 이름공간 정하기","anchor":"namespacing-url-names","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"시작하기","url":"/ko/4.1/intro/"}],"prev":{"docname":"intro/tutorial02","title":"첫 번째 장고 앱 작성하기, part 2","url":"/ko/4.1/intro/tutorial02/"},"next":{"docname":"intro/tutorial04","title":"첫 번째 장고 앱 작성하기, part 4","url":"/ko/4.1/intro/tutorial04/"},"formats":{"html":"/ko/4.1/intro/tutorial03/","markdown":"/ko/4.1/intro/tutorial03.md","json":"/ko/4.1/intro/tutorial03.json"},"source":"https://github.com/django/django/blob/stable/4.1.x/docs/intro/tutorial03.txt","official":"https://docs.djangoproject.com/ko/4.1/intro/tutorial03/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11"],"inLocales":["en","zh-hans","fr","ja","id","it","pt-br","ko","es","el","pl"]}