[Vue] 8. Vue 컴포넌트 재활용 & 공통 함수 개발 - 1) 믹스인(Mixins)

38613 단어 vuevue

믹스인(Mixins)

믹스인(Mixins)은 vue 프로젝트 내에서 공통 함수를 만들 수 있게 해주는 기능이다.

우리가 저번에 ServerData.vue 파일에서 mock 서버에 있는 데이터를 호출해서 이용하는 코드를 짠 적이 있는데 그때 api라는 함수를 만들어서 사용했었다. 그런데 이 함수는 이 화면에서만 필요한 게 아니고 거의 모든 화면에서 이 함수를 사용한다. 그래서 이 함수를 공통 함수로 만들어서 사용하려고 한다. 이때, 믹스인(Mixins)를 활용해서 해보려고 한다.

mixin 사용하기

// ServerData.vue

async api(url, method, data) {
      return (
        await axios({
          method: method,
          url: url,
          data: data,
        }).catch((e) => {
          console.log(e);
        })
      ).data;
    },

main.js와 같은 경로에 api.js 파일을 생성해준다.

그리고 api 함수를 여기에 정의해준다. 이름은 $callAPI로 바꿔준다. 함수 이름 앞에 $ 기호를 붙이는 이유는 이 함수를 컴포넌트에서 사용할 때 그 컴포넌트에 존재하는 함수와 이름이 겹칠 수도 있기 때문이다. mixin에서 callAPI라는 함수를 가져와서 사용하는데 그 컴포넌트에도 callAPI라는 함수가 있으면 함수 오버라이딩이 일어나서 문제가 생긴다. 그래서 이름 충돌을 방지하기 위해 특이한 prefix를 붙여주는 것이다.

// api.js
import axios from "axios";

export default {
  methods: {
    async $callAPI(url, method, data) {
      return (
        await axios({
          method: method,
          url: url,
          data: data,
        }).catch((e) => {
          console.log(e);
        })
      ).data;
    },
  },
};

views 폴더에 MixinTest.vue 파일을 만들어준다.
api.js를 import 해준다.

import ApiMixin from "../api.js";

mixins에 ApiMixin을 추가해준다. 이렇게 하면 api.js에 정의한 함수 코드가 그대로 이 컴포넌트에 복사돼서 들어가게 된다.

