Todo 앱 설계
1. 첫 페이지는 Django 에서 생성해서 클라이언트에게 보내준다.
2. 이 후 화면 렌더링은 Vue.js 코드에서 수행
3. Data 저장은 서버 측의 DB 에 저장(SQLite)
4. Client-Server 간 Data 연동은 JSON 포맷으로
5. Vue.js 의 directive/axios 기능 사용
6. DRF(Django Rest Framework) 대신에 Django 에서는 JsonResponse 사용
Server Rendering
1. 브라우저에서 처음으로 페이지를 요청(request)하면
2. DJango 에서는 첫 화면을 위해 HTML과 CSS를 생성해서 클라이언트에 응답
3. 첫 화면에 대한 응답 index.html
Client Rendering
4. 버튼 클릭 등의 이벤트가 발생하면 Vue.js의 axios를 사용하여 비동기로 Django로 전달
5. Django는 JsonResponse로 응답
*SEO title부분은 검색엔진에서 중요하게 분석하는 태그이기 때문에 검색엔진에 노출되어야 하는 페이지를 작성하고 있다면, 타이틀 태그 부분에 의미있는 문장을 넣는게 좋다.
Vue.js directive 사용하기
<div class="inputBox">
<input class="name" type="text" placeholder="name ..."
v-model="name"><!--Vue.js 에서는 input으로 받는 데이터를 바인딩하기 위해서 v-model 디렉티브를 사용한다.-->
<input class="name" type="text" placeholder="type anything welcomed ..."
v-model="todo" v-on:keyup.enter="add_todo()"><!--v-on:keyup.enter="add_todo()" 는 enter 키를 눌러도 onclick 이벤트가 작동한다.-->
<button class="btn btn-primary btn-sm"
v-on:click="add_todo()">ADD</button>
<!--ADD 버튼을 클릭했을 때 클릭 이벤트를 수신하기 위해서 v-on 디렉티브를 사용한다. 이벤트 핸들러는 add_todo()-->
</div>
<ul>
<li v-for="(todo, index) in todoList"><!--v-for 디렉티브를 사용하여 여러개의 todo 항목을 보여준다. vue.js에서는 index는 todo 뒤에 위치해야한다.-->
<span>{{todo.name}}:: {{todo.todo}}</span> <!--for문을 돌면서 todo.name과 todo.todo를 가져와서 출력 {{}} 변경예정, 왜냐하면 Django와 겹치기 때문이다.-->
<span class="removeBtn"
v-on:click="remove_todo(index)">x</span> <!-- index는 for loop에서 가져온다. -->
</li>
</ul>
Vue 인스턴스 생성부터 설정에 대한 설명
new Vue({ <!-- Vue 인스턴스를 생성한다.-->
delimiters: [[]]<!-- {{ }} 설정을 여기서 변경할 수 있다. -->
el: #app <!-- Vue 인스턴스가 부착될 엘리먼트, 엘리먼트는 Vue가 적용 될 최상위 엘리먼트 id 값을 지정해주면 된다. ex) div id=app -> #app -->
data: name, todo, todoList<!-- Vue 에서 사용 될 변수들-->
methods: add_todo(), remove_todo()<!-- Vue 에서 사용 될 메소드-->
created: <!-- Json 연동을 위해서 Created Life Cycle 도 설정-->
})
Json 연동
Vue.js 코드 내에서 this 는 보통은 Vue 인스턴스를 의미한다.
그래서 여기서 this.fetch_all_todo(); 는 methods: 에 정의 된 this를 말한다.
하지만 methods 안에 있는 fetch_all_tood: function () { this.todoList = res.data; } 는 윈도우 객체를 의미하기 때문에
this.todoList 는 정의되지 않은(Undefined) 객체가 되버린다. 이를 해결하는 방법으로는
로컬변수에 임시로 var vm = this; this를 저장해두었다가 사용하면 된다.
Vue.js 에서는 created, mounted, updated 와 같은 라이프 사이클 훅을 지원하고 있다.
Vue 인스턴스가 생성되는 시점에 호출되는 메서드는 created 이다.
created: function() {
console.log("created()...");
this.fetch_all_todo(); // 메서드 호출
},
methods: {
fetch_all_todo: function () { // 해당 메서드가 호출된다.
console.log("fetch_all_todo()...");
var vm = this;
axios.get('/api/todo/list/') // url을 먼저 넣어주고,
.then(function (res) { // 인자는 임의로 아무거나 넣어줘도 된다. ex) res
console.log("GET RES", res);
vm.todoList = res.data; // res.data 객체를 vue 인스턴스의 객체인 todoList에 대입
}) // .then 성공 시 callback 함수
.catch(function (err) { // .catch 실패 시 callback 함수
console.log("GET ERR", err)
})
},
axios.~ 를 사용하기 위해선 CDN Link 추가가 필요하다.
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios.get() - 테이블(data) 가져오기
Json을 사용하려면 JsonResponse objects에 대해 알 필요가 있다.
JsonResponse objects(data, safe=True)
data 클라이언트에게 보낼 데이터를 지정하게 된다.
data는 딕셔너리형식이나 리스트 형식에도 사용이 가능하고, 딕셔너리 형식이 아닐 때는 safe를 False로 해줘야 한다.
class ApiTodoLV(ListView):
model = Todo
# template_name = ''
# 요청이 get 방식으로 온다.
def get(self, request, *args, **kwargs):
tmpList = [
{'id': 1, 'name': '이창석', 'todo': '오늘 하루는 즐거운 노래로 시작해보자!!'},
{'id': 2, 'name': '이순신', 'todo': '저에게 힘을 주소서!!'},
]
return JsonResponse(data=tmpList, safe=False)
render_to_response 메서드 오버라이딩
class ApiTodoLV(ListView): # 테이블에서 데이터 가져오기
model = Todo
# template_name = ''
# 요청이 get 방식으로 온다.
# def get(self, request, *args, **kwargs):
# tmpList = [
# {'id': 1, 'name': '이창석', 'todo': '오늘 하루는 즐거운 노래로 시작해보자!!'},
# {'id': 2, 'name': '이순신', 'todo': '저에게 힘을 주소서!!'},
# ]
# return JsonResponse(data=tmpList, safe=False)
def render_to_response(self, context, **response_kwargs): # 여기서 하는 일은 JsonResponse를 리턴하는 것이다.
# get_queryset() 메서드에서 테이블로 부터 레코드를 가져와서
todoList = list(context['object_list'].values()) # 테이블에서 가져온 레코드들을 Dict형태로 풀어준다.
return JsonResponse(data=todoList, safe=False)
axios.delete() - 글 삭제
# views.py
@method_decorator(csrf_exempt, name='dispatch') # csrf를 사용하지 않겟다고 클래스형뷰에서 선언
class ApiTodoDelV(DeleteView): # axios.delete
model = Todo
def delete(self, request, *args, **kwargs):
self.object = self.get_object() # Todo 테이블에서 해당 레코드를 찾아서 self.object 변수에 대입
self.object.delete() # 테이블에서 해당 레코드가 삭제된다.
return JsonResponse(data={}, status=204) # dict 형태이기 때문에 safe=True 생략가능, status는 204로 변경
<ul class="todoList">
<li v-for="(todo, index) in todoList">
<!--v-for 디렉티브를 사용하여 여러개의 todo 항목을 보여준다. vue.js에서는 index는 todo 뒤에 위치해야한다.-->
<span>[[todo.name]]:: [[todo.todo]]</span>
<!--for문을 돌면서 todo.name과 todo.todo를 가져와서 출력-->
<span class="removeBtn" <!-- delete 여기서 todo를 remove_todo 시 보내면서 todo.id로 pk를 가져온다. -->
v-on:click="remove_todo(todo, index)">x</span> <!-- index는 for loop에서 가져온다. -->
</li>
</ul>
remove_todo: function (todo ,index) { // <span class="removeBtn" v-on:click="remove_todo(todo, index)">x</span> <!-- index는 for loop에서 가져온다. -->
console.log("remove_todo()...", index);
var vm = this;
axios.delete('/api/todo/' + todo.id + '/delete/') // 99는 pk키
.then(function (res){ // 삭제 성공
console.log("DELETE RED", res);
vm.todoList.splice(index, 1);
})
.catch(function (err){ // 삭제 실패
console.log("DELETE ERR", err);
})
},
정상적으로 작동되지만
여기서 템플릿 처리가 필요없는 경우에는 ListView와 DeleteView를 상속받기보다
BaseListView와 BaseDeleteView를 사용하는 것이 좋다.
axios.post() - 글 작성
add_todo: function () {
console.log("add_todo()...");
if (this.name == '') this.name = '홍길동'; // 사용자가 이름을 입력하지 않으면 this.name 에 홍길동을 넣는다.
if (this.todo == '') return; // 만일 todo 항목을 입력을 안하면 아무일도 하지 않도록 return 시킨다.
var vm = this;
var postData = {name: this.name, todo: this.todo}; // post로 보내는 데이터의 key 명칭이 테이블 컬럼과 같아야한다.
axios.post('/api/todo/create/', postData)
.then(function (res){
console.log("POST RES", res);
vm.todoList.push({id: res.data.id, name: res.data.name, todo: res.data.todo});
// 사용자가 입력한 name과 todo를 v-model기능에 의해서 name과 todo 변수에 바인딩이 되고 name과 todo 변수값을
// todoList 배열의 맨 뒤에 넣고 있는 것이다.
})
.catch(function (err){
console.log("POST ERR", err);
})
this.name = this.todo = ''; // 사용자가 입력한 내용을 지워주는 기능 name과 todo를 비워준다.
},
Client 에서 axios.post 로 데이터를 보낼 때 Django 에서 처리할 코드를 작성해보자.
confirm 기능
if (confirm("Really delete ?") == false) return; // confirm 기능
CSRF Token
Django 에서 csrftoken을 생성해서 클라이언트에 보내주고, 클라이언트는 이를 쿠키에 저장하고, vue.js에서는 이 쿠키를 찾아서 요청헤더에 쿠키 값을 넣어서 보내줘야 한다. Django 에서와 Vue.js에서의 csrf 헤더와 토큰 이름을 맞춰줘야 한다.
axios.post('/api/todo/create/', postData, {xsrfCookieName: 'csrftoken', xsrfHeaderName: 'X-CSRFToken'})
# 처럼 사용할 axios 마다 지정해줄 수도 있고,
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
# 전역변수로 설정해줘서 사용도 가능하다.
CSRF Token은 Django에서 생성을 하는데 {% csrf_token %} 으로 생성한다.
Django 에서 처음부터 csrf token이 만들어져 있지 않다면 (CSRF cookie not set) 이라는 오류가 발생한다.
이러한 문제를 해결하기 위해서 Django 에서는 데코레이터를 제공하고 있다.
ensure_csrf_cookie # 이미 만들어져 있으면 그것을 생성하고, 없으면 새로 생성하여 클라이언트에 보내줘라
'Project > Django & Vue.js' 카테고리의 다른 글
Vue 와 Django(DRF) 를 이용하여 Todo 리스트 만들기 - 2 (백엔드 Django Model) (0) | 2020.06.07 |
---|---|
Vue 와 Django(DRF) 를 이용하여 Todo 리스트 만들기 - 1 (백엔드 환경세팅) (0) | 2020.06.07 |
Django - Vue(Client vs Server Rendering, SPA vs SSR, 4) (0) | 2019.09.06 |
Django - Vue(Mixin 사용하기, bootstrap 팝업창 만들기 , 4) (0) | 2019.09.04 |
Django - Vue(Django 뼈대 만들기 , 2) (0) | 2019.09.02 |
댓글