Django #8 - JSON 연동 Sequence Chart(Vue-Django 연동하기)

Corner·2022년 4월 24일
0

django

목록 보기
11/14

JSON 연동 Sequence Chart

브라우저에서 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와 장고간에 연동 방식을 보여준다.

VueDjTodo URL 설계

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)불필요

Vue.js - axios.get() 코딩하기

todo_index.htmlcreated이벤트와 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)">&#x00D7</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 링크를 걸어서 사용해도 된다.


get.JsonResponse 코딩하기

터미널을 열고 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)">&#x00D7</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

👉🏻깃허브 소스

profile
Full-stack Engineer. email - corner3499@kakao.com,

0개의 댓글