컴포저블을 사용하여 Vue.js에서 로직을 재사용하는 방법

프로그래밍 작업에서 코드의 재사용 가능성은 매우 중요한 요소입니다. 코드 재사용을 통해 코드베이스를 효율적으로 관리하고, 불필요한 코드 중복을 피할 수 있으며, 전반적인 개발 생산성을 향상시킬 수 있습니다. 특히 대규모 애플리케이션에서는 코드 재사용이 더욱 중요해지며, 이를 통해 복잡성을 줄이고 디버깅을 간소화할 수 있습니다.

Vue.js는 컴포저블이라는 강력한 기능을 통해 코드 재사용을 효율적으로 관리할 수 있도록 지원합니다. 컴포저블은 특정 로직을 캡슐화하는 함수로, 애플리케이션 전반에서 재사용하여 유사한 기능을 수행할 수 있습니다. 이를 통해 코드의 일관성을 유지하고 개발 시간을 단축할 수 있습니다.

과거에는 항상 컴포저블이었을까요?

Vue 3에서 컴포저블이 도입되기 이전에는 믹스인이라는 개념이 존재했습니다. 믹스인은 컴포넌트 로직을 캡처하여 여러 컴포넌트에서 재사용할 수 있도록 하는 기능입니다. 믹스인은 데이터, 메서드, 라이프사이클 훅 등 Vue.js 옵션을 포함하여 여러 컴포넌트에서 코드를 공유할 수 있도록 했습니다.

믹스인을 사용하려면, 우선 믹스인 로직을 별도의 파일로 구성해야 합니다. 그리고 나서, 해당 믹스인을 사용하려는 컴포넌트의 옵션 객체 내 `mixins` 속성에 추가하여 적용할 수 있었습니다. 예를 들어, 다음 코드는 양식 유효성 검사를 위한 믹스인의 예시를 보여줍니다.

export const formValidationMixin = {
  data() {
    return {
      formData: {
        username: '',
        password: '',
      },
      formErrors: {
        username: '',
        password: '',
      },
    };
  },
  methods: {
    validateForm() {
      this.formErrors = {};

      if (!this.formData.username.trim()) {
        this.formErrors.username="Username is required.";
      }

      if (!this.formData.password.trim()) {
        this.formErrors.password = 'Password is required.';
      }

      return Object.keys(this.formErrors).length === 0;
    },
  },
};

이 코드 조각에서, `formValidationMixin`은 양식 데이터를 저장하는 `formData` 객체와 유효성 검사 오류를 저장하는 `formErrors` 객체를 포함하고 있습니다. 초기값은 모두 비어 있는 문자열로 설정되어 있습니다.

`validateForm` 메서드는 `formData`의 사용자 이름과 비밀번호 필드의 유효성을 검사합니다. 만약 필드가 비어있으면, 해당하는 오류 메시지를 `formErrors` 객체에 추가합니다. 만약 모든 필드가 유효하면, `formErrors` 객체가 비어있을 때 true를 반환합니다.

이 믹스인은 Vue 컴포넌트로 가져와서 Options 객체의 `mixin` 속성에 추가하여 사용할 수 있습니다. 아래 코드는 믹스인을 사용하는 컴포넌트의 예시입니다.

<template>
  <div>
    <form @submit.prevent="submitForm">
      <div>
        <label for="username">Username:</label>
        <input type="text" id="username" v-model="formData.username" />
        <span class="error">{{ formErrors.username }}</span>
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" id="password" v-model="formData.password" />
        <span class="error">{{ formErrors.password }}</span>
      </div>
      <button type="submit">Submit</button>
    </form>
  </div>
</template>

<script>
import { formValidation } from "./formValidation.js";

export default {
  mixins: [formValidation],
  methods: {
    submitForm() {
      if (this.validateForm()) {
        alert("Form submitted successfully!");
      } else {
        alert("Please correct the errors in the form.");
      }
    },
  },
};
</script>

<style>
.error {
  color: red;
}
</style>

이 코드는 Options API 방식으로 작성된 Vue 컴포넌트를 보여줍니다. `mixins` 속성은 가져온 모든 믹스인을 포함하며, 이 경우 컴포넌트는 `formValidation` 믹스인의 `validateForm` 메서드를 사용하여 폼 제출 성공 여부를 사용자에게 알립니다.

