중간 컴포넌트 네 개가 쓰지도 않는 prop을 넘기고만 있던 문제
문제 발생
테마 설정을 최상위에서 5단계 아래 버튼까지 내리는데, 중간 컴포넌트 네 개가 자기는 쓰지도 않는 prop을 받아서 그대로 넘기고만 있었습니다.
<Layout :theme="theme">
<Sidebar :theme="theme">
<Menu :theme="theme">
<MenuItem :theme="theme">
<ThemeButton :theme="theme" />테마에 값 하나를 추가할 때마다 다섯 파일을 고쳐야 했습니다.
원인 분석
공식 문서가 prop drilling이라고 부르는 상황입니다. 문서의 설명대로, 깊이 중첩된 자식이 멀리 있는 조상의 값을 필요로 할 때 props만으로는 부모 체인 전체에 같은 prop을 넘겨야 합니다.
provide/inject가 이걸 위한 도구입니다. 부모 컴포넌트가 의존성 공급자(dependency provider) 가 되고, 자손 트리의 어떤 컴포넌트든 깊이와 무관하게 그 값을 주입받습니다.
다만 규칙이 몇 개 있고, 이걸 모르면 조용히 동작하지 않습니다. provide()는 Composition API의 setup 단계에서 동기적으로 호출해야 합니다. await 뒤나 콜백 안에서 부르면 등록되지 않습니다.
해결 방안
- 공급자와 소비자를 직접 연결합니다.
<!-- Layout.vue -->
<script setup>
import { provide, ref } from "vue";
const theme = ref("light");
function toggleTheme() {
theme.value = theme.value === "light" ? "dark" : "light";
}
provide("theme", { theme, toggleTheme });
</script><!-- ThemeButton.vue -->
<script setup>
import { inject } from "vue";
const { theme, toggleTheme } = inject("theme");
</script>- 반응성을 유지하려면 ref나 computed를 넘깁니다. 값을 꺼내서 넘기면 그 시점의 값일 뿐입니다.
provide("theme", theme.value); // ❌ 이후 변경이 전달되지 않음
provide("theme", theme); // ✅-
변경은 공급자 쪽에 둡니다. 문서가 굵게 권합니다 — 반응형 상태의 변경은 가능한 한 공급자 안에 두는 것이 좋습니다. 상태와 그 변경 방법이 한 컴포넌트에 모여 있어야 나중에 유지보수가 됩니다. 그래서 위 예제도 값과 함께
toggleTheme함수를 같이 내려보냅니다. -
소비자가 못 바꾸게 하려면
readonly()로 감쌉니다.
provide("read-only-count", readonly(count));- 기본값을 줍니다. 공급자 없이 단독으로 쓰이는 컴포넌트가 있다면
inject의 두 번째 인자가 필요합니다. 비싼 객체라면 팩토리 형태를 씁니다 — 세 번째 인자true가 "기본값을 팩토리 함수로 취급하라"는 뜻입니다.
const message = inject("message", "기본값");
const value = inject("key", () => new ExpensiveClass(), true);- 큰 앱에서는 Symbol 키를 씁니다. 문자열 키는 서로 다른 라이브러리·모듈끼리 부딪칠 수 있습니다. 키를 별도 파일에 모아 export합니다.
// keys.js
export const themeKey = Symbol();- 앱 수준으로도 공급할 수 있습니다. 플러그인처럼 컴포넌트가 아닌 곳에서 값을 내려야 할 때 쓰입니다.
const app = createApp({});
app.provide("message", "hello!");- 전역 상태 관리를 대체하지는 않습니다.
provide/inject는 트리 아래로만 흐릅니다. 형제나 관련 없는 화면끼리 공유해야 하면 그건 다른 문제입니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.