Vue.js 를 배워보자

Vue 3 Composition API 완전 정복: ref, reactive, methods, v-model 그리고 스프레드 연산자

_Blue_Sky_ 2025. 2. 22. 17:00
728x90
728x90

 

 
VUE3의 Composition API는 Vue.js에서 컴포넌트 로직을 더 유연하고 재사용 가능하게 작성할 수 있도록 설계된 새로운 API입니다. 기존의 Options API (data, methods, computed 등)와 달리, Composition API는 컴포넌트의 로직을 기능별로 묶어서 관리할 수 있게 해줍니다. 여기서는 질문에서 요청한 ref, reactive, methods, v-model에 대해 자세히 설명하겠습니다.

1. ref
  • 정의: ref는 반응형 데이터(reactivity)를 제공하는 가장 기본적인 방법으로, 단일 값(원시 타입 또는 객체)을 반응형으로 만들 때 사용됩니다.
  • 사용법: ref로 정의된 값은 .value 속성을 통해 접근합니다. 템플릿에서는 자동으로 .value가 생략됩니다.
  • 예제:
     
    import { ref } from 'vue';
    
    export default {
      setup() {
        const count = ref(0); // 반응형 변수 생성
    
        const increment = () => {
          count.value++; // .value로 값 변경
        };
    
        return {
          count, // 템플릿에서 {{ count }}로 사용 가능
          increment,
        };
      },
    };
  • 특징:
    • 원시 타입(숫자, 문자열 등)과 객체 모두 감쌀 수 있음.
    • .value를 통해 내부 값에 접근하지만, 템플릿에서는 생략 가능.
    • 반응형 프록시로 작동하며 값이 변경되면 UI가 자동으로 업데이트됨.

2. reactive
  • 정의: reactive는 객체 또는 배열과 같은 복합 데이터 구조를 반응형으로 만들 때 사용됩니다. ref와 달리 .value가 필요 없습니다.
  • 사용법: reactive로 감싼 객체의 속성을 직접 수정하면 반응형이 유지됩니다.
  • 예제:
     
    import { reactive } from 'vue';
    
    export default {
      setup() {
        const state = reactive({
          name: 'John',
          age: 30,
        });
    
        const updateName = () => {
          state.name = 'Jane'; // 직접 수정 가능
        };
    
        return {
          state,
          updateName,
        };
      },
    };
  • 특징:
    • 객체나 배열에만 적용 가능(원시 타입은 ref 사용).
    • 깊은 반응형(deep reactivity)을 제공하여 중첩된 속성도 반응형으로 동작.
    • reactive 객체를 재할당하면 반응형이 깨지므로 주의 (state = {}는 안 됨).

3. methods
  • 정의: Composition API에서 "methods"라는 별도의 객체는 존재하지 않습니다. 대신 setup() 함수 내에서 일반 JavaScript 함수로 로직을 정의하고, 이를 반환하여 템플릿에서 호출할 수 있습니다.
  • 사용법: 함수를 정의하고 return으로 노출하면 됩니다.
  • 예제:
     
    import { ref } from 'vue';
    
    export default {
      setup() {
        const count = ref(0);
    
        // 메소드처럼 동작하는 함수
        const increment = () => {
          count.value++;
        };
    
        const decrement = () => {
          count.value--;
        };
    
        return {
          count,
          increment,
          decrement,
        };
      },
    };
  • 템플릿에서 사용:
     
    <button @click="increment">증가</button>
    <button @click="decrement">감소</button>
    <p>Count: {{ count }}</p>
  • 특징:
    • Options API의 methods와 달리, 함수가 setup() 안에서 정의되며, 반응형 데이터(ref, reactive)와 결합해 사용.
    • 로직을 기능별로 분리하거나 재사용 가능한 컴포저블(composable)로 만들기 쉬움.

