Intro
Vue는 컴포지션 API, 옵션 API, setup 사용 유무 등 여러가지 코딩 스타일이 존재하는데, 아래 내용은 Vue 공식 문서 권장에 따라 컴포지션 API(Composition API) + setup 옵션을 적용한 스타일을 기준으로 설명한다.
https://ko.vuejs.org/api/composition-api-setup.html
Vue.js
Vue.js - The Progressive JavaScript Framework
vuejs.org
🚀 목표
- props로 값 전달하는 방법
- props 값을 구조 분해 할당하여 사용하기
✨ 기본적인 Props 전달 방법
- Vue의 모든 props는 하위 속성과 상위 속성 사이의 단방향 바인딩을 형성하므로 부모가 내려준 값을 자식이 직접 변경할 수 없다.
- 즉, props는 읽기 전용(immutable)이며, 변경하려 하면 Vue에서 경고가 발생한다! 🚨
부모 컴포넌트 (Parent.vue)
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const message = ref('안녕하세요, 자식 컴포넌트!');
</script>
<template>
<div>
<h2>부모 컴포넌트</h2>
<Child :msg="message" />
</div>
</template>
:msg="message"로 message를 Child 컴포넌트에 전달한다.
자식 컴포넌트 (Child.vue)
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
msg: String
});
</script>
<template>
<div>
<h3>자식 컴포넌트</h3>
<p>부모로부터 받은 메시지: {{ props.msg }}</p>
</div>
</template>
defineProps()를 사용해 props를 선언하고 msg 값을 받을 수 있다.
✨ Props에 기본값 & 타입 지정하기
defineProps에 타입과 기본값을 설정하여 props를 전달할 수 있다.
<script setup>
const props = defineProps({
msg: {
type: String,
default: '기본 메시지'
}
});
</script>
- type: String → msg는 문자열이어야 한다.
- default: '기본 메시지' → 부모에서 값을 안 넘겨주면 기본값이 사용된다.
✨ Props로 객체 전달하기
부모 컴포넌트 (Parent.vue)
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const user = ref({
name: '홍길동',
age: 25
});
</script>
<template>
<div>
<h2>부모 컴포넌트</h2>
<Child :userInfo="user" />
</div>
</template>
자식 컴포넌트 (Child.vue)
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
userInfo: Object
});
</script>
<template>
<div>
<h3>자식 컴포넌트</h3>
<p>이름: {{ props.userInfo.name }}</p>
<p>나이: {{ props.userInfo.age }}</p>
</div>
</template>
💡 props를 구조 분해 할당으로 꺼내 사용할 수 있을까?
위와 같이 props.msg로 사용하면 Vue가 내부적으로 반응형 시스템을 유지하기 때문에 부모의 값이 변경되면 UI도 자동으로 업데이트 되는데, 아래와 같이 msg를 구조 분해 할당 하게 되면 반응성 문제가 발생할 수 있다.
❓ 반응성 문제란?
부모에서 값이 변경되어도 자식 컴포넌트에서 UI가 자동으로 업데이트되지 않는 문제
⚠️ 반응성 문제 발생 가능성 있음
<script setup>
const { msg } = defineProps({
msg: {
type: String,
default: '기본 메시지'
}
});
</script>
<template>
<p>{{ msg }}</p> <!-- 반응성이 사라질 수 있음 -->
</template>
Vue 3에서는 defineProps()가 내부적으로 reactive() 객체를 반환하는데, 이렇게 구조 분해를 하면 반응성이 깨질 수 있다.
💡 왜 React는 구조 분해해도 반응성이 깨지지 않을까?
React에서의 props
- React에서는 컴포넌트가 리렌더링될 때마다 새로운 props가 함수 매개변수로 전달된다.
- 즉, 구조 분해를 해도 리렌더링이 발생하면 새로운 값이 전달되므로 반응성이 깨지지 않는다.
- 하지만 useState를 사용하지 않으면 반응성을 유지하지 못할 수도 있는데, useState 없이 props를 로컬 변수로 저장하면 변경 사항이 반영되지 않는다.
const Child = ({ msg }) => {
let localMsg = msg; // ❌ props 변경 시 업데이트되지 않음
return <p>{localMsg}</p>;
};
Vue에서의 props
- Vue의 defineProps()는 내부적으로 Vue의 reactive()로 감싸진 객체를 반환한다.
- 이 객체에서 구조 분해를 하면 반응형 객체에서 벗어나면서 반응성이 깨지는 것이다.
✅ 안전한 구조 분해 방법 - toRefs
props를toRefs(props)를 사용하여 구조 분해 하면 msg가 반응형 상태를 유지하므로, 부모가 msg를 업데이트하면 자식도 변경된 값을 반영한다.
toRefs : https://ko.vuejs.org/api/reactivity-utilities.html#torefs
<script setup>
import { toRefs } from 'vue';
const props = defineProps({
msg: {
type: String,
default: '기본 메시지'
}
});
const { msg } = toRefs(props);
</script>
<template>
<p>{{ msg }}</p> <!-- 반응성을 유지하면서 사용 가능 -->
</template>
✅ 타입을 명시하여 구조 분해 하기 - defineProps<T>()
defineProps<>()에서 타입을 직접 지정하면, Vue가 내부적으로 반응성을 유지하는 방식으로 처리하므로 toRefs() 없이도 안전하게 구조 분해할 수 있다.
defineProps : https://ko.vuejs.org/api/sfc-script-setup.html#defineprops-defineemits
<script setup lang="ts">
const { msg } = defineProps<{
msg?: string
}>();
</script>
<template>
<p>{{ msg }}</p> <!-- 안전하게 구조 분해 가능 -->
</template>
참고
'Vue.js' 카테고리의 다른 글
[Vue.js] VSCode import 오류가 발생하지 않는 상황 해결하기 (1) | 2025.02.18 |
---|---|
[Vue.js] TypeScript에서 '__dirname'과 'path' 모듈이 동작하지 않을 때 (0) | 2025.02.17 |
[Vue.js] 컴포넌트 렌더링 시 v-if & v-show & component 비교 (0) | 2025.02.12 |
[Vue.js] computed & watch 차이점 (feat. watchEffect) (0) | 2025.02.12 |
[Vue.js] v-for 사용시 key 값으로 index를 사용하지 않는 이유 (0) | 2025.02.07 |
Intro
Vue는 컴포지션 API, 옵션 API, setup 사용 유무 등 여러가지 코딩 스타일이 존재하는데, 아래 내용은 Vue 공식 문서 권장에 따라 컴포지션 API(Composition API) + setup 옵션을 적용한 스타일을 기준으로 설명한다.
https://ko.vuejs.org/api/composition-api-setup.html
Vue.js
Vue.js - The Progressive JavaScript Framework
vuejs.org
🚀 목표
- props로 값 전달하는 방법
- props 값을 구조 분해 할당하여 사용하기
✨ 기본적인 Props 전달 방법
- Vue의 모든 props는 하위 속성과 상위 속성 사이의 단방향 바인딩을 형성하므로 부모가 내려준 값을 자식이 직접 변경할 수 없다.
- 즉, props는 읽기 전용(immutable)이며, 변경하려 하면 Vue에서 경고가 발생한다! 🚨
부모 컴포넌트 (Parent.vue)
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const message = ref('안녕하세요, 자식 컴포넌트!');
</script>
<template>
<div>
<h2>부모 컴포넌트</h2>
<Child :msg="message" />
</div>
</template>
:msg="message"로 message를 Child 컴포넌트에 전달한다.
자식 컴포넌트 (Child.vue)
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
msg: String
});
</script>
<template>
<div>
<h3>자식 컴포넌트</h3>
<p>부모로부터 받은 메시지: {{ props.msg }}</p>
</div>
</template>
defineProps()를 사용해 props를 선언하고 msg 값을 받을 수 있다.
✨ Props에 기본값 & 타입 지정하기
defineProps에 타입과 기본값을 설정하여 props를 전달할 수 있다.
<script setup>
const props = defineProps({
msg: {
type: String,
default: '기본 메시지'
}
});
</script>
- type: String → msg는 문자열이어야 한다.
- default: '기본 메시지' → 부모에서 값을 안 넘겨주면 기본값이 사용된다.
✨ Props로 객체 전달하기
부모 컴포넌트 (Parent.vue)
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const user = ref({
name: '홍길동',
age: 25
});
</script>
<template>
<div>
<h2>부모 컴포넌트</h2>
<Child :userInfo="user" />
</div>
</template>
자식 컴포넌트 (Child.vue)
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
userInfo: Object
});
</script>
<template>
<div>
<h3>자식 컴포넌트</h3>
<p>이름: {{ props.userInfo.name }}</p>
<p>나이: {{ props.userInfo.age }}</p>
</div>
</template>
💡 props를 구조 분해 할당으로 꺼내 사용할 수 있을까?
위와 같이 props.msg로 사용하면 Vue가 내부적으로 반응형 시스템을 유지하기 때문에 부모의 값이 변경되면 UI도 자동으로 업데이트 되는데, 아래와 같이 msg를 구조 분해 할당 하게 되면 반응성 문제가 발생할 수 있다.
❓ 반응성 문제란?
부모에서 값이 변경되어도 자식 컴포넌트에서 UI가 자동으로 업데이트되지 않는 문제
⚠️ 반응성 문제 발생 가능성 있음
<script setup>
const { msg } = defineProps({
msg: {
type: String,
default: '기본 메시지'
}
});
</script>
<template>
<p>{{ msg }}</p> <!-- 반응성이 사라질 수 있음 -->
</template>
Vue 3에서는 defineProps()가 내부적으로 reactive() 객체를 반환하는데, 이렇게 구조 분해를 하면 반응성이 깨질 수 있다.
💡 왜 React는 구조 분해해도 반응성이 깨지지 않을까?
React에서의 props
- React에서는 컴포넌트가 리렌더링될 때마다 새로운 props가 함수 매개변수로 전달된다.
- 즉, 구조 분해를 해도 리렌더링이 발생하면 새로운 값이 전달되므로 반응성이 깨지지 않는다.
- 하지만 useState를 사용하지 않으면 반응성을 유지하지 못할 수도 있는데, useState 없이 props를 로컬 변수로 저장하면 변경 사항이 반영되지 않는다.
const Child = ({ msg }) => {
let localMsg = msg; // ❌ props 변경 시 업데이트되지 않음
return <p>{localMsg}</p>;
};
Vue에서의 props
- Vue의 defineProps()는 내부적으로 Vue의 reactive()로 감싸진 객체를 반환한다.
- 이 객체에서 구조 분해를 하면 반응형 객체에서 벗어나면서 반응성이 깨지는 것이다.
✅ 안전한 구조 분해 방법 - toRefs
props를toRefs(props)를 사용하여 구조 분해 하면 msg가 반응형 상태를 유지하므로, 부모가 msg를 업데이트하면 자식도 변경된 값을 반영한다.
toRefs : https://ko.vuejs.org/api/reactivity-utilities.html#torefs
<script setup>
import { toRefs } from 'vue';
const props = defineProps({
msg: {
type: String,
default: '기본 메시지'
}
});
const { msg } = toRefs(props);
</script>
<template>
<p>{{ msg }}</p> <!-- 반응성을 유지하면서 사용 가능 -->
</template>
✅ 타입을 명시하여 구조 분해 하기 - defineProps<T>()
defineProps<>()에서 타입을 직접 지정하면, Vue가 내부적으로 반응성을 유지하는 방식으로 처리하므로 toRefs() 없이도 안전하게 구조 분해할 수 있다.
defineProps : https://ko.vuejs.org/api/sfc-script-setup.html#defineprops-defineemits
<script setup lang="ts">
const { msg } = defineProps<{
msg?: string
}>();
</script>
<template>
<p>{{ msg }}</p> <!-- 안전하게 구조 분해 가능 -->
</template>
참고
'Vue.js' 카테고리의 다른 글
[Vue.js] VSCode import 오류가 발생하지 않는 상황 해결하기 (1) | 2025.02.18 |
---|---|
[Vue.js] TypeScript에서 '__dirname'과 'path' 모듈이 동작하지 않을 때 (0) | 2025.02.17 |
[Vue.js] 컴포넌트 렌더링 시 v-if & v-show & component 비교 (0) | 2025.02.12 |
[Vue.js] computed & watch 차이점 (feat. watchEffect) (0) | 2025.02.12 |
[Vue.js] v-for 사용시 key 값으로 index를 사용하지 않는 이유 (0) | 2025.02.07 |