export default {
  mixins: [ApiMixin],
};
// MixinTest.vue
<template>
  <div>
    <button type="button" @click="getProductList">조회</button>
    <table>
      <thead>
        <tr>
          <th>제품명</th>
          <th>가격</th>
          <th>카테고리</th>
          <th>배송료</th>
        </tr>
      </thead>
      <tbody>
        <tr :key="i" v-for="(product, i) in productList">
          <td>{{ product.product_name }}</td>
          <td>{{ product.price }}</td>
          <td>{{ product.category }}</td>
          <td>{{ product.delivery_price }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import ApiMixin from "../api.js";

export default {
  mixins: [ApiMixin],
  components: {},
  data() {
    return {
      productList: [],
    };
  },
  setup() {},
  created() {},
  mounted() {},
  unmounted() {},
  methods: {
    async getProductList() {
      this.productList = await this.$callAPI(
        "https://a1e284c5-db6b-4726-a0be-a72a59f81862.mock.pstmn.io/productList",
        "get",
        {}
      );
      console.log(this.productList);
    },
  },
};
</script>

<style scoped></style>

routes에 등록해준다.

// index.js
const routes = [
  ...,
  {
    path: "/mixin",
    name: "MixinTest",
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/MixinTest.vue"),
  },
];

ServeData.vue에서 만들었던 결과와 똑같이 나오는 것을 확인할 수 있다.


mixin mounted와 component mounted의 순서

mixin 함수를 정의하는 파일에서 mounted하는 것과 component에서 mounted하는 것의 순서가 어떻게 되는지 알아보자.

api.js의 mounted에 console 창에 프린트하는 코드를 넣어준다.

// api.js
import axios from "axios";

export default {
  mounted() {
    console.log("믹스인 mounted");
  },
  methods: {
    async $callAPI(url, method, data) {
      return (
        await axios({
          method: method,
          url: url,
          data: data,
        }).catch((e) => {
          console.log(e);
        })
      ).data;
    },
  },
};

MixinTest.vue의 mounted에도 console창에 프린트하는 코드를 넣어준다.

// MixinTest.vue
export default {
  ..., // 이전 코드 생략
  mounted() {
    console.log("컴포넌트 mounted");
  },
};

mixin에 있는 mounted 코드가 먼저 실행되고 그 다음에 component에 있는 mounted 코드가 실행되는 것을 볼 수 있다.


lifecycle을 이용하는 함수를 mixin을 사용해서 만들기

vue lifecycle은 다음과 같다. 사용자가 그 컴포넌트에 머무르는 시간을 측정하는 함수를 만들려고 한다. 그럼 mounted에서 시간을 측정하고 unmounted에서 시간을 측정해서 빼주면 사용자가 컴포넌트에 머무르는 시간을 측정할 수 있다. 그런데 이 코드를 모든 컴포넌트마다 짜기는 불편하다. 그래서 mixin을 사용해서 만들어보려고 한다.

monitoring.js 파일을 main.js와 같은 경로에 생성하고 머무르는 시간을 계산하는 코드를 작성했다고 가정하자.

// monitoring.js
export default {
  mounted() {
      // 데이터베이스에 시간 저장
  },
  unmounted() {
      // 데이터베이스에 시간 저장
  },
};

MixinTest.vue 파일에 monitoring.js를 import해주고 mixins에 추가해준다.

// MixinTest.vue
<script>
import ApiMixin from "../api.js";
import MonitoringMixin from "../monitoring.js";

export default {
  mixins: [ApiMixin, MonitoringMixin],
};
</script>

mixin을 Global로 선언해주기

사실 $callAPI같은 함수는 거의 모든 컴포넌트에서 사용한다. 그런데 이걸 모든 컴포넌트에서 매번 import 해주는 것도 사실 굉장히 귀찮은 일이다. 그런 경우에 이것을 global로 등록해두고 사용할 수 있다.

mixins.js 파일을 main.js와 같은 경로에 만들고 $callAPI 함수를 똑같이 복사해서 넣어준다. 그런데 함수 이름은 $api라고 하자.

// mixins.js
import axios from "axios";

export default {
  methods: {
    async $api(url, method, data) {
      return (
        await axios({
          method: method,
          url: url,
          data: data,
        }).catch((e) => {
          console.log(e);
        })
      ).data;
    },
  },
};

main.js에 mixins를 import해주고 사용 설정을 해준다.

import mixins from "./mixins";
app.mixin(mixins);
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import mixins from "./mixins";

const app = createApp(App);
app.use(router);
app.mixin(mixins);
app.mount("#app");

그러면 MixinTest.vue에서 import를 하지 않아도 바로 사용할 수가 있다. 기존에 import하는 코드와 mixins에 ApiMixin을 추가해줬던 것을 지우고 다시 실행해보자.

// MixinTest.vue
<template>
  <div>
    <button type="button" @click="getProductList">조회</button>
    <table>
      <thead>
        <tr>
          <th>제품명</th>
          <th>가격</th>
          <th>카테고리</th>
          <th>배송료</th>
        </tr>
      </thead>
      <tbody>
        <tr :key="i" v-for="(product, i) in productList">
          <td>{{ product.product_name }}</td>
          <td>{{ product.price }}</td>
          <td>{{ product.category }}</td>
          <td>{{ product.delivery_price }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
// import ApiMixin from "../api.js";
import MonitoringMixin from "../monitoring.js";

export default {
  mixins: [MonitoringMixin],
  components: {},
  data() {
    return {
      productList: [],
    };
  },
  setup() {},
  created() {},
  mounted() {
    console.log("컴포넌트 mounted");
  },
  unmounted() {},
  methods: {
    async getProductList() {
      this.productList = await this.$api(
        "https://a1e284c5-db6b-4726-a0be-a72a59f81862.mock.pstmn.io/productList",
        "get",
        {}
      );
      console.log(this.productList);
    },
  },
};
</script>

<style scoped></style>

똑같이 잘 실행되는 것을 확인할 수 있다.

mixin을 global로 사용할 때 주의할 점

쓸데없는 함수들을 모두 global로 선언해놓으면 컴포넌트에서 그 함수를 실제로 사용하지도 않는데 함수 코드가 모든 컴포넌트에 전부 복사되어서 비효율적으로 될 수 있다. 그러니 거의 모든 컴포넌트에서 사용하는 함수 정도만 global로 선언해놓고 나머지는 그냥 import 해서 사용하는 것을 추천한다.

좋은 웹페이지 즐겨찾기