컴포저블은 어떻게 사용할까요?

컴포저블은 특정 문제나 요구 사항에 맞춰 기능을 구현한 독립적인 JavaScript 파일입니다. 컴포저블은 `ref`나 `computed`와 같은 기능을 활용하여 Vue의 Composition API를 사용할 수 있습니다. Composition API에 대한 접근을 통해 여러 컴포넌트에서 재사용 가능한 기능을 만들 수 있습니다. 이러한 함수들은 Composition API의 `setup` 함수를 통해 Vue 컴포넌트로 쉽게 가져오고 통합할 수 있는 객체를 반환합니다.

컴포저블을 사용하려면, 프로젝트의 `src` 디렉토리에 새 JavaScript 파일을 만들어야 합니다. 대규모 프로젝트에서는 `src` 내에 폴더를 구성하고 다양한 컴포저블에 대한 별도의 JavaScript 파일을 생성하는 것이 좋습니다. 각 컴포저블의 이름은 해당 목적을 반영해야 합니다.

JavaScript 파일 내에서 필요한 기능을 정의합니다. 다음은 위에서 본 `formValidation` 믹스인을 컴포저블로 재구성한 예시입니다.

import { reactive } from 'vue';

export function useFormValidation() {
  const state = reactive({
    formData: {
      username: '',
      password: '',
    },
    formErrors: {
      username: '',
      password: '',
    },
  });

  function validateForm() {
    state.formErrors = {};

    if (!state.formData.username.trim()) {
      state.formErrors.username="Username is required.";
    }

    if (!state.formData.password.trim()) {
      state.formErrors.password = 'Password is required.';
    }

    return Object.keys(state.formErrors).length === 0;
  }

  return {
    state,
    validateForm,
  };
}

이 코드 스니펫은 Vue 패키지에서 `reactive` 함수를 가져오는 것으로 시작합니다. 그런 다음 `useFormValidation()` 함수를 생성하고 export합니다. 이 함수는 `formData`와 `formErrors` 속성을 포함하는 `reactive` 변수인 `state`를 생성합니다. `validateForm` 함수는 믹스인과 매우 유사한 방식으로 폼 유효성 검사를 처리하며, 마지막으로 `state` 변수와 `validateForm` 함수를 객체로 반환합니다.

컴포넌트 파일에서 이 자바스크립트 함수를 가져와서 컴포저블을 사용할 수 있습니다.

<template>
  <div>
    <form @submit.prevent="submitForm">
      <div>
        <label for="username">Username:</label>
        <input type="text" id="username" v-model="state.formData.username" />
        <span class="error">{{ state.formErrors.username }}</span>
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" id="password" v-model="state.formData.password" />
        <span class="error">{{ state.formErrors.password }}</span>
      </div>
      <button type="submit">Submit</button>
    </form>
  </div>
</template>

<script setup>
import { useFormValidation } from "./formValidation.js";
import { ref } from "vue";

const { state, validateForm } = useFormValidation();

const submitForm = () => {
  if (validateForm()) {
    alert("Form submitted successfully!");
  } else {
    alert("Please correct the errors in the form.");
  }
};
</script>

<style>
.error {
  color: red;
}
</style>

이 코드는 `useFormValidation` 컴포저블을 가져온 후 반환되는 JavaScript 객체의 구조를 비구조화하여 폼 유효성 검사를 계속 처리합니다. 제출된 폼이 성공했는지, 아니면 오류가 있는지 사용자에게 알립니다.

컴포저블은 새로운 믹스인입니다.

Vue 2에서 믹스인은 코드를 재사용하는 데 유용했지만, Vue 3에서는 컴포저블이 이러한 역할을 대체합니다. 컴포저블은 Vue.js 애플리케이션에서 로직을 재사용하는 데 있어 보다 구조적이고 유지 관리하기 쉬운 접근 방식을 제공하며, 이를 통해 Vue를 사용하여 확장 가능한 웹 앱을 보다 쉽게 구축할 수 있도록 합니다.