Vuejs - 더 이상 믹스인을 사용하면 안됩니다.

Corner·2022년 7월 19일
1

Vue.js 실무

목록 보기
7/14
post-thumbnail

더 이상 믹스인을 사용하면 안됩니다.

믹스인은 Vue 2에서 컴포넌트 간에 재사용 가능한 로직을 공유하는 가장 기본적인 방법이었습니다. 하지만 이제 컴포지션 API를 사용할 수 있게 되면서 훨씬 더 깔끔한 방식으로 코드 재사용성을 달성할 수 있습니다.

믹스인이란?(Mixin)

믹스인은 믹스인의 속성을 컴포넌트와 병합하여 컴포넌트 간의 기능을 재사용하는 방법입니다. 특정 논리를 한 번 정의하고 여러 컴포넌트에서 사용할 수 있습니다.

왜 믹스인을 사용하면 안될까요?

믹스인은 몇 가지 단점이 있기 때문에 일반적으로 논리를 공유하는 이상적인 방법이 아닙니다.

겹치는 이름

이름 충돌은 종종 mixin에서 발생합니다. 병합 프로세스 동안 일부 병합 전략에 따라 믹스인과 컴포넌트 간의 충돌 속성이 재정의됩니다. 이로 인해 애플리케이션에서 예기치 않은 동작이 발생할 수 있습니다.

타이트 커플링

믹스인과 컴포넌트 간에 암시적 종속성을 만나는 것은 드문 일이 아닙니다. 이것은 기존 코드를 깨뜨리지 않고 컴포넌트나 믹스인을 리팩토링하는 것을 극도로 어렵게 만듭니다.

믹스인은 또한 새로운 요구 사항이 등장함에 따라, 시간이 지남에 따라 다른 믹스인과 긴밀하게 결합될 수 있습니다.

이해하고 디버그하기 어려움

새로운 개발자가 믹스인이 많은 코드 기반을 이해하는 것은 매우 어렵습니다..

특히 글로벌(global)로 등록된 믹스인이 있는 경우 속성이 어디에서 오는지 명확하지 않습니다. 애플리케이션이 성장함에 따라 버그를 격리하는 것도 악몽이 됩니다.

믹스인을 컴포저블로 대체

믹스인의 몇 가지 결함과 이러한 문제를 Composition API로 해결하는 방법을 설명하는 예제부터 시작하겠습니다.

컴포넌트의 주요 내용을 표시하기 전에 몇 초 동안 기다리는 간단한 카운트다운 타이머가 있다고 가정해 보겠습니다. 이 로직은 믹스인으로 추출했습니다.

countDownMixin.js 코드는 다음과 같습니다.

const countDownMixin = {
  data() {
    return {
      countDown: 10,
      interval: null,
    };
  },
  computed: {
    isDone: function () {
      return this.countDown <= 0;
    },
  },
  methods: {
    decrement() {
      if (this.countDown > 0) this.countDown -= 1;
    },
  },
  mounted: function () {
    this.interval = setInterval(this.decrement, 1000);
  },
  unmounted: function () {
    clearInterval(this.interval);
  },
};

export default countDownMixin;

이 믹스인을 등록하면 카운트다운 로직을 컴포넌트에 "혼합"할 수 있습니다.

<template>
  <div>
  <h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>
  <h1 v-else>Hello World</h1>
  </div>
</template>

<script>
import countDownMixin from "./countDownMixin";
export default {
  name: "HelloWorld",
  mixins: [countDownMixin]
};
</script>

얼핏 보기엔 깔끔한 솔루션 같지만 10초가 아닌 20초를 기다리면 어떻게 될까요?

한 가지 해결책은 소비 컴포넌트의 초기 상태를 재정의하는 것 입니다.

그렇게 함으로써 컴포넌트에 정의된 데이터 속성이 최우선 순위임을 알고 충돌하는 이름을 명시적으로 만듭니다.

<template>
  <div>
  <h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>
  <h1 v-else>Hello World</h1>
  </div>
</template>

<script>
import countDownMixin from "./countDownMixin";
export default {
  name: "HelloWorld",
  mixins: [countDownMixin],
  data(){
  return {countDown:20}
  }
};
</script>

이 솔루션은 동작하지만 이를 수행 할 수있는 기능이 있으면 믹스인이 다른 옵션을 의도하지 않게 대체하는 경우에도 취약합니다. 이제 컴포넌트가 동일한 이름의 메서드가 decrement있다는 것을 모른 채 메서드를 정의하면(countDownMixin) 컴포넌트가 더 이상 예상대로 작동하지 않습니다.

<template>
  <div>
  <h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>
  <h1 v-else>Hello World</h1>
  </div>
</template>

<script>
import countDownMixin from "./countDownMixin";
export default {
  name: "HelloWorld",
  mixins: [countDownMixin],
  data(){
  return {countDown:20}
  },
  methods:{
    decrement() {
      console.log("decrement from self");
   }
  },
};
</script>

decrement 함수는 내부용으로만 정의되어 있지만 재정의를 피할수는 없습니다.

컴포지션 API를 사용한 더 나은 솔루션

import { ref, computed, onMounted, onUnmounted, readonly } from "vue";

export default function useCountDown(countDownDuration = 10) {
  const countDown = ref(countDownDuration);
  const interval = ref(null);
  const decrement = () => {
    if (countDown.value > 0) countDown.value -= 1;
  };
  const isDone = computed(() => {
    return countDown.value <= 0;
  });
  onMounted(() => {
    interval.value = setInterval(decrement, 1000);
  });
  onUnmounted(() => {
    clearInterval(interval.value);
  });

  return { countDown:readonly(countDown), isDone };
}

컴포지션 API를 사용하여 믹스인에서 직면한 모든 문제를 피할 수 있습니다. 컴포저블 useCountDown은 카운트다운 기간을 매개변수로 사용합니다. 무엇이든 재정의할 필요가 없으며 종속성은 구성 가능한 기능의 매개변수로 깔끔하게 구성됩니다.

또한 소비하는 구성 요소에서만 사용하도록 의도된 속성을 노출하고 내부 상태가 보호되므로 컴포넌트가 컴포넌트의 동작을 실수로 변경할 수 없습니다.

컴포저블의 반환된 모든 변수에 대한 이름을 명시적으로 제공해야 하므로 이름 중복도 문제가 되지 않습니다.

<template>
  <div>
  <h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>
  <h1 v-else>Hello World</h1>
  </div>
</template>

<script>
import useCountDown from "./useCountDown";
export default {
  name: "HelloWorld",
  setup(){
  const {countDown, isDone} = useCountDown();
  return {countDown, isDone};
  }
};
</script>

결론

컴포지션 API를 사용하면 코드를 매우 쉽고 간단하게 재사용할 수 있습니다.

믹스인에서 마이그레이션하고 컴포저블의 힘을 수용하기에 충분히 설득될 수 있길 바랍니다.

profile
Full-stack Engineer. email - corner3499@kakao.com,

0개의 댓글