1. Redirect1.1. 결과 페이지의 문제점1.2. redirect란?1.3. views.py 수정1.4. URL 인자2. URL 설정 개선2.1. URL 분리2.2. URL name3. 템플릿 개선3.1. 템플릿 이름 구별3.2. 템플릿 상속
1. Redirect
1.1. 결과 페이지의 문제점
지금까지 만든 MBIT 프로젝트의 결과 페이지에는 치명적인 문제점이 있습니다. 웹 브라우저의 탭을 두 개 열어서 하나는 메인 페이지를 하나는 설문을 마친 결과 페이지로 접속합니다.


현재 메인 페이지를 보면
데이터 분석과 인공지능
이 결과로 나온 인원은 4명
입니다. 이제 결과 페이지를 새로고침 해봅시다. 그러면 사진 처럼 양식 다시 제출 확인
메시지 창이 뜨는데 계속
버튼을 눌러봅니다.
이 새로고침 과정을 여러 번 반복해봅니다. 그 후 메인 페이지 탭을 새로고침 해봅니다. 그러면 결과 페이지를 새로고침한 횟수만큼
데이터 분석과 인공지능
인원이 늘어나 있는 것을 확인 할 수 있습니다.
이는 당연히 우리가 원하는 결과가 아닙니다. 사용자가 결과 페이지를 새로고침해도 페이지만 새로 로딩되기만 원하지 설문 결과가 다시 제출되는 것은 원치 않습니다.
웹 브라우저는 요청을 보내고 응답을 받은 뒤에도 보낸 요청이 무엇이었는지 기억하고 있습니다. 유저가 새로고침을 하면 기억하고 있던 요청을 다시 보내는 것입니다. 즉, 결과 페이지에서 새로고침을 하면 설문 데이터가 서버로 다시 전달되는 것입니다. 이를 방지하기 위해서
redirect
를 사용해야 합니다.1.2. redirect란?
redirect
는 브라우저에게 어떤 특정한 url로 재요청을 하라고 명령하는 것입니다. 설문 제출 요청을 처리를 한 뒤, 결과 페이지를 렌더링하기만 하는 요청으로 리다이렉트하여 문제점을 해결할 수 있습니다. 설문 제출 요청 후에 결과 페이지를 받기만 하는 요청이 한 번 더 이루어지기 때문에 더 이상 처음의 설문 제출 요청은 웹 브라우저에 남지 않게 됩니다. 왜냐하면 웹 브라우저는 이전 상태는 전혀 기억하지 못하게 되어있기 때문입니다.[이전] : 설문제출 요청 -> 처리 후 결과페이지 렌더링 [이후] : 설문 제출 요청 -> 처리 -> 결과 페이지 요청 -> 결과 페이지 렌더링
1.3. views.py 수정
이제
views.py
에서 설문 처리와 결과 페이지를 보여주는 result
함수를 두 개로 나누어야 합니다. 하나는 설문 처리만 하는 submit
함수, 하나는 결과 페이지를 렌더링 하는 result
함수로 나눌 것입니다.- 기존의
result
함수의 이름을submit
으로 바꿉니다.
def submit(request): ...
urls.py
에서submit
을 요청할 URL로submit/
을 추가합니다.
urlpatterns = [ ... path('submit/', views.submit), # 여기 ]
form.html
에서<form>
태그의action
속성의 URL을/submit/
으로 바꿉니다.
<form id="form" action="/submit/" method="post">
- 결과 페이지
result.html
을 렌더링할result
함수를 새로 정의합니다.
def result(request): return render(request, 'result.html')
submit
함수의 return 값을 다음과 같이redirect
로/result/
로 재요청을 보내도록 합니다. (redirect
를 반드시 import 해줍니다.)
from django.shortcut import render, redirect def submit(request): ... return redirect('/result/')
1.4. URL 인자
여기서 문제가 있습니다. 결과페이지에는 결과 개발유형에 대한 정보를
context
로 넘겨주었습니다. 하지만 redirect
시에는 context
로 넘겨줄 수 없습니다. 대신에 URL argument
를 이용합니다.urls.py
에서 결과 페이지를 보여주는 URL을 다음과 같이 수정합니다.
urlpatterns = [ ... path('result/<int:developer_id>/', views.result), ]
이는 URL 패턴으로 int 타입의
developer_id
라는 인자를 받을 수 있다는 뜻입니다.submit
함수의redirect
를 다음과 같이 수정합니다.
def submit(request): ... return redirect(f'/result/{best_developer_id}')
그러면 URL 패턴의
<int:developer_id>
자리에 best_developer_id
값이 들어가게 됩니다. 예를 들어, best_developer_id
값이 2
라면 /result/2/
라는 URL로 요청이 가게 됩니다.result
함수에서<int:developer_id>
인자 값을 받으려면 똑같은 이름의 매개변수를 설정해주면 됩니다.
def result(request, developer_id): ...
- 이제
developer_id
를 이용해 개발자 유형을 조회하여context
로 넘겨주면 됩니다.
def result(request, developer_id): developer = Developer.objects.get(pk=developer_id) context = { 'developer': developer, } return render(request, 'result.html', context=context)
이제 아무리 결과 페이지에서 새로고침을 해도
result
함수만 호출되기 때문에 설문 참여자 수는 늘어나지 않습니다.2. URL 설정 개선
2.1. URL 분리
현재 모든 URL 라우팅 설정을
MBIT/urls.py
에 설정하였습니다. 하지만 프로젝트에 앱이 많아지면 URL 설정을 한곳에 모으면 관리하기 힘들어집니다. 그래서 URL 설정을 앱별로 나누어서 관리 하는 것이 좋습니다. main
앱과 관련된 URL을 main/urls.py
에 설정해보겠습니다.- 먼저
main
앱 폴더 아래에urls.py
파일을 만듭니다.

urls.py
의 내용을 다음과 같이 작성합니다.
from django.urls import path from . import views url_patterns = [ path('', views.index), path('form/', views.form), path('submit/', views.submit), path('result/<int:developer_id>/', views.result), ]
MBIT/urls.py
를 다음과 같이 수정합니다.
from django.contrib import admin from django.urls import path, include from main import views urlpatterns = [ path('admin/', admin.site.urls), path('', include('main.urls')), ]
[프로젝트 URL]/~
로 요청이 오면 main
앱의 urls.py
에서 URL 매칭을 찾으라는 설정입니다.2.2. URL name
여기에서 리다이렉트를 사용하기 위해서
/result/
URL을 /submit/
과 /result/
로 분리 시키기 위해서 기존의 /result/
URL을 /submit/
으로 바꾸었습니다. 이때 /result/
URL을 사용하고 있는 모든 곳에서 바꾸어주어야 했습니다. 예를 들어, form.html
에서 <form>
태그의 action
속성을 /result/
에서 /submit/
으로 바꾸어 주어야 했습니다. 만약 기존에 /result/
URL을 사용하던 곳이 수 없이 많다고 가정해 봅시다. 그 전부를 /submit/
으로 바꾸는 것은 정말로 비효율적입니다. 이와 같은 상황을 우리는 '하드코딩' 이라고 표현합니다. 이를 해결할 수 있는 방법이 URL name입니다.간단히 말하면 URL을 변수(name)에 넣어서 사용하는 겁니다. 특정 URL에 우리가 알 수 있는 변수(name)을 지정하고, 이 URL을 사용해야 하는 곳에서 URL을 직접 사용하는 대신에 이 변수(name)를 사용합니다.
main/urls.py
에서 각 path에name
인자를 추가합니다.
urlpatterns = [ path('', views.index, name='index'), path('form/', views.form, name='form'), path('submit/', views.submit, name='submit'), path('result/<int:developer_id>/', views.result, name='result'), ]
main/urls.py
에app_name
이라는 변수를 정의합니다.
app_name = 'main' urlpatterns = [ ... ]
app_name
은 URL namespace(이름공간)으로 여러 앱들 마다 동일한 URL name을 가질 때 구별하기 위함입니다. 예를 들어, main 앱 뿐만 아니라 다른 앱에도 submit
이라는 URL name이 존재하면 app_name:submit
형식으로 URL name 앞에 namespace를 붙여서 구별할 수 있습니다.- 템플릿에서 URL을 사용한 부분들을 URL 태그
{% url '[URL name]' %}
를 사용합니다. - index.html의 '시작하기' 버튼
- form.html의
<form>
태그의aciton
속성 - result.html의 '테스트 다시 하기' 버튼
<a href="{% url 'main:form' %}"> <button class="start" type="button">시작하기</button> </a>
<form id="form" action="{% url 'main:submit' %}" method="post">
<a href="{% url 'main:index' %}"> <button type="button">테스트 다시 하기</button> </a>
views.py
에서submit
함수의 경우 redirect를 사용하기 위해 URL을 사용한 적이 있습니다. 이 또한 URL name으로 표현할 수 있습니다.
def submit(request): ... return redirect('main:result', developer_id=best_developer_id)
redirect
의 developer_id
라는 키워드 인자가 URL 인자 값으로 들어갑니다.이제 URL을 변경하고 싶을 때
urls.py
의 URL 만 변경 시키면 해당 URL name을 사용하는 곳은 자동으로 반영이 됩니다.3. 템플릿 개선
3.1. 템플릿 이름 구별
지금은 앱이
main
하나지만, 여러개의 앱이 있을 때는 템플릿의 이름이 겹칠 수 있는 문제가 발생합니다. 예를 들어, main
외의 다른 앱들도 form.html
이라는 이름의 템플릿을 가지고 싶을 수 있습니다. 하지만 똑같이 form.html
이라는 이름으로 만들면 개발자들의 입장에서는 어느 앱의 form.html
을 가리키는 것인지 헷갈리게 됩니다. 그래서 각 앱의 templates
폴더 아래에 각 앱의 이름의 폴더를 하나 더 만들고 그 안에 템플릿 파일들을 저장합니다. 그러면 각 템플릿의 이름은 앱이름/템플릿명.html
형태가 되어서 똑같은 템플릿 명이더라도 앱이름
으로 구별할 수 있게 됩니다.main
앱의templates
폴더 아래에main
폴더를 만들고 템플릿 파일들을 이 폴더에 옮깁니다.

views.py
에서render
함수에 쓰인 템플릿 이름들을 수정합니다.index
함수form
함수result
함수
def index(request): ... return render(request, 'main/index.html', context=context)
def form(request): ... return render(request, 'main/form.html', context)
def result(request, developer_id): ... return render(request, 'main/result.html', context=context)
3.2. 템플릿 상속
지금까지 작성한 템플릿들의 문제점은 반복적인 코드가 많다는 것입니다. 각 템플릿 마다 다음과 같은 공통의 코드를 가지고 있습니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> ... <title>나의 개발 유형찾기</title> </head> <body> ... </body> </html>
이러한 경우 공통의 틀이 되는 코드는 하나의 파일에 한 번만 작성하고, 이를 필요로 하는 템플릿에서 가져다 쓸 수 있도록 만들 수 있습니다. 이를 템플릿 상속이라고 합니다.
main/templates/main/
폴더에 공통의 템플릿을 작성할base.html
파일을 만들고, 다음과 같이 작성합니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>나의 개발 유형찾기 - final</title> {% block head %} {% endblock head %} <title>Document</title> </head> <body> {% block body %} {% endblock body %} {% block js %} {% endblock js %} </body> </html>
이 템플릿은 가장 큰 틀을 제공하고, 이 틀의 각
{% block [block이름] %}
이라는 공간에 각 템플릿들의 고유한 코드가 들어갑니다.- index.html을 다음과 같이 수정합니다.
{% extends 'base.html' %}
: 부모 템플릿으로base.html
을 사용하겠다는 뜻입니다.{% block [blockname] %}
~{% endblock [blockname] %}
{% extends 'main/base.html' %} {% load static %} {% block head %} <link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}"> {% endblock head %} {% block body %} <section id="main_contents"> <div class="wrapper"> ... </div> </section> {% endblock body %}
base.html
의 각 block
부분에 삽입될 코드를 작성합니다.- 동일한 방식으로
form.html
,result.html
도 수정합니다.
{% extends 'main/base.html' %} {% load static %} {% block head %} <link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'css/form.css' %}"> <script src="https://code.jquery.com/jquery-3.5.1.js"></script> {% endblock head %} {% block body %} <section id="survey"> <div class="wrapper"> ... </div> </section> {% endblock body %} {% block js %} <script type="text/javascript" src="{% static 'js/form.js' %}"></script> {% endblock js %}
{% extends 'main/base.html' %} {% load static %} {% block head %} <meta property="og:title" content="나의 개발 유형은?" /> <meta property="og:image" content="" /> <meta property="og:url" content="" /> <meta property="og:description" content="나에게 꼭 맞는 개발 유형은 무엇일까?" /> <link rel="stylesheet" href="{% static 'css/reset.css' %}"> <link rel="stylesheet" href="{% static 'css/result.css' %}"> <script src="https://code.jquery.com/jquery-3.6.0.js"></script> {% endblock head %} {% block body %} <section id="main_contents"> <div class="wrapper"> ... </div> </section> {% endblock body %} {% block js %} <script type="text/javascript" src="{% static 'js/result.js' %}"></script> <script src="https://developers.kakao.com/sdk/js/kakao.js"></script> {% endblock js %}