django

순수 django + vue template을 사용하여 비동기로 게시물 불러오기, 좋아요 처리

발전생 2020. 12. 28. 15:49

vue.js의 template을 사용하니 document element를 직접 찾고 조작해주는 과정이 없으니 편하다. 다만 vue.js에 익숙하지 않아 vue적인 사고를 하기까지가 어렵다.

 

{% extends 'my_book/base.html' %}

{% load static %}
{% block style %}
<link rel="stylesheet" href="{% static 'my_book/css/navbar.css' %}">
<link rel="stylesheet" href="{% static 'my_book/css/home.css' %}">
{% endblock %}

{% block content %}
    {% include 'my_book/navbar.html' %}
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <main>
        <article id="new-post">
            <div class="title">게시물 작성</div>
            <form action="" method="POST">
                {% csrf_token %}
                {{ form.content }}
                <input class="submit-btn" type="submit" value="등록">
            </form>
        </article>

        <section id="friend-feed">
            <article :id=[[post["pk"]]] v-for="post in posts">
                <div v-if="loading">로딩 중.....</div>
                <div>사용자 [[ post["fields"].author ]]</div>
                <span>[[ post["fields"].created_date ]]</span>
                <div>[[ post["fields"].content ]]</div>
                <button class="thumb" @click="thumb_up(post)"></button>
                <span>좋아요 [[ post["fields"].likes ]]</span>
            </article>
            <div v-if="end">더 이상 불러올 게시물이 없습니다</div>
            <button @click="fetch_posts()">more</button>
        </section>

    </main>


    <script>
        axios.defaults.xsrfCookieName = 'csrftoken';
        axios.defaults.xsrfHeaderName = 'X-CSRFToken';

        var app = new Vue({
            delimiters: ['[[', ']]'],
            el: '#friend-feed',
            data: {
                posts: [],
                loading: true,
                end: false,
            },
            methods: {
                fetch_posts: function() {
                     if(this.end) return
                     this.loading = true
                     axios.get('../api/post-10/from/' + (this.posts.length))
                        .then(response => {
                            let data = JSON.parse(response.data)
                            if(data.length) {
                                for(var i = 0; i < data.length; i++) {
                                    this.posts.push(data[i])
                                }
                            } else {
                                this.end = true
                            }
                        })
                        .catch(error => {
                            console.log(error)
                        })
                        .finally(() => {
                            this.loading = false
                        })
                },
                thumb_up: function(post) {
                    post["fields"].likes += 1
                    axios.post('../api/thumb-up/' + post["pk"])
                },
            },
            mounted() {
                this.fetch_posts()
            }
        })

    </script>
{% endblock %}

 

기능

more 버튼을 누르면 지금까지 불러온 데이터에다 10개를 추가로 불러와서 posts 배열에 넣는다. 그러면 템플릿에서 알아서 추가적으로 DOM을 생성해준다. (물론 비동기로)

그리고 v-if 를 잘 사용하면 사용자에게 로딩 중임을 쉽게 알려줄 수 있다. 더 이상 불러올 게시물이 없는데 계속 more 버튼을 누르는 이상한 행동을 하는 사람이 있을 수도 있다. 그러한 서버 부하를 막기 위해 "더 이상 불러올 게시물이 없다"는 것을 알려주고 서버에 더 이상 get 요청을 보내지 않는다.

좋아요 버튼은 누를 경우 바로 좋아요 수를 증가시켜 브라우저에 보여주고 서버에 post 요청을 보내서 데이터베이스에 반영하도록 했다.

 

어려웠던 점

순수 django만 쓰다가 vue라는 자바스크립트를 다루려니까 this를 자꾸 빼먹어서 예상치 못한 결과를 낳았다. this에 주의하자. 자바스크립트에서 빈 배열은 null로 안 받는 것인가? 그래서 length로 처리했다.

django의 serializer를 사용하여 json 데이터를 템플릿으로 넘겨줬을 때 JSON.parse(response.data) 를 해주지 않으면 배열이 아닌 요상한 값이 들어가 있었다.

 

남은 문제

DRF를 사용하지 않고 serializer만으로 foreign key 관계의 원하는 데이터를 넘길 수 있을까? (기본적으로 foreign key 관계는 id만 전달된다.)