'Router'는 본디 네트워크 장비의 한 종류를 의미합니다. Router는 들어온 패킷의 IP주소를 검사하여 목적지 네트워크가 위치하는 방향의 출력 포트로 패킷을 전달해주는 기능을 수행하며, 이러한 동작을 'Routing'이라 합니다.
Django내에도 위와 같은 Router장비와 유사한 기능을 하는 구성요소로 URL Router가 존재합니다. URL Router는 요청이 들어온 주소를 검사하여 적합한 View메소드와 연결시켜주는 동작을 수행합니다.
예를 들어, https://tuwlab.com/ece/26314 라는 주소로 요청이 들어오면 게시판 모듈의 View메소드를 호출하고, 동시에 파라미터로 'ece'와 '26314'를 전달하여 해당 번호의 게시글을 표시해 주는 동작을 수행하는 것입니다.
전통적인 PHP와 같은 서버스크립트 언어로 웹 사이트를 만든 경우 Document Root Directory에 실제로 ece디렉토리가 존재하고, 그 아래에 26314라는 이름의 파일이 존재해야 위와 같은 요청을 처리할 수 있었습니다.
물론 Apache등 웹 서버에 포함됨 Rewrite Module의 도움을 받아서 위와 같이 구조화된 주소를 사용할 수도 있지만, 웹 서버가 주소를 변환해서 변환된 주소로 미리 지정된 PHP 스크립트를 호출해줘야만 합니다. 이러한 방식은 웹 서버의 도움을 받아야만 하기 때문에 주소 체계 관리나 유연성에 제약이 있을 수밖에 없습니다. 그게 싫다면 https://tuwlab.com/index.php?mid=ece&document_srl=26314 와 같은 지저분한 주소를 사용할 수밖에 없겠지요.ㅎㅎ
Django는 자체적으로 URL을 분석해서 적절한 처리 메소드로 연결시켜 주는 URL Router기능을 내장하고 있으므로 주소 체계와 관련된 귀찮은 작업을 파일 하나에서 체계적으로 정의하고 관리할 수 있습니다.
사이트 전체의 주소 체계가 복잡한 경우 각 사이트 모듈별로 독립적인 URL Router를 정의한 뒤, 계층화해서 Multi-level URL Routing체계도 구성할 수 있습니다.
이번 글에서는 URL Router를 통해 주소를 View 메소드와 연동시키는 방법을 다루고, 주소에 전달한 파라미터를 View메소드에서 받아서 Template Renderer까지 전달시키는 예제를 만들어 보도록 하겠습니다.
Template 파일 작성하기
Template은 HTML응답을 만들어내기 위한 '틀'과 같은 역할을 하는 파일입니다. 전체적인 형식은 HTML파일과 다를 바가 없지만, 곳곳에 렌더링을 할 때 전달된 파라미터로 치환할 'Marker'들이 존재합니다.
Template Renderer가 이 'Marker'들을 전달된 파라미터 변수들로 치환하고, 제어 구문들을 처리해서 최종적으로 요청을 보낸 클라이언트에 응답할 Document Markup을 만들어냅니다. 따라서 Template을 설계할 때는 보통 HTML파일을 작성하는 것과 똑같이 작업을 하되, 서버에서 처리한 내용에 따라 가변적인 내용이 들어가야 할 위치에 바로 이 Marker들을 입력하면 됩니다.
Template Renderer는 렌더링을 할 때 각 모듈 디렉토리의 'templates'디렉토리를 시작점으로 하여 Template파일을 찾습니다. 이 templates디렉토리의 네임스페이스는 전체 모듈이 공유하기 때문에 네임스페이스의 충돌을 방지하기 위해서는 다음과 같이 templates디렉토리 내에 다시 모듈 이름의 디렉토리를 만든 뒤 그 안에 해당 모듈에서 사용할 Template파일들을 위치시키는 것이 좋습니다.
(venv)$ mkdir -p myapp/templates/myapp
myapp/templates/myapp/mypage.html파일을 다음과 같이 작성합니다.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Django Tutorial</title> </head> <body> {{ welcome_text }} </body> </html>
<body>태그 내에 있는 '{{ welcome_text }}'가 바로 추후 치환될 Marker입니다. 이 Marker는 렌더링을 할 때 함께 전달된 welcome_text라는 이름의 변수 값으로 그 내용이 치환됩니다.
이 글의 예제에서는 단순히 파라미터로 전달된 내용을 표시해 주는 Marker만 사용했지만, 조건에 따라 블록을 생략하거나 하나의 블럭을 반복해서 복제해내는 다양한 제어 구문도 사용할 수 있습니다. 이러한 제어 구문들을 잘 활용하면 반복되는 Markup을 하나의 블록으로 표현해 일관된 규칙을 적용할 수 있으므로 프론트엔드의 유지관리 측면에 있어서 효율이 높아집니다.
Django의 Template 문법은 파이썬 문법과 많이 다르기 때문에 Django에 처음 입문한 경우 한 번쯤 어떤 구문들이 있는지 살펴볼 필요가 있습니다. Template문법에 대한 보다 자세한 내용은 다음 Django 메뉴얼 페이지를 참조해 주세요.
https://docs.djangoproject.com/en/1.8/topics/templates/
View 메소드 정의하기
Django에서 View는 기존 Model-View-Controller(MVC)모델에서 View와 Controller를 모두 포함하는 개념입니다. 즉, View메소드에서는 요청에 대한 처리와 응답할 Markup의 생성작업을 모두 함께 처리해야 합니다.
(참고로, Django에서는 MVC가 아닌 MTV: Model-Template-View모델을 따르고 있습니다.)
View메소드들은 모듈 디렉토리 안의 views.py파일에 정의합니다. View 메소드는 파라미터로 클라이언트의 요청에 관한 정보가 포함되어 있는 Request 객체를 받으며, 최종적으로 Response 객체를 만들어서 반환해야 합니다. Response객체는 직접 생성할 수도 있고, Template Renderer를 호출하여 응답할 Markup이 포함된 Response객체를 반환받을 수도 있습니다.
이 예제에서는 위에서 정의한 mypage.html파일의 랜더링을 통해 Response객체를 생성하여 반환하되, 랜더링 과정에서 welcome_text파라미터를 'Hello World!'로 지정하도록 하겠습니다. 정의할 View메소드의 이름은 'DisplayMyPage'입니다.
render메소드 의 세 번째 Parameter가 바로 렌더링에 사용할 Context변수 입니다. 즉, Template에 포함되어 있는 Marker들은 렌더링 과정에서 Context에 정의되어 있는 해당 값으로 치환됩니다.
from django.shortcuts import render def DisplayMyPage(request): return render(request, 'myapp/mypage.html', { 'welcome_text': 'Hello World!' })
실제 프로젝트에서 작업을 할 때는 Template파일과 View메소드를 함께 열어놓고 작업하는 편이 훨씬 효율적입니다.
URL Router에 View 메소드 등록하기
구슬이 서 말이라도 꿰어야 보배라고, 정의한 View 메소드도 URL Router에 등록해줘야만 제 기능을 발휘할 수 있습니다. 앞서 설명했듯이, URL Router는 요청 URL과 View 메소드를 연결해 주는 징검다리 역할을 합니다.
즉, 무슨 URL일때 어떤 View를 호출할지는 전적으로 URL Router에서 정의한다고 할 수 있습니다.
URL Router는 각 모듈이 아닌 프로젝트(Sitebase)디렉토리 내에 존재합니다. 이 예제에서는 sitebase/urls.py가 바로 그 파일입니다. URL Router를 다단계로 구성한 경우, 이 파일이 최상위 Router가 됩니다.
sitebase/urls.py를 열고, 상단에 위에서 정의한 View를 Import시켜줍니다. 이는 myapp의 view모듈을 MyAppView라는 네임스페이스로 Import하라는 의미입니다. 그냥 Import를 해도 되지만, 모듈간 View메소드 이름의 독립성을 보장하기 위해 as구문으로 네임스페이스를 각각 분리해 주는 편이 좋습니다.
... from myapp import views as MyAppView ...
다음으로, 파일 하단의 urlpatterns변수에 다음 줄을 추가해 줍니다. 이는 '/mypage' 요청을 MyAppView의 DisplayMyPage메소드로 연결시키겠다는 의미입니다.
urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^mypage/', MyAppView.DisplayMyPage), ]
이제 드디어 지금까지 작성한 결과를 확인해 볼 시간입니다. runserver명령을 실행해서 개발용 서버 데몬을 실행합니다.
(venv)$ ./manage.py runserver
브라우저를 열고 http://localhost:8000/mypage 로 접속하면 다음과 같은 결과 화면을 볼 수 있을 것입니다. Template의 welcome_text Marker가 지정한 값인 'Hello World!'로 치환되어 나타난 것을 알 수 있습니다.
응용: URL에서 Parameter를 추출하여 View 메소드로 전달하기
이번에는 위 예제에 덧붙여서 정규식 패턴추출 기법을 이용하여 URL의 일부를 Parameter로 추출하여 View메소드로 전달하는 방법에 대한 예제를 살펴보도록 하겠습니다. 지금까지 진행한 예제에 덧붙여서 진행하면 됩니다.
myapp/views.py에 다음과 같이 1개의 Parameter를 추가로 입력받는 DisplayMyPageWithParameter라는 이름의 View메소드를 새로 정의하도록 합니다. 이 메소드는 기존에 정의한 DisplayMyPage메소드와 기본적으로 동작이 같지만, 전달받은 Parameter를 welcome_text로 전달한다는 점이 다릅니다.
... def DisplayMyPageWithParameter(request, my_parameter): welcomeText = my_parameter return render(request, 'myapp/mypage.html', { 'welcome_text': welcomeText })
다음으로 sitebase/urls.py의 urlpatterns변수에 다음 항목을 삽입합니다.
urlpatterns = [ ... url(r'^mypage_param/(?P<my_parameter>.+)', MyAppView.DisplayMyPageWithParameter), ]
여기서 지정한 URL패턴 '^mypage_param/(?P<my_parameter>.+)'를 유심히 볼 필요가 있습니다. 이 패턴은 '/mypage_param/~~~'형식의 URL은 MyAppView.DisplayMyPageWithParameter메소드로 연결하되, '~~~'에 해당하는 부분을 my_parameter로 전달하라는 의미입니다.
괄호로 둘러싸인 '(?P<my_parameter>.+)'는 정규식에서 패턴 그룹을 의미합니다. 일반적인 정규식이라면 '(.+)'으로 나타내고, 이 그룹의 이름은 Group#1이었겠지만, 파이썬에서는 그룹 괄호 직후에 '?P<그룹 이름>'을 추가해서 그룹 이름을 임으로 지정할 수 있습니다. 즉, 파이썬에서 확장된 정규식 규칙이 그대로 적용되어 있는 것입니다.
이렇게 작성하고 runserver를 실행한 뒤 브라우저를 열고 http://localhost:8000/mypage_param/ThisIsParameter! 주소로 접속하면 다음과 같은 결과 화면을 볼 수 있습니다.
정규식에서 지정한 그룹 텍스트가 그대로 View메소드의 파라미터로 전달된 것을 확인할 수 있습니다.
혹시 정규식이 너무 낯설다! 하시는 분은 다음글을 참조해 주세요. 정규식에 관한 기본적인 지식을 정리해 놓은 글입니다. 비단 URL패턴 추출 뿐만 아니라, 전반적인 웹 개발에서 정규식은 상당히 중요하다고 할 수 있습니다. 이번 기회에 정규식에 대해 빠삭하게 익혀놓고 다음 단계로 넘어가는 것은 어떨까요?^^
[정규식(Regular Expression)에 대한 소개와 입문, 튜토리얼] https://tuwlab.com/25809
HTTP 파라미터(GET, POST) 및 쿠키(Cookie) 사용하기
클라이언트에서 Form을 통해 전송한 파라미터나 브라우저 Cookie 정보는 모두 View 메소드에 파라미터로 전달되는 request 객체에 포함되어 있습니다. 뭔가 '요청에 대한 정보가 필요해!'라고 판단되면 무조건 이 파라미터를 뒤져 보면 알 수 있는 것이죠.
Form을 통해 전송한 GET및 POST파라미터는 request.GET및 request.POST를 통해 전달됩니다. 다음은 GET방식으로 전달된 HTTP파라미터 목록에서 'uid'이라는 이름의 파라미터를 가져오는 예시입니다.
uid = request.GET.get('uid')
POST방식일 경우에도 유사하게 다음과 같이 작성하면 파라미터를 가져올 수 있습니다.
uid = request.POST.get('uid')
Method방식(GET, POST)에 상관없이 파라미터를 읽어오려면 다음과 같은 방법을 사용합니다. 단, 이 때 값을 찾는 우선순위가 POST에 있으므로 POST와 GET에 모두 같은 이름의 파라미터가 존재하는 경우 POST로 전송된 값이 전달됩니다.
uid = request.REQUEST['uid']
브라우저 쿠키는 request.COOKIES를 통해 전달됩니다. 다음은 전달된 쿠키에서 'lang'이라는 이름의 쿠키 값을 가져오는 예시입니다.
lang = request.COOKIES.get('lang')
반대로, 응답(response)을 보낼 때 쿠키값을 설정하려는 경우, HttpResponse객체의 set_cookie()메소드를 활용합니다. 이 때, HttpResponse객체를 직접 생성해서 쿠키 값을 설정해도 되지만, render()메소드의 반환값이 HttpResponse객체이므로 Template랜더링을 먼저 한 뒤, 반환된 HttpResponse객체에 대해 set_cookie()메소드를 호출해 주는 방법을 사용하면 편리합니다.
다음은 'lang'쿠키 값으로 'ko_kr'을 설정해서 응답을 보내는 예시입니다.
def DisplayMyPage(request): ... response = render(request, 'myapp/mypage.html', context) response.set_cookie('lang', 'ko_kr') return response
HttpRequest객체에는 HTTP파라미터와 쿠키 정보 외에도 Method나 Encoding, Path등 요청에 관한 다양한 정보들을 포함하고 있습니다. HttpRequest 객체에 어떤 정보들이 포함되어 있는지는 다음 공식 매뉴얼에서 확인해 보세요.
https://docs.djangoproject.com/en/1.8/ref/request-response/
다음 글에서는 Django에서 Static File들을 등록하고 관리하는 방법에 대해 다루도록 하겠습니다.
- [Django Tutorial] 9. Production - uWSGI를 통해 Nginx 웹 서버와 연동하기 (11278) *1
- [Django Tutorial] 8. Production - setting.py설정, Static파일 모으기 (5419)
- [Django Tutorial] 7. 백엔드 콘솔에 Custom Command 추가하기 (4112)
- [Django Tutorial] 6. Database 연동하기 - Model설계, Migration (30076)
- [Django Tutorial] 5. Static 파일 사용하고 관리하기 (9211)
- [Django Tutorial] 3. 프로젝트 및 App 생성, settings.py수정(DB연동, Migration), Runserver (12241)
- [Django Tutorial] 2. 개발 환경 세팅하기 - pyenv 및 virtualenv 활용 (6551)
- [Django Tutorial] 1. 파이썬 기반 웹 프레임워크 Django에 대한 소개 (10892) *2
- VirtualEnv를 통한 Python Sandbox 개발환경 구축하기 (4341)
- pyenv를 이용하여 여러 버전의 Python 동시에 사용하기 (14662) *3