Django - Vue(Json연동, Vue.js Directive, axios)
Project/Django & Vue.js

Django - Vue(Json연동, Vue.js Directive, axios)

뉴비뉴 2019. 9. 7.

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 # 이미 만들어져 있으면 그것을 생성하고, 없으면 새로 생성하여 클라이언트에 보내줘라

 

 

댓글

💲 추천 글