계발하는 개발자

[Vue.js] 컴포넌트간 공통기능 메서드를 묶는 Mixins(믹스인) 본문

💻 Frontend/Vue.js

[Vue.js] 컴포넌트간 공통기능 메서드를 묶는 Mixins(믹스인)

dev_genie 2023. 8. 16. 18:23

뷰로 컴포넌트를 만들다보면 시간이 지날수록 관리해야 하는 컴포넌트 수가 많아지게 되는데

그러다보면 기능적으로 겹치게 되는 메서드도 생기게 마련이다.

 

이러한 문제를 해결해주기 위해 존재하는 게 바로 믹스인 되시겠다.

믹스인 옵션을 사용하면 컴포넌트간 중첩되는 공통 기능들을 말그대로 하나로 믹스시켜 해당 기능을 한 번만 정의하고도

컴포넌트 단에서 재사용해서 쓸 수가 있다.

 

이렇게 말만 하면 입 아프니까 코드를 비교해서 보자.

1. 믹스인 사용 전 문제 상황 : 컴포넌트간 메서드 기능이 중첩되는 상황

// 뷰 인스턴스
new Vue({
  el: "#cont",
  store,
  methods: {
    // 하위 컴포넌트에서 접근하기 위해 인스턴스에 등록 !!
    // 세자리 콤마 함수
    numberWithCommas(x) {
        return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    },
    // 배열데이터 순번 셋업해주는 메서드
    chgList(pm) {
      store.state.setNumber = pm;
    },
  },
  
// [10] 뷰컴포넌트 - 메인 섹션2 베스트상품 컴포넌트
Vue.component("mb-comp", {
  template: mainData.bestarea,
  methods: {
    // instance 배열 순번 셋업함수 호출
    setNum(val) {
      return this.$root.chgList(val);
    },
    // instance 세자리콤마함수 호출
    setComma(val) {
      return this.$root.numberWithCommas(val);
    },
  },
  
  // [11] 뷰컴포넌트 - 메인 섹션4 신제품 컴포넌트
Vue.component("mn-comp", {
  template: mainData.newarea,
  methods: {
    // instance 배열 순번 셋업함수 호출
    setNum2(val) {
      return this.$root.chgList(val);
    },
    // instance 세자리콤마함수 호출
    setComma2(val) {
      return this.$root.numberWithCommas(val);
    },
  },

위에서 보다시피 처음에 믹스인을 몰랐을 때는,

#cont 라는 root 인스턴스 메서드 구역에 각각의 기능을 하는 함수를 전역 등록해놓고

이 함수를 하위 자식 컴포넌트들에서 this.$root.함수명 해서 호출하는 방식으로 사용했었다.

처음엔 좋았다. 그런데.. 문제는 컴포넌트가 많아질수록 굳이 공통 기능을 하는 다른 함수를 또 만들어 그 함수를 통해 호출했어야 됐다.

이렇다보니 일단 코드가 복잡해지고, 함수 안에서 또 다른 함수를 콜백 호출하는 것이다보니 성능적으로도 좋지 않았을 것 같다.

 

이 문제를 어떻게 해결할 수 있지? 구글링과 뷰 가이드문서를 다시 읽어본 끝에 믹스인이라는 컴포넌트 옵션을 알게 되었고, 이를 사용해보았다.

 

믹스인 — Vue.js

Vue.js - 프로그레시브 자바스크립트 프레임워크

v2.ko.vuejs.org

믹스인 사용 방법에 대해서는 가이드에서 상세한 예시와 함께 나와있어서 적용이 어렵진 않았다 :D

근데 vue2 버전에서는 믹스인이 잘 쓰였는데, vue3 버전이 업데이트 되고 난후로는 컴포지션 API 라는 게 좀더 일반적으로 쓰이는 추세인 것 같기는 하다!

 

2. 믹스인 사용 후 - 컴포넌트 공통 기능을 하나의 함수로 일원화

/************* 공통기능 함수 *************/
const crossMixin = {
  methods: {
    // 세자리 콤마찍기 함수
    setComma(val) {
      return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    },
    // 배열 순번 셋업함수
    setNum(pm) {
      return store.state.setNumber = pm;
    },
  },
};
//////////////////////////////////////////////////

// [10] 뷰컴포넌트 - 메인 섹션2 베스트상품 컴포넌트
Vue.component("mb-comp", {
  template: mainData.bestarea,
  mixins: [crossMixin],
},

// [11] 뷰컴포넌트 - 메인 섹션4 신제품 컴포넌트
Vue.component("mn-comp", {
  template: mainData.newarea,
  mixins: [crossMixin],
},

 

코드가 더 깔끔해지고, 해당 기능을 어디서 참조하는지가 명확해졌다. 

서로 다른 컴포넌트들간에 모두 crossMixin 이라는 하나의 객체를 mixins로 참조하고 있다.

// 세자리 콤마함수 필요한 부분
<div class="btxt2">
    <span class="original-price">
    <em>{{setComma(a.oprice)}}</em>
    <span v-if="a.oprice">원</span>
    </span>
    <br>
    <span class="discount-price">
    <em>{{setComma(a.dprice)}}</em>
    <span>원</span>
    </span>
    <span class="percent-price" v-if="a.oprice && a.dprice">
    <em>{{((a.oprice - a.dprice) / a.oprice * 100).toFixed(0)}}%</em>
    </span>
</div>

// 배열 순번 셋업함수 필요한 부분
<dl class="mbtit2">
    <dd v-for="(v,i) in notelnb" :key="i">
        <a href="#" @click.prevent="setNum(i)">{{v}}</a>
    </dd>
</dl>

crossMixin 객체를 믹스인 받았으니, 이제 출력될 컴포넌트 템플릿에서 crossMixin 객체 안의 접근할 메서드명(전달 인수) 넘겨주면 호출된다.

setComma 함수 실행된 모습(가격에 콤마 표시)
setNum 함수 실행된 모습(lnb 클릭시 해당 순번에 셋업된 데이터를 출력)

잘 실행된다 !!!!!!!

각각의 mb-comp와 mn-comp의 <template> 안에서 자신의 컴포넌트에 존재 하지 않는 메서드 기능을 사용해 렌더링하려 하지만 crossMixin에 해당 메서드가 정의되어있기 때문에 오류 없이 믹스인을 사용해 렌더링 되는 것을 볼 수 있다.

아래는 믹스인 관련해서 좀더 찾아서 정리한 내용이다.

 

# 믹스인(Mixins)

  • 믹스인이란?
    • 재사용이 필요한 요소들을 모아 놓은 객체를 의미하며,
    • 믹스인으로 공통 관심사를 분리하여 정의하고 필요로하는 컴포넌트에서 가져다 쓸 수 있다.
  • 특징1
    • Mixin 은 컴포넌트 우선으로 병합된다.
      • Mixin에 data 와 methods 가 정의되어있고, 같은 이름으로 컴포넌트에도 data, methods 되어있다면 중복되는 값은 어떻게 처리 될까? methods, directive, components, data 와 같이 객체 값을 요구하는 속성은 컴포넌트에 있는 요소를 우선으로 병합한다.
/************* 공통기능 함수 *************/
const crossMixin = {
  methods: {
    // 배열 순번 셋업함수
    setNum(pm) {
        console.log('나는 mixIn !!!')
      return store.state.setNumber = pm;
    },
  },
};

// [10] 뷰컴포넌트 - 메인 섹션2 베스트상품 컴포넌트
Vue.component("mb-comp", {
  template: mainData.bestarea,
  mixins: [crossMixin],
  methods: {
    // 배열 순번 셋업함수
    setNum(pm) {
        console.log('나는 component !!!')
      return store.state.setNumber = pm;
    },
  },

}); //////////////////// Vue 컴포넌트 ///////////////////////

콘솔을 찍고 확인해보면 Mixin의 콘솔이 아닌 컴포넌트의 콘솔이 먼저 호출된게 확인된다.

컴포넌트에 없는 data는 Mixin의 것을, 가지고 있는 data는 컴포넌트 것을 사용한다는 것을 알 수 있다. 

 

  • 특징2
    • Vue에서 Mixin의 훅은 컴포넌트의 훅 이전에 호출된다.
/************* 공통기능 함수 *************/
const crossMixin = {
  methods: {
    // 배열 순번 셋업함수
    setNum(pm) {
      return store.state.setNumber = pm;
    },
  },
  created() {
   console.log("crossMixin")
 }
};

// [10] 뷰컴포넌트 - 메인 섹션2 베스트상품 컴포넌트
Vue.component("mb-comp", {
  template: mainData.bestarea,
  mixins: [crossMixin],
 created() {
   console.log("mb-comp")
 }
}); //////////////////// Vue 컴포넌트 ///////////////////////

mixIn의 created 훅이 우선 실행된후, 컴포넌트의 created 훅이 실행된 모습

 

  • 믹스인을 사용하지 않을 경우 발생하는 문제 상황
    • 기능이 같은 코드가 중복된다.

  • 믹스인 동작 순서: 
    1. 믹스인할 객체를 만든다.
    2. 컴포넌트에 객체를 믹스인한다.
    3. 나머지 부분을 구현해 완성한다.

  • 믹스인을 사용함으로써 얻는 개선점
    • 같은 코드를 반복하지 않고, 특정 기능을 나타내는 객체를 캡슐화해서 재사용할 수 있다.
    • 유지보수 및 협업 측면에서 훌륭한 구조를 갖출 수 있다.

 


# 짧은 회고

어제 공통기능 함수를 묶어서 리팩토링한 회고를 포스팅했는데 이렇게 또 공통기능으로 이런 글을 쓰게 될 줄 몰랐다.

그만큼 내가 요즘 중요성을 실감하는 부분 중 하나다.

실제로 공통기능을 하는 것들끼리 잘 묶어만 놔도 동작이 훨씬 부드러워지고, 또 가독성이 좋아 유지보수도 편한 것을 볼 수 있었다. 그리고 뭣보다 내 코드가 클린코드에 가까워지고 있다는 생각에 조금씩 희망이 든다!!

어찌보면 가장 바쁜 이 시기에, 이러한 경험들을 조금씩이나마 다져나간 것은 참 값진 것 같다.

또, 이번 기회에 뷰를 쓰면서 기본적으로 제공하는 다양한 옵션들을 관심을 갖고 익혀나갈 수 있었어서 이번 뷰로 진행한 2차 프로젝트는 절대 잊지 못할 것 같단 생각이 든다.ㅎㅎ

LIST
profile

dev_genie

@dev_genie

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!