관련 지식
#javascript #vue.js #mixin

Vue.js 에서 Mixin 은 컴포넌트 개념과 함께 매우 유용한 기능입니다.
어떻게 쓰면 좋을지 알아 보겠습니다.

기본 사용법

  1. var mixin = {
  2. created: function () {
  3. console.log('mixin hook called')
  4. }
  5. }
  6. new Vue({
  7. mixins: [mixin],
  8. created: function () {
  9. console.log('component hook called')
  10. }
  11. })

위의 소스는 공식 홈페이지에 있는 내용입니다. 믹스인을 만들고 뷰 컴포넌트에 설정하면 뷰 컴포넌트의 훅(hook) 보다 믹스인 컴포넌트의 훅을 먼저 실행하게 됩니다. 따라서 위의 소스를 실행하면 아래와 같은 순서로 출력됩니다.

  1. mixin hook called
  2. component hook called

뷰 컴포넌트 생성시 적용할 믹스인을 선택할 수 있기 때문에 아래와 같이 다른 동작을 하는 동일 인터페이스 적용이 편리 합니다.

  1. var mixin_localStorage = { //로컬 스토리지 사용
  2. methods : {
  3. setItem : function(k, v) {
  4. localStorage.setItem(k, v);
  5. },
  6. getItem : function() {
  7. return localStorage.getItem(k);
  8. }
  9. }
  10. }
  11. var mixin_sessionStorage = { //세션 스토리지 사용
  12. methods : {
  13. setItem : function(k, v) {
  14. sessionStorage.setItem(k, v);
  15. },
  16. getItem : function() {
  17. return sessionStorage.getItem(k);
  18. }
  19. }
  20. }
  21. new Vue({
  22. mixins: [mixin_localStorage],
  23. created: function () {
  24. this.setItem('new', 'newitem'); //믹스인에 따라 다르게 동작한다.
  25. }
  26. })

중첩 적용

mixins 속성은 배열이기 때문에 여러개의 믹스인을 중첩하여 적용할 수도 있습니다. 배열에 정의된 순서로 hook 이 발생합니다.

  1. var mixin = {
  2. created: function () {
  3. console.log('mixin hook called')
  4. }
  5. }
  6. var mixin2 = {
  7. created: function () {
  8. console.log('this is mixin2')
  9. }
  10. }
  11. new Vue({
  12. mixins: [mixin, mixin2],
  13. created: function () {
  14. console.log('component hook called')
  15. }
  16. })
  1. mixin hook called
  2. this is mixin2
  3. component hook called

여기서 중요한 포인트가 있습니다. 믹스인에 정의된 라이프사이클 hook 이벤트는 믹스인을 중첩해서 사용했을때 차례대로 호출됩니다. 그러나 믹스인의 methods에 정의된 함수는 이름이 같을 경우 나중 믹스인의 함수가 덮어쓰게 됩니다. 따라서 믹스인을 중첩해서 사용하려고 할 경우엔 hook 이벤트를 활용하는 방향이 맞습니다.

  1. var startIndicator = {
  2. beforeCreate: function () {
  3. console.log('인디케이터 호출')
  4. }
  5. }
  6. var updateIndicator = {
  7. updated: function () {
  8. console.log('업데이트 되었습니다')
  9. }
  10. }
  11. new Vue({
  12. data : {
  13. count : 0
  14. },
  15. mixins: [startIndicator, updateIndicator],
  16. created: function () {
  17. var $this = this;
  18. setInterval(function() {
  19. $this.count++;
  20. }, 1000);
  21. }
  22. })

위 예제는 count 변수를 렌더링하는 대상이 없기 때문에 updated 이벤트는 발생하지 않습니다. 데이터가 업데이트가 되었을때 Toast Popup 같은 메시지를 보여주고 싶다면 그에 대한 믹스인을 만들고 필요하면 화면에서 적용만 하면 되겠습니다.

전역 Mixin

위에서 보인 예제는 뷰 컴포넌트에서 어떤 믹스인을 사용할지 선택할 수 있었습니다. 그러나 모든 뷰 컴포넌트에서 공통으로 적용하고 싶을수도 있을것입니다. 예를들어 Server Side Rendering 사이트에서 Vue.js를 적용할 경우 아래와 같은 화면을 볼 수도 있습니다.

뷰 컴포넌트가 생성되어 실행되기 전까지는 일반 HTML로 파싱되어 화면에 보이기 때문입니다.

  1. <div v-for='(item, index) in list' v-bind:class='[hasFile(item)]' v-bind:data-index='index' class='itemWrap'>
  2. <div class='media'>{{item.media}}</div>
  3. <div class='no'>{{item.norae_no}}</div>
  4. <div class='info'>
  5. <div class='title'>{{item.title}}</div>
  6. <div class='hasFile'>{{hasFile(item)}}</div>
  7. <div class='artist'>{{item.artist}}</div>
  8. </div>
  9. </div>

이에 대해 가장 편한 방법은 뷰 컴포넌트의 el로 지정되는 DOM을 display:none 상태로 만들어두고 뷰 컴포넌트가 마운트 되었을때 보여주게 하는 것입니다. 하지만 모든 뷰 컴포넌트에 그러한 코드를 매번 동일하게 할 필요는 없습니다.

  1. Vue.mixin({
  2. mounted : function() {
  3. $(this.$options.el).show();
  4. }
  5. });

DOM 셀렉터를 편하게 처리하기 위해 JQuery를 이용했지만 바닐라JS로도 충분히 가능합니다. 이제 개발자가 일일이 수작업을 하지 않아도 파싱된 형태로 뷰 영역이 화면에 보이게 될 것입니다.

참고
믹스인 : https://kr.vuejs.org/v2/guide/mixins.html
라이프사이클 : https://kr.vuejs.org/v2/guide/instance.html#%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4-%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8