
보통 컴포넌트 간에 데이터를 전달하기 위해 props을 많이 사용합니다. 하지만 계층이 깊어지면 깊어질 수록 props로 전달해야할 횟수는 증가하게됩니다. 이러한 불편함을 해소하기 위해 vue에서는 provide와 inject를 지원합니다. 자세히 알아볼까요?

Provide
문법은 간단합니다. data를 생성하듯이 provide
option을 넣어주시면 됩니다.
export default {
provide: {
message: 'hello!'
}
}
인스턴스마다 고유한 값을 가지게 하고 싶다면 함수로 선언해주세요.
export default {
provide() {
return {
message: 'hello!';
}
}
}
data의 값을 사용하기
provide를 추가할 컴포넌트에서 단순한 값이 아니라 data의 값을 활용하고 싶다면, provide를 함수로 만들어서 this
를 사용해서 넣어주시면 됩니다.
export default {
data() {
return {
message: 'hello!'
}
},
provide() {
return {
message: this.message
}
}
}
이럴 경우 data의 message값이 바뀌어도 provide message의 값은 바뀌지 않습니다. 이미 값을 넣어줬기 때문이죠. data의 값이 바뀔 때마다 바뀌는 provide로 전달되는 값도 바뀌길 원한다면 computed
를 사용하면 됩니다.
import { computed } from 'vue'
export default {
data() {
return {
message: 'hello!'
}
},
provide() {
return {
// computed 함수 사용!
message: computed(() => this.message)
}
}
}
사실 Composition API에서 사용하는 함수이지만 Option API에서도 사용할 수 있긴 합니다.
만약 vue 3.3 버전 이하라면 app.config.unwrapInjectedRef = true
설정을 추가해야합니다.
전역에서 provide 선언
컴포넌트말고 앱을 만들었을 때 앱에서 provide를 추가하고 싶다면 provide
함수를 사용하면 됩니다. 이럴 경우 message
란 값은 모든 컴포넌트에서 사용 가능합니다.
import { createApp } from 'vue';
const app = createApp({});
app.provide('message', 'hello!');
Inject
상위 컴포넌트에서 provide가 만들어졌다면, 이 값을 사용하려는 컴포넌트에서 inject를 만들어야 합니다. props를 받을 때처럼 배열로 선언해주면 컴포넌트 데이터처럼 사용할 수 있습니다.
export default {
inject: ['message'],
created() {
console.log(this.message)
}
}
Inject의 값을 바꾼다면?
만약 값을 바꾸면 어떻게 될까요? 버튼을 만들고 클릭시에 bye
라는 값으로 바뀌게 한다고 해보겠습니다.
<template>
<button @click="message = 'bye'">{{ message }}</button>
</template>
<script>
export default {
inject: ['message'],
}
</script>
message 값은 바뀌지만 button의 글자는 bye라고 바뀌지 않습니다. vue는 inject의 변화를 추적해서 새로 DOM을 그리지 않기 때문이죠. inject에서 들어온 값이 바뀌는건 provide에 의해서여야만 합니다.
<template>
<Child @click="message='bye'" />
</template>
<script>
import { computed } from 'vue'
export default {
data() {
return {
message: 'hello!'
}
},
provide() {
return {
// computed 함수 사용!
message: computed(() => this.message)
}
}
}
</script>
<template>
<button>{{ message }}</button>
</template>
<script>
export default {
inject: ["message"],
};
</script>
Inject 이름 바꾸기
provide에서 전달해준 값이 현재 컴포넌트에서도 사용하는 명칭이라 다른 이름으로 바꾸고 싶다면 배열대신 객체로 선언하면 됩니다.
export default {
inject: {
// inject에서 받을 때엔 localMessage라 정의
localMessage: {
// provide에서는 message로 정의
from: 'message'
}
}
}
inject 기본값
하위 컴포넌트를 만든다고 할 때, provide에 원하는 값이 들어오지 않을 경우 기본값을 정해줄 필요가 있습니다. 이 경우 props처럼 default를 만들어주면 됩니다.
export default {
inject: {
message: {
from: 'message', // 명칭이 동일하다면 from은 안 써도 됩니다.
default: 'default value'
},
user: {
// 객체나 배열 같은 경우는 내부 참조에 의해 값이 바뀔 수 있으니 팩토리 함수로 정의해주어야합니다.
default: () => ({ name: 'John' })
}
}
}
협업을 한다면
provide를 여러 사람이 만든다면 겹치는 경우가 생기고 덮어쓰는 상황이 생깁니다. 그래서 Symbol
을 이용해 provide 키를 구분해서 사용하는걸 추천하고 있습니다.
export const myInjectionKey = Symbol()
import { myInjectionKey } from './keys.js'
export default {
provide() {
return {
[myInjectionKey]: {
// provide 데이터
}
}
}
}
import { myInjectionKey } from './keys.js'
export default {
inject: {
injected: { from: myInjectionKey }
}
}
마무리하며
결론부터 말하면 유용한 기능은 아니라 생각합니다. 상위 컴포넌트에서 값 전달용만 될 뿐더러, provide 값이 어디서 왔는지 알기 어려워질 수도 있습니다. 차라리 vuex 또는 pinia 같은 상태 관리자를 이용하는 게 더 낫지 않을까 생각합니다. 그럼에도 불구하고 굳이 써야하는 상황이라면 크지 않은 어플리케이션에서 store 쓰기에는 작고 props을 쓰기에는 귀찮은 상황일 때 적절하지 않을까 싶습니다.