일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 웹
- 이더리움
- 블록체인
- 컴퓨터공학
- react
- 솔리디티
- kubernetes
- VUE
- 파이썬
- TypeScript
- 클라우드
- CSS
- 알고리즘
- 가상화
- next.js
- BFS
- 백준
- 이슈
- 타입스크립트
- 리액트
- k8s
- JavaScript
- 프론트엔드
- docker
- AWS
- HTML
- 백엔드
- 쿠버네티스
- es6
- 자바스크립트
- Today
- Total

즐겁게, 코드
[Vue] inject-provide 패턴 조금 더 잘 써보기 본문
React에서는 Context.Provider
↔ useContext
로 구성된 Context API를 사용해 하위 컴포넌트로 값을 공유할 수 있듯, Vue에서도 provide ↔ inject 라는 함수(편의상 '패턴' 이라 부르겠습니다)를 조합해 값을 공유할 수 있습니다.
이번 글에서는 Provide - inject 패턴의 사용법을 간단히 알아보면서, 두 가지 팁을 함께 소개해보려 합니다.
TL;DR
- provide - inject 함수의 키로 심볼을 활용하면 키의 중복을 차단할 수 있다.
InjectionKey
타입을 활용하면 키에 따라 inject될 값의 타입을 미리 추론할 수 있다.
Provide - Inject 패턴의 예시
앱에 다크 모드 / 라이트 모드 / 시스템 테마에 알맞는 UI 색상을 적용하기 위해 테마 값을 전역으로 관리하는 모습입니다.
// Provider.vue
<script setup lang="ts">
import { provide, ref } from "vue";
import Consumer from "./Consumer.vue";
// 다크 테마, 라이트 테마, 시스템 설정 테마
const enum ThemePreference {
DARK,
LIGHT,
SYSTEM,
}
export type Theme = { theme: ThemePreference };
const themeRef = ref<Theme>({ theme: "system" });
provide("theme", themeRef);
</script>
<template>
<Consumer />
</template>
// Consumer.vue
<script setup lang="ts">
import { inject } from "vue";
import { Theme } from "./Provider.vue";
// inject 함수에 제네릭 타입을 부여해, 주입할 값의 타입을 추론할 수 있다.
const theme = inject<Theme>("theme");
</script>
<template>
<div>현재 선택된 테마 : {{ theme?.theme }}</div>
</template>
<style scoped></style>
이 코드는 완벽하게 동작하지만, 조금 더 나은 DX를 위해 두 부분을 개선할 수 있습니다.
1. Symbol(심볼) 키 사용하기
하나는 공식 문서에서도 제안하는 심볼(Symbol) 키를 사용하는 방법입니다.
각각의 심볼은 고유함이 보장되므로 중복될 수 있는 문자열과는 달리 언제나 고유함이 보장됩니다.
💡 장점이 잘 와닿지 않는다면 CSS의 클래스명을 사용할 때와 Module CSS, Scoped CSS를 사용했을 때의 차이를 떠올려 보세요.
// constants/keys.ts
export const INJECT_THEME_KEY = Symbol("theme");
// Provider.vue
<script setup lang="ts">
import { provide, ref } from "vue";
import Consumer from "./Consumer.vue";
import { INJECT_THEME_KEY } from "./constants/keys";
const enum ThemePreference {
DARK,
LIGHT,
SYSTEM,
}
export type Theme = { theme: ThemePreference };
const themeRef = ref<Theme>({ theme: "system" });
provide(INJECT_THEME_KEY, themeRef);
</script>
<template>
<Consumer />
</template>
// Consumer.vue
<script setup lang="ts">
import { inject } from "vue";
import { Theme } from "./Provider.vue";
import { INJECT_THEME_KEY } from "./constants/keys";
// Symbol 키는 항상 고유함이 보장되어, 키가 중복되거나 네이밍을 고려해야 하는 수고를 덜 수 있다.
const theme = inject<Theme>(INJECT_THEME_KEY);
</script>
<template>
<div>현재 선택된 테마 : {{ theme?.theme }}</div>
</template>
<style scoped></style>
2. InjectionKey 타입으로 값 타이핑하기
다음은 주입된 값의 타입을 선언하는 방법입니다.
// Consumer.vue
<script setup lang="ts">
import { inject } from "vue";
import { Theme } from "./Provider.vue";
import { INJECT_THEME_KEY } from "./constants/keys";
// 값을 inject해 사용할 때마다 <Theme> 제네릭을 명시적으로 선언해야 할까?
const theme = inject<Theme>(INJECT_THEME_KEY);
</script>
<template>
<div>현재 선택된 테마 : {{ theme?.theme }}</div>
</template>
<style scoped></style>
제네릭으로 타입을 선언하면 강력한 타입 추론의 이점을 누릴 수 있지만, 값을 주입할 때마다 심볼 키에 대응하는 타입을 명시적으로 선언하는 작업은 유지보수에 불리합니다.
이를 개선하기 위해 InjectionKey
타입을 활용할 수 있습니다.
// constants/keys.ts
import { InjectionKey, Ref } from "vue";
// "inject(INJECT_THEME_KEY) 로 주입한 값은 Ref<Theme> 타입이야" 라고 선언했습니다.
export const INJECT_THEME_KEY: InjectionKey<Ref<Theme>> = Symbol("theme")
마치며
강력한 Devtool 지원을 받고 싶거나 비동기 로직 또는 setup API
바깥에서 상태를 관리하는 등 복잡한 경우를 가정한다면 상태 관리 라이브러리인 pinia가 provide - inject 패턴보다 효과적인 선택이 될 수 있습니다.
하지만 *값이 생산되는 지점이 명확하면서 변하지 않음이 보장되면서 provide - inject 패턴을 사용하는 것이 보다 쉽고 강력한 방법이 될 수 있을 것이라 생각합니다. (Ex. 전역으로 사용하는 UI 컴포넌트의 참조를 등록하는 등)
type Key = "user" | "profile" | "cart";
provide("user") // provide의 키를 Key 값 중 하나로 좁힐 수는 없을까? (현재는 불가능)
inject("user") // inject의 키를 Key 값 중 하나로 좁힐 수는 없을까?
미래에는 이런 타입도 지원할 수 있길 바라며, 감기 조심하시길 바라겠습니다! 🥶
'🎨 프론트엔드 > Vue.js' 카테고리의 다른 글
vee-validate에 타입스크립트 적용하기 (@vee-validate/yup) (0) | 2024.12.19 |
---|---|
Vue 3에서 페이지 전환 애니메이션 추가하기 (0) | 2023.12.24 |
Vuex의 namespaced module 활용하기 (0) | 2022.04.04 |
v-show와 v-if 지시자의 차이 (0) | 2021.08.18 |
Vue 컴포넌트에서 name 속성을 명시하는 이유 (0) | 2021.05.24 |