All Articles

Vue components within Django Template

작년(2017년) 크라우드소싱 수업 시간에 동관씨, 현규씨랑 같이 크라우드 소싱으로 법률의 가독성을 개선하겠다고 야심차게 시작한 프로젝트가 있었다 사이트 Github Repo.
당시 프로토타입을 만들때 팀 내에 백엔드 API를 만들고, 다시 프론트엔드 웹앱을 만드는 방식이 비효율적이라는 공감대가 형성되서 Django Template으로 페이지를 찍어내되 페이지의 인터랙티브한 요소는 Vue로 구현을 해보기로 했고, 어찌어찌 돌아가는 물건을 만들기는 했다. 최근에 조금 더 잘할 수 있는 방법을 공부할 일이 있어서 간단히 과정과 소감을 기록해 본다.

어떻게?

STEP1. Vue 불러오기

  • 그냥 jQuery 불러오듯이 CDN 한 줄 넣으면 된다.

STEP2. Vue로 다루고 싶은 데이터를 JSON형태로 템플릿에 넣어주기

  • 데이터를 넣어줄 때, 모델의 인스턴스는 to_dict()를 사용하면 serialize가 되고 쿼리셋의 경우는 values()를 사용하면 된다고 한다. 아니면 아예 Django REST Framework에 있는 Serializer를 사용하면 다양한 형태로 Related Field를 serialize 할 수 있다.
  • Serialize된 데이터를 json.dumps에 넣어 템플릿에 넣어 준다.
  • 더 자세한 사항은 SO 질문을 참조

STEP3. 템플릿에서 Json으로 들어가있는 스트링을 읽기

  • 아까 만든 Json 스트링을 템플릿에 넣으면 Escaping이 일어나서 다음과 같은 형태로 템플릿에 들어간다.

    [{"id": 93, "url": ...
  • 따라서 템플릿 내부에서 변수를 읽을 때

    {{ data|safe }}

    로 읽든, 템플릿의 autoescape를 사용하든(Escaping 링크 참조), safestring을 활용하든지 해서 json을 알맞은 형태로 읽어준다.

STEP4. Vue 코드를 작성

여기부터는 그냥 Vue 인데, 한가지 유의할 점은 장고 템플릿의 Delimitter도 {{ }}이고 Vue에서도 {{ }}을 사용한다는 점이다. 여기서 두가지 솔루션이 가능하다.

  1. 장고 템플릿의 verbatim 태그를 사용. 이러면 템플릿 엔진이 verbatim 태그 내부는 건드리지 않는다.
  2. Vue 인스턴스의 delimiters 옵션을 활용해서 다른 delimiter를 사용할 수 있다.

    new Vue({
    delimiters: ['[[', ']]']
    })

이렇게 템플릿이랑 CSS와 같이 넣으면 나름 Vue 컴포넌트 느낌으로 파일 하나에 스타일이랑 로직을 다 정의할 수 있다.

  {% verbatim %}
  <div id="app">
    <ul>
      <li v-for="page in pages">{{ page }}</li>
    </ul>
  <div>
  {% endverbatim %}

  <style>
  /* */ CSS
  </style>

  <script>
  var data = {{ data|safe }}
  new Vue ({
    el: '#app',
    data: {
      pages: data.pages,
    }
  })
  </script>

STEP5. Vue 코드를 이곳 저곳에서 재사용하기

  • 위의 방식은 나름 편한데, 템플릿 여러 곳에서 쓰이는 Vue component를 다 따로 정의해야한다는 단점이 있다.
  • 그래서 Vue 컴포넌트만 따로 별도의 js파일에 작성하고, 이를 템플릿에서 불러와서 쓰는 방법이 더 바람직하다고 생각한다. 아까 코드를 두 파일로 나눈다면,

component.js 에 Vue 컴포넌트를 정리하고,

Vue.component('vue-component', {
	props: ['pages'],
	template: `
		<ul>
			<li v-for="page in pages">{{ page }}</li>
		</ul>
	`
})

page.html에서 불러서 쓰는 방법이 있다. CSS도 js에 넣고 싶은데 방법을 모르겠다.

  <script src="{% static "js/component.js" %}"></script> 
  /* 전역 컴포넌트여서인지 템플릿보다 먼저 불려야함*/

  <div id="app">
    <vue-component :pages="pages">
    </vue-component>
  <div>

  <style>
  /* */ CSS
  </style>

  <script>

  var data = {{ data|safe }}
  new Vue ({
    el: '#app',
    data: {
      pages: data.pages,
    }
  })
  </script>

과연 이럴 가치가 있는가?

느꼈던 장점:

  • 보일러플레이트가 필요 없어짐 -> 인증, HTTP 요청 등을 프론트엔드 단에서 고민할 필요가 하나도 없다. 그냥 데이터를 가지고 인터랙션만 구현하면 된다.
  • 필요한 경우 몇몇 페이지는 장고 폼 / 기타 라이브러리에서 그려주는 페이지 들을 그대로 활용할 수 있다.
  • 모-던한 프론트 개발에 덜 익숙한 분과도 빠르게 협업할 수 있다.

느꼈던 단점:

  • 컴포넌트를 잘게 쪼개기가 어렵다. 위 예제에서 컴포넌트 안에서 다시 컴포넌트를 부르려면 음… script 태그를 두 개 사용하든지 이제 모든 메이저 브라우저에서 지원한다는 ES6 모듈을 사용하든지 해야한다.
  • 자바스크립트 코드를 작성할 때 에디터의 지원을 받기가 어렵다.
  • 다른 컴포넌트를 가져오기가 불편하다.

결론

  • 그냥 API를 빠르게 Django REST 등으로 찍어내고, 프론트를 모-던하게 짜는게 개인의 성장, 확장성, 유지보수 등 여러면에서 훨씬 훨씬 유리하다. 이제는 공부할 자료도 많고, Vue 정도 되면 React보다 코드도 훨씬 짧아진다.
  • 다만 여전히 수업 프로젝트 같은 제한적인 상황에서 이런 방법이 같은 기능을 훨씬 빠른 시간안에 만드는 방법일 수는 있다고 생각한다.

참고한 글들

Published 7 Jul 2018

If I keep marking the dots, someday they will 🔗🔗
Hyeungshik Jung on Twitter