4. v-model
  • 정의: v-model은 양방향 데이터 바인딩을 제공하는 디렉티브로, Composition API에서도 동일하게 사용됩니다. ref와 결합하여 입력 요소와 반응형 데이터를 연결합니다.
  • 사용법: v-model은 내부적으로 valueinput 이벤트를 사용하며, ref로 정의된 변수와 연결됩니다.
  • 예제:
     
    import { ref } from 'vue';
    
    export default {
      setup() {
        const inputValue = ref(''); // 반응형 변수
    
        const updateValue = () => {
          console.log('입력값:', inputValue.value);
        };
    
        return {
          inputValue,
          updateValue,
        };
      },
    };
  • 템플릿:
     
    <input v-model="inputValue" @input="updateValue" />
    <p>입력값: {{ inputValue }}</p>
  • 커스텀 컴포넌트에서 v-model:
    • 부모 컴포넌트에서 자식 컴포넌트에 v-model을 사용할 경우, 자식은 modelValue prop과 update:modelValue 이벤트를 사용.
    • 예제 (자식 컴포넌트):
       
      export default {
        props: ['modelValue'],
        emits: ['update:modelValue'],
        setup(props, { emit }) {
          const update = (event) => {
            emit('update:modelValue', event.target.value);
          };
      
          return { update };
        },
      };
       
      <input :value="modelValue" @input="update" />
    • 부모 컴포넌트:
       
      <CustomInput v-model="inputValue" />
  • 특징:
    • ref와 함께 사용하면 간단히 양방향 바인딩 구현 가능.
    • 커스텀 컴포넌트에서는 v-model을 prop과 이벤트로 확장 가능.

추가 팁
  • ref vs reactive:
    • ref는 단일 값이나 객체에 적합, .value 필요.
    • reactive는 객체/배열에 적합, 속성 직접 수정 가능.
  • 컴포저블(Composable):
    • Composition API의 강력한 기능으로, 로직을 별도의 함수로 분리해 재사용 가능.
    • 예: useCounter.js
       
      import { ref } from 'vue';
      
      export function useCounter() {
        const count = ref(0);
        const increment = () => count.value++;
        return { count, increment };
      }
       
      import { useCounter } from './useCounter';
      
      export default {
        setup() {
          const { count, increment } = useCounter();
          return { count, increment };
        },
      };

결론
Vue 3의 Composition API는 refreactive로 반응형 데이터를 관리하고, setup()에서 함수를 정의해 "methods"처럼 사용하며, v-model로 양방향 바인딩을 쉽게 구현할 수 있습니다. Options API보다 코드 구조화와 재사용성이 뛰어나며, 대규모 프로젝트에서 특히 유리합니다. 질문이 더 있다면 언제든 물어보세요!
728x90
 

 

Vue 3의 Composition API에서 스프레드 연산자(...)를 사용하는 방법을 보여드리겠습니다. 스프레드 연산자는 주로 객체 또는 배열을 복사하거나 병합할 때 유용하며, 반응형 데이터(ref, reactive)와 함께 사용할 수도 있습니다. 아래에서 몇 가지 예제를 통해 설명하겠습니다.


1. reactive와 스프레드 연산자 사용
reactive 객체의 속성을 다른 객체와 병합하거나 복사할 때 스프레드 연산자를 사용할 수 있습니다. 다만, reactive 객체 자체는 프록시로 감싸져 있으므로 스프레드 연산자를 사용하면 반응형이 풀린 일반 객체로 변환됩니다. 이를 다시 reactive로 감싸야 반응형을 유지할 수 있습니다.
예제:
 
import { reactive } from 'vue';

export default {
  setup() {
    const state = reactive({
      name: 'John',
      age: 30,
    });

    // 스프레드 연산자로 객체 병합
    const updatedState = reactive({
      ...state, // 기존 state 복사
      city: 'Seoul', // 새 속성 추가
    });

    return {
      state,
      updatedState,
    };
  },
};
템플릿:
 
