브라우저에서 Todo 메뉴를 클릭하게 되면 Request를 보내고 Response를 받게된다.
이때 장고에서는 HttpResponse 클래스를 이용해서 응답을 만들게 되는데, 응답에는 html/css/vue.js 코드가 들어있게 된다.
응답을 받은 브라우저는 화면을 그리기 시작하는데 화면을 그리는 렌더링 과정에서 Vue.js 코드가 실행되는 시점에 axios 기능을 이용해서 get(/api/xxx/)메소드를 사용해 서버에 요청을 보내게 된다.
장고에서는 JsonResponse 서브 클래스를 이용해서 응답을 보내게 된다.
응답코드는 200, 응답에 들어있는 데이터는 장고가 테이블에서 todoList 데이터를 응답에 담아서 보내게 된다.
이 이후 사용자가 ADD버튼을 클릭하거나, Remove삭제 버튼을 클릭하게 되는데
만일, ADD버튼을 클릭한 경우 axios.post(/api/xxx/) 메소드가 호출되면서 서버로 요청을 한다.
장고에서는 테이블에 새로운 레코드를 만든 후에 응답 코드는 201으로 응답에 들어있는 데이터는 새로운 todo 항목을 넣어서 응답을 보내게 된다.
사용자가 삭제버튼을 클릭한 경우, axios.delete (/api/xxx/)메소드가 호출되면서 서버로 요청한다.
장고에서는 테이블에서 해당 레코드를 삭제한 다음 응답코드 204로 클라이언트에 응답을 보내게 된다.
여기까지가 axios 세 가지 과정이 vue.js와 장고간에 연동 방식을 보여준다.
URL 패턴 | 뷰 이름 | 템플릿 파일명 |
---|---|---|
/admin/ | (장고 기본 제공) | |
/ | HomeView(TemplateView) | home.html |
/todo/ | TodoTV(TemplateView) | todo_index.html (vue.js 코드 포함) |
/api/todo/list/ | ApiTodoLV(BaseListView) | 불필요 |
/api/todo/create/ | ApiTodoCV(BaseCreateView) | 불필요 |
/api/todo/:index/delete/ | ApiTodoDelV(BaseDeleteView) | 불필요 |
todo_index.html
에 created
이벤트와 methods를 추가한다.
{% extends 'base.html' %}
{% block title %}Vue.js - Django todo app{% endblock %}
{% block extra-style %}
<style>
body {
text-align: center;
background-color: #ddd;
}
.inputBox {
margin: auto;
width: 70%;
background: white;
height: 50px;
border-radius: 50px;
line-height: 50px;
}
.inputBox .name {
border-style: none;
border-bottom: 1px solid #ddd;
width: 90px;
padding-left: 20px;
line-height: 20px;
}
.inputBox .item {
border-style: none;
border-bottom: 1px solid #ddd;
width: 400px;
margin-left: 50px;
padding-left: 20px;
line-height: 20px;
}
.todoList {
list-style: none;
padding: 10px 0;
text-align: left;
}
.todoList li {
display: flex;
height: 50px;
line-height: 50px;
margin: 0.5rem 0;
padding: 0 0.9rem;
background: white;
border-radius: 5px;
}
.removeBtn {
font-size: 20px;
cursor: pointer;
margin: auto 0 auto auto;
}
.removeBtn:hover {
color: #de4d85;
}
</style>
{% endblock %}
{% block content %}
<div id='app'>
<h1>My Todo App !</h1>
<strong>서로 할 일이나 의견을 공유해 봅시다.</strong>
<br>
<div>
<input class="name" type="text" placeholder="name ..." v-model.trim="name">
<!-- v-model에 .trim 이라는 수식어를 사용하면 앞뒤 공백을 삭제해서 삽입한다. -->
<input class="item" type="text" placeholder="type anything welcomed ..." v-model.trim="todo"
@keyup.enter="add_todo()">
<button class="btn btn-primary btn-sm" @click="add_todo()">ADD</button>
</div>
<ul class="todoList">
<li v-for="(todo, index) in todoList">
<span>{ todo.name } :: { todo.todo }</span>
<span class="removeBtn" @click="remove_todo(index)">×</span>
</li>
</ul>
<!-- - -->
</div>
{% endblock %}
{% block extra-script %}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
let vm = new Vue({
delimiters: ['{', '}'],
el: '#app',
data: {
name: '',
todo: '',
todoList: [
{name: '테스트1', todo: '테스트 내용 1'},
{name: '테스트4', todo: '테스트 내용 4'},
{name: '테스트2', todo: '테스트 내용 2'},
{name: '테스트3', todo: '테스트 내용 3'},
],
},
created: function () {
console.log("created()..")
this.fetch_all_todo();
},
methods: {
fetch_all_todo: function () {
console.log("fetch_all_todo()...")
let vm = this;
axios.get('/api/todo/list').then(function (res){
console.log("GET RES", res);
// 콜백 함수내에 정의된 this는 window 객체를 의미하기 때문에 this.todoList는 Undefiend 객체가 되므로
// 로컬 변수에 임시로 this로 저장했다가 사용하면 된다.
// this.todoList = res.data;
vm.todoList = res.data;
}).catch(function (err) {
console.log("GET ERR", err);
})
},
add_todo: function () {
console.log("add_todo()...");
if (this.name === '') {
this.name = 'user'
}
if (this.todo === '') {
return;
}
this.todoList.push({name: this.name, todo: this.todo});
this.name = this.todo = '';
},
remove_todo: function (index) {
console.log(index);
this.todoList.splice(index, 1);
},
},
})
</script>
{% endblock %}
axios
을 사용하기 위해서는 라이브러리를 설치해야한다.
npm install axios
또는 CDN 링크를 걸어서 사용해도 된다.
터미널을 열고 api 앱을 하나 생성한다. python manage.py startapp api
api 앱을 settings.py에 등록한다.
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todo.apps.TodoConfig',
'api.apps.ApiConfig', # 추가
]
myiste/urls.py
에 path 추가
path('api/', include('api.urls')),
api/urls.py
작업
from django.urls import path
from . import views
app_name = 'api'
urlpatterns = [
path('todo/list/', views.ApiTodoLV.as_view(), name='list'),
]
api/views.py
작업
from django.http import JsonResponse
from django.views.generic import ListView
from todo.models import Todo
class ApiTodoLV(ListView):
model = Todo
# template_name =
def get(self, request, *args, **kwargs):
tmpList = [
{'id': 1, 'name': 'd테스트1', 'todo': '테스트 내용 1'},
{'id': 2, 'name': 'd테스트4', 'todo': '테스트 내용 4'},
{'id': 3, 'name': 'd테스트2', 'todo': '테스트 내용 2'},
{'id': 4, 'name': 'd테스트3', 'todo': '테스트 내용 3'},
]
return JsonResponse(data=tmpList, safe=False)
여기까지 완료되었다면 /todo/ url로 들어가서 확인한다.
장고의 api 데이터가 잘 나오는 것을 확인할 수 있다.
그리고 개발자 도구에서 콘솔을 확인해보면
서버에서 보내준 데이터, 우리가 원하는 데이터는 res.data
안에 들어있다.이 점을 유의한다.
네트워크 부분도 확인해서 내용들이 잘 받아오는지도 확인한다.
위 과정은 임시로 많은 데이터이기 떄문에 테이블 데이터를 보여줄 수 있도록 장고 소스를 수정한다.
테이블에서 데이터를 가져오는 일은 ListView에서 하는데 어떻게 하는지에 대해서는
https://ccbv.co.uk 에서 확인하도록 한다.
views.py
에서 기존의 get()
메소드는 주석처리하고def render_to_response
메소드를 오버라이딩 한다.
def render_to_response(self, context, **response_kwargs):
todoList = list(context['object_list'].values())
return JsonResponse(data=todoList, safe=False)
그리고 다시 확인해보면 데이터를 확인할 수 없는데, 콘솔에 보다시피 object_list의 array값이 없기 떄문이다.
테이블에 데이터를 추가한다.
![image-20220430152029118](/Users/corner/Library/Application Support/typora-user-images/image-20220430152029118.png)
어드민으로 들어와서 add todo 버튼을 눌러 할일을 적고, 이름을 빼고 할일만 적었을 때 홍길동 이름이 자동으로 추가 되는지에 대해 확인해본다.
마지막으로
todo_index.html
에서
data에 todoList 배열 데이터를 지워준다.
{% extends 'base.html' %}
{% block title %}Vue.js - Django todo app{% endblock %}
{% block extra-style %}
<style>
body {
text-align: center;
background-color: #ddd;
}
.inputBox {
margin: auto;
width: 70%;
background: white;
height: 50px;
border-radius: 50px;
line-height: 50px;
}
.inputBox .name {
border-style: none;
border-bottom: 1px solid #ddd;
width: 90px;
padding-left: 20px;
line-height: 20px;
}
.inputBox .item {
border-style: none;
border-bottom: 1px solid #ddd;
width: 400px;
margin-left: 50px;
padding-left: 20px;
line-height: 20px;
}
.todoList {
list-style: none;
padding: 10px 0;
text-align: left;
}
.todoList li {
display: flex;
height: 50px;
line-height: 50px;
margin: 0.5rem 0;
padding: 0 0.9rem;
background: white;
border-radius: 5px;
}
.removeBtn {
font-size: 20px;
cursor: pointer;
margin: auto 0 auto auto;
}
.removeBtn:hover {
color: #de4d85;
}
</style>
{% endblock %}
{% block content %}
<div id='app'>
<h1>My Todo App !</h1>
<strong>서로 할 일이나 의견을 공유해 봅시다.</strong>
<br>
<div>
<input class="name" type="text" placeholder="name ..." v-model.trim="name">
<!-- v-model에 .trim 이라는 수식어를 사용하면 앞뒤 공백을 삭제해서 삽입한다. -->
<input class="item" type="text" placeholder="type anything welcomed ..." v-model.trim="todo"
@keyup.enter="add_todo()">
<button class="btn btn-primary btn-sm" @click="add_todo()">ADD</button>
</div>
<ul class="todoList">
<li v-for="(todo, index) in todoList">
<span>{ todo.name } :: { todo.todo }</span>
<span class="removeBtn" @click="remove_todo(index)">×</span>
</li>
</ul>
<!-- - -->
</div>
{% endblock %}
{% block extra-script %}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
let vm = new Vue({
delimiters: ['{', '}'],
el: '#app',
data: {
name: '',
todo: '',
todoList: [],
},
created: function () {
console.log("created()..")
this.fetch_all_todo();
},
methods: {
fetch_all_todo: function () {
console.log("fetch_all_todo()...")
let vm = this;
axios.get('/api/todo/list').then(function (res){
console.log("GET RES", res);
// 콜백 함수내에 정의된 this는 window 객체를 의미하기 때문에 this.todoList는 Undefiend 객체가 되므로
// 로컬 변수에 임시로 this로 저장했다가 사용하면 된다.
// this.todoList = res.data;
vm.todoList = res.data;
}).catch(function (err) {
console.log("GET ERR", err);
})
},
add_todo: function () {
console.log("add_todo()...");
if (this.name === '') {
this.name = 'user'
}
if (this.todo === '') {
return;
}
this.todoList.push({name: this.name, todo: this.todo});
this.name = this.todo = '';
},
remove_todo: function (index) {
console.log(index);
this.todoList.splice(index, 1);
},
},
})
</script>
{% endblock %}
GitHub Source
👉🏻깃허브 소스