2025. 3. 30. 21:30ㆍFront-End/Vue.js
Prop Drilling 문제
Vue에서 일반적으로 부모 → 자식으로 데이터를 전달할 때 props를 사용한다.
하지만 컴포넌트 구조가 깊어지면 아래처럼 중간 단계의 컴포넌트들이 단순히 전달만을 위한 용도로 props를 받아야 하는 상황이 생긴다.
<App>
<Layout>
<Footer>
<DeepChild />
</Footer>
</Layout>
</App>
App → DeepChild까지 데이터를 전달하려면 Layout, Footer까지 모두 props를 받아야 한다.
이런 문제를 Prop Drilling이라고 부른다.
Provide / Inject란?
Vue 3에서는 provide()와 inject()를 사용하면 컴포넌트 트리 구조와 상관없이 상위 → 하위로 직접 데이터 전달이 가능하다.
- 상위: provide()를 통해 데이터를 “제공”
- 하위: inject()로 데이터를 “주입받음”
- 중간 컴포넌트는 관여하지 않아도 된다
기본 사용법
상위 컴포넌트에서 제공
// ProviderComponent.vue
import { provide, ref } from 'vue';
export default {
setup() {
const message = ref('Hello from parent');
provide('sharedMessage', message);
return { message };
}
}
하위 컴포넌트에서 주입
// DeepChild.vue
import { inject } from 'vue';
export default {
setup() {
const message = inject('sharedMessage');
return { message };
}
}
이제 DeepChild는 ProviderComponent에서 제공한 message에 접근할 수 있다.
message가 ref이기 때문에 반응성도 그대로 유지된다.
기본값 설정
주입하려는 키가 실제로 제공되지 않을 경우, 기본값을 두 번째 인자로 넣을 수 있다.
const fallback = inject('nonexistentKey', '기본 메시지');
기본값을 함수로 전달할 수도 있다.
const fallback = inject('nonexistentKey', () => '함수형 기본 메시지');
함수 전달하기
반응형 데이터뿐 아니라 함수도 전달할 수 있다.
// 상위 컴포넌트
const count = ref(0);
const increment = () => count.value += 1;
provide('counter', {
count,
increment
});
// 하위 컴포넌트
const { count, increment } = inject('counter');
하위 컴포넌트에서도 increment()를 호출해 값을 변경할 수 있다.
readonly로 전달 막기
하위 컴포넌트에서 값을 직접 바꾸는 걸 막고 싶다면 readonly()를 사용한다.
import { provide, readonly, ref } from 'vue';
const count = ref(0);
provide('readonlyCount', readonly(count));
Symbol을 키로 사용하기
문자열 키는 충돌 위험이 있다. 대규모 앱에서는 Symbol을 키로 사용하는 것이 좋다.
// keys.js
export const MessageKey = Symbol('messageKey');
// 상위 컴포넌트
import { provide, ref } from 'vue';
import { MessageKey } from './keys';
const message = ref('메시지입니다');
provide(MessageKey, message);
// 하위 컴포넌트
import { inject } from 'vue';
import { MessageKey } from './keys';
const message = inject(MessageKey);
App 레벨에서 provide
provide()는 루트 앱 인스턴스에서도 설정할 수 있다. 이 경우 전체 앱에서 사용할 수 있다.
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.provide('appName', 'My Vue App');
app.mount('#app');
이렇게 설정한 값은 모든 컴포넌트에서 inject('appName')으로 사용할 수 있다.
예: 글로벌 Config 전달
Vue 2에서는 $root나 this.$app 같은 방식으로 글로벌 데이터를 공유했지만, Vue 3의 setup()에서는 컴포넌트 인스턴스에 접근할 수 없기 때문에 provide/inject 방식이 권장된다.
// main.js
app.provide('config', {
apiBase: 'https://api.example.com',
version: '1.0.0',
});
// 내부 컴포넌트
const config = inject('config');
console.log(config.apiBase); // https://api.example.com
정리
기능 설명
provide() | 상위 컴포넌트에서 데이터를 등록 |
inject() | 하위 컴포넌트에서 해당 데이터를 사용 |
반응형 지원 | ref, reactive 모두 가능 (연결 유지) |
기본값 설정 | inject 두 번째 인자로 fallback 가능 |
함수 전달 | 데이터 외에 함수도 함께 제공 가능 |
읽기 전용 | readonly()로 하위에서 직접 수정 방지 |
Symbol 키 | 키 충돌 방지를 위한 권장 방식 |
App-level 제공 | 앱 전체에 공통 데이터를 제공할 때 사용 |
Vue의 provide/inject는 단순히 props 전달을 생략하기 위한 편법이 아니라, 역할이 분리된 데이터 공유 방식이이다.
특히 전역 설정, 공통 유틸, 다국어 리소스, 테마 구성 등에서 유용하게 사용할 수 있다.
'Front-End > Vue.js' 카테고리의 다른 글
Vue 3 – Template Ref로 DOM에 직접 접근하기 (0) | 2025.03.30 |
---|---|
Vue 3 – 컴포넌트 라이프사이클과 훅 정리 (0) | 2025.03.30 |
Vue 3 – Slot을 활용한 컴포넌트 콘텐츠 전달 (0) | 2025.03.30 |
Vue 3 - Non-Prop 속성 제대로 이해하기 (0) | 2025.03.30 |
Vue 3 – 이벤트(emit)로 컴포넌트 간 데이터 전달하기 (0) | 2025.03.30 |