<p>원본: {{ state.name }}, {{ state.age }}</p>
<p>업데이트: {{ updatedState.name }}, {{ updatedState.age }}, {{ updatedState.city }}</p>
  • 주의: const newState = { ...state }만 하면 반응형이 유지되지 않으므로, 반드시 reactive()로 다시 감싸야 합니다.

2. ref와 스프레드 연산자 사용 (객체 내부)
ref로 객체를 감싼 경우, .value를 통해 내부 객체에 접근한 뒤 스프레드 연산자를 사용할 수 있습니다.
예제:
 
import { ref } from 'vue';

export default {
  setup() {
    const user = ref({
      name: 'Jane',
      age: 25,
    });

    const updateUser = () => {
      user.value = {
        ...user.value, // 기존 값 복사
        age: 26, // 특정 속성 업데이트
      };
    };

    return {
      user,
      updateUser,
    };
  },
};
템플릿:
 
<p>{{ user.name }}, {{ user.age }}</p>
<button @click="updateUser">나이 증가</button>
  • 설명: user.value를 스프레드 연산자로 복사한 뒤 새로운 속성을 추가하거나 덮어씌웁니다. ref는 여전히 반응형을 유지합니다.

3. 배열과 스프레드 연산자 사용
refreactive로 관리되는 배열에서도 스프레드 연산자를 활용해 요소를 추가하거나 복사할 수 있습니다.
예제:
 
import { ref } from 'vue';

export default {
  setup() {
    const items = ref(['apple', 'banana']);

    const addItem = () => {
      items.value = [...items.value, 'orange']; // 배열에 새 요소 추가
    };

    return {
      items,
      addItem,
    };
  },
};
템플릿:
 
<ul>
  <li v-for="item in items" :key="item">{{ item }}</li>
</ul>
<button @click="addItem">아이템 추가</button>
  • 설명: items.value를 스프레드 연산자로 복사하고 새 요소를 추가하면 반응형 배열이 업데이트됩니다.

4. 컴포저블에서 스프레드 연산자 활용
스프레드 연산자는 컴포저블 함수의 반환값을 병합하거나 재사용할 때도 유용합니다.
예제:
 
// useUser.js
import { ref } from 'vue';

export function useUser() {
  const user = ref({ name: 'Alice', age: 28 });

  const updateName = (newName) => {
    user.value = { ...user.value, name: newName };
  };

  return { user, updateName };
}

// 컴포넌트
import { useUser } from './useUser';

export default {
  setup() {
    const { user, updateName } = useUser();

    const extendedUser = ref({
      ...user.value, // useUser에서 가져온 객체 복사
      city: 'Tokyo',
    });

    return {
      user,
      extendedUser,
      updateName,
    };
  },
};
템플릿:
 
<p>원본: {{ user.name }}, {{ user.age }}</p>
<p>확장: {{ extendedUser.name }}, {{ extendedUser.age }}, {{ extendedUser.city }}</p>
<button @click="updateName('Bob')">이름 변경</button>
  • 설명: useUser에서 가져온 반응형 데이터를 스프레드 연산자로 확장하여 새로운 객체를 만듭니다. 단, extendedUser는 독립적인 ref이므로 user 변경 시 연동되지 않음에 주의하세요.

주의사항
  1. 반응형 유지: reactive 객체에 스프레드 연산자를 사용하면 반응형이 깨지므로, 결과물을 reactive()로 다시 감싸야 합니다.
  2. 얕은 복사: 스프레드 연산자는 얕은 복사(shallow copy)를 수행하므로, 중첩된 객체는 참조가 유지됩니다. 깊은 복사가 필요하면 별도의 로직(예: JSON.parse(JSON.stringify()))을 사용하세요.
  3. 성능: 대량의 데이터를 다룰 때는 스프레드 연산자가 성능에 영향을 줄 수 있으니 적절히 사용하세요.

728x90
위 예제들을 통해 ref, reactive, 배열, 컴포저블 등 다양한 상황에서 스프레드 연산자를 사용하는 방법을 확인할 수 있습니다. 추가로 궁금한 점이 있다면 말씀해주세요!

728x90
728x90