HTTP : 네트워크 상에서 데이터를 주고 받기 위한 약속
HTTP request methods : 데이터에 어떤 요청을 원하는지를 나타내는 것
특정 리소스를 조회하는 요청(반드시 조회 시에만 사용)
입력 길이 255자 제한
GET으로 데이터 전달 시 Query String 형식으로 보내짐
특정 리소스에 변경 사항을 만드는 요청
입력 길이 제한 X
POST로 데이터 전달 시 HTTP Body에 담겨 보내짐
이는 데이터 베이스에 변경사항을 만드는 요청이므로 CSRF 등의 공격을 막기위해 토큰 사용(최소한의 신원 확인)
Security Token(CSRF Token)
Cross-Site-Request-Forgery(사이트 간 요청 위조) : 사용자의 의지와 무관하게 공격자가 의도한 행동을 하여 특정 웹 페이지를 보안에 취약하게 하거나 수정, 삭제 등의 작업을 하게 만드는 공격 방법
CSRF Token은 CSRF의 대표적인 방어 방법
서버가 사용자 입력 데이터에 임의의 난수값(token)을 부여하여 함께 서버로 전송 시키고 전달된 token이 유효한지 검증
form tag 안에 {% csrf_token %}
태그 작성하여 토큰 값 부여
Django ORM과 View 함수를 결합하여 웹 애플리케이션의 데이터를 저장 & 렌더링
# todos/urls.py
app_name = 'todos'
urlpatterns = [
# todos app의 메인 페이지
path('', views.index, name='index'),
# 단일 todo 조회 시 보여지는 세부정보 페이지
path('detail/<int:todo_pk>/', views.detail, name='detail'),
]
# todos/views.py
def index(request):
# 모든 데이터를 불러와 'todos'변수에 저장하여 return
todos = Todo.objects.all()
context = {
'todos': todos,
}
return render(request, 'todos/index.html', context)
<!-- template -->
<h1 class="mx-3 my-5">✨My Todo List✨</h1>
<!-- todo 생성 페이지 -->
<a href="{% url 'todos:new' %}" class="my-3 ms-5">Todo 생성</a>
<!-- 'todos'변수의 데이터를 모두 나열하고
각각의 세부 페이지로 갈 수 있도록 해당 데이터의 pk인자와 함께 detail url을 걸어줌 -->
{% for todo in todos %}
<div class="my-3 ms-5">
<a href="{% url 'todos:detail' todo.pk %}">할 일 {{ todo.pk }}</a>
</div>
{% endfor %}
# todos/views.py
def detail(request, todo_pk):
# 받은 인자와 같은 pk값을 가지는 데이터를 찾아 'todo'변수에 저장 후 return
todo = Todo.objects.get(pk=todo_pk)
context = {
'todo': todo,
}
return render(request, 'todos/detail.html', context)
<!-- template -->
<!-- 각각의 값을 출력 -->
<h1>제목 - {{ todo.title }}</h1>
<h3>내용 - {{ todo.content }}</h3>
<h3>우선순위 - {{ todo.priority }}</h3>
<h3>마감기한 - {{ todo.deadline }}</h3>
<h3>완료 여부 - {{ todo.completed }}</h3><br>
<!-- 메인 페이지로 돌아가기 -->
<a href="{% url 'todos:index' %}">뒤로 가기</a>
# todos/urls.py
app_name = 'todos'
urlpatterns = [
path('', views.index, name='index'),
path('detail/<int:todo_pk>/', views.detail, name='detail'),
path('new/', views.new, name='new'),
path('create/', views.create, name='create'),
]
# todos/views.py
def new(request):
return render(request, 'todos/new.html')
<!-- template -->
<div class="container">
<h1 class="m-5">Todo list 작성</h1>
<!-- input 제출 시 create url로 보냄 -->
<form action="{% url 'todos:create' %}" method='GET'>
<!-- 각각의 input을 name을 이용해 key 설정 -->
<div class="mb-3">
<label for="title" class="form-label">제목</label>
<input type="text" class="form-control" name="title" id="title">
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea class="form-control" name="content" id="content" rows="3"></textarea>
</div>
<div class="d-flex justify-content-between">
<div class="mb-3 col-5">
<label for="priority" class="form-label">우선순위</label>
<input type="number" class="form-control" name="priority" id="priority">
</div>
<div class="mb-3 col-6">
<label for="deadline" class="form-label">마감기한</label>
<input type="date" class="form-control" name="deadline" id="deadline">
</div>
</div>
<!-- 버튼 누르면 제출되어 create url로 이동 -->
<div class="d-flex justify-content-center mt-5">
<button type="submit" class="btn btn-outline-primary w-50">list에 추가</button>
</div>
</form>
</div>
# todos/views.py
def create(request):
# 각각의 값을 변수에 저장
title = request.GET.get('title')
content = request.GET.get('content')
priority = request.GET.get('priority')
deadline = request.GET.get('deadline')
# 저장한 값을 Todo 인스턴스 생성 시 argument로 넣어줌
todo = Todo(title=title, content=content, priority=priority, deadline=deadline)
# 생성된 인스턴스 저장 = DB에 저장
todo.save()
return render(request, 'todos/create.html')
<!-- template -->
<h1>할 일을 등록하였습니다!</h1>
<!-- 등록 확인 후 메인 페이지로 돌아가기 -->
<a href="{% url 'todos:index' %}">돌아가기</a>
# todos/urls.py
urlpatterns = [
path('', views.index, name='index'),
path('<int:pk>/', views.detail, name='detail'),
path('new/', views.new, name='new'),
path('create/', views.create, name='create'),
path('<int:pk>/delete/', views.delete, name='delete'),
]
# todos/views.py
def delete(request, pk):
todo = Todo.objects.get(pk=pk)
todo.delete()
return redirect('todos:index')
# todos/urls.py
urlpatterns = [
path('', views.index, name='index'),
path('<int:pk>/', views.detail, name='detail'),
path('new/', views.new, name='new'),
path('create/', views.create, name='create'),
path('<int:pk>/delete/', views.delete, name='delete'),
path('<int:pk>/edit/', views.edit, name='edit'),
path('<int:pk>/update/', views.update, name='update'),
]
# todos/views.py
def edit(request, pk):
# 수정할 데이터 조회하여 변수에 저장
todo = Todo.objects.get(pk=pk)
context = {
'todo': todo,
}
return render(request, 'todos/edit.html', context)
<!-- template -->
<div class="container">
<h1 class="m-5">Todo list 수정</h1>
<!-- form 제출 시 todo.pk의 값과 함께 데이터를 update url로 보냄 -->
<!-- 변경사항을 만드므로 POST method 사용 -->
<form action="{% url 'todos:update' todo.pk %}" method='POST'>
<!-- POST method 사용 시 토큰태그 작성 -->
{% csrf_token %}
<div class="mb-3">
<label for="title" class="form-label">제목</label>
<!-- value에 값을 넣어 기존 상태 보여줌 -->
<input type="text" class="form-control" name="title" id="title" value={{ todo.title }}>
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea class="form-control" name="content" id="content" rows="3">{{ todo.content }}</textarea>
</div>
<div class="d-flex justify-content-between">
<div class="mb-3 col-5">
<label for="priority" class="form-label">우선순위</label>
<input type="number" class="form-control" min="1" name="priority" id="priority" value={{ todo.priority }}>
</div>
<div class="mb-3 col-6">
<label for="deadline" class="form-label">마감기한</label>
<!-- date type은 value에 {{ todo.deadline|date:"Y-m-d" }} dateFormat 넣어줘야 함 -->
<input type="date" class="form-control" name="deadline" id="deadline" value={{ todo.deadline|date:"Y-m-d" }}>
</div>
</div>
<div class="d-flex justify-content-center mt-5">
<button type="submit" class="btn btn-outline-primary w-50">저장하기</button>
</div>
</form>
</div>
# todos/views.py
def update(request, pk):
# 수정한 데이터 조회하여 저장
todo = Todo.objects.get(pk=pk)
# 수정된 데이터를 가져와 각각의 필드에 저장
todo.title = request.POST.get('title')
todo.content = request.POST.get('content')
todo.priority = request.POST.get('priority')
todo.deadline = request.POST.get('deadline')
# 변경사항 저장
todo.save()
# 종료 시 해당 데이터의 detail 페이지로 감
return redirect('todos:detail', pk)
<!-- index.html template -->
<div class="container mt-5 mx-auto">
<h1 class="mb-5">✨My Todo List✨</h1>
<ul>
{% for todo in todos %}
<li class="mb-3">
<a href="{% url 'todos:detail' todo.pk %}">{{ todo.title }}</a>
<!-- 현재 상태 출력 -->
/ 완료여부 - {{ todo.completed }}
<!-- if문으로 완료 여부에 따라 다른 text 보이게 함 -->
{% if todo.completed %}
<a href="{% url 'todos:complete' todo.pk %}">완료 취소</a>
{% else %}
<a href="{% url 'todos:complete' todo.pk %}">완료 하기</a>
{% endif %}
</li>
{% endfor %}
</ul>
<a class="btn btn-outline-primary m-3" href="{% url 'todos:new' %}" role="button">Todo 추가하기</a>
</div>
# todos/urls.py
urlpatterns = [
path('', views.index, name='index'),
path('<int:pk>/', views.detail, name='detail'),
path('new/', views.new, name='new'),
path('create/', views.create, name='create'),
path('<int:pk>/delete/', views.delete, name='delete'),
path('<int:pk>/edit/', views.edit, name='edit'),
path('<int:pk>/update/', views.update, name='update'),
# 해당 pk값을 받아 complete 함수 실행
path('<int:pk>/complete/', views.complete, name='complete'),
]
# todos/views.py
def complete(request, pk):
todo = Todo.objects.get(pk=pk)
# 완료이면 False, 미완료이면 True로 바꾼 후 저장
if todo.completed:
todo.completed = False
else:
todo.completed = True
todo.save()
return redirect('todos:index')