cover

이미 이전 글을 통해 Provide와 inject에 대해 다루었지만, Composition API에서는 어떻게 사용하는지는 보지 않았습니다. 그래서 다시금 내용을 복습하고 Composition API에서의 provide와 inject에 대해서 알아보겠습니다.

provide / inject 필요성

컴포넌트 간의 통신은 props와 emits을 통해 이루어집니다. 그런데 만약 이러한 계층 구조가 깊어지면 깊어질수록 상위의 컴포넌트가 깊은 하위 컴포넌트에게 원하는 데이터를 전달하기 위해서 여러 번의 props를 사용해야합니다. 반대로 이벤트를 emit할 때도 마찬가지입니다.

그래서 이를 한 번에 전달하기 쉽도록 vue에서는 provide와 inject 기능을 제공합니다.

image

provide는 상위 컴포넌트에서 전달하고자 하는 값을, inject에서는 상위 컴포넌트에서 받고자 하는 값을 정의하면 됩니다.

provide

provide 함수를 import 한 다음 첫 번째 매개변수에 키 값을, 두 번째 매개변수에 값을 넣어주면 됩니다.

html
      <script>
import { provide } from 'vue'

export default {
  setup() {
    provide(/* 키 */ 'message', /* 값 */ '안녕!')
  }
}
</script>
    

setup script에서도 크게 다르지 않습니다.

html
      <script setup>
import { provide } from 'vue';

provide(키, 값);
</script>
    

여기서 키 값은 문자열 또는 Symbol을 넣어서 사용할 수 있으며, 값은 어떤 타입이여도 상관없습니다.

앱에서 provide

상위 컴포넌트가 아니라 앱 수준의 최상단에서 provide를 원하는 경우 app의 provide 함수를 사용하면 됩니다.

javascript
      import { createApp } from 'vue';

const app = createApp({});

app.provide(키, 값);
    

Inject

inject 함수를 import 한 다음 provide에서 받고자 하는 키 값을 동일하게 사용하면 됩니다. 두 번째 매개변수는 기본값으로 만약 해당 키 값이 provide를 통해 전달되고 있지 않다면 이 값을 반환합니다. 기본값은 선택사항으로 키 값만 넣어도 됩니다.

html
      <script>
import { inject } from 'vue';

export default {
  setup() {
    const value = inject(키값, 기본값);
    return { value };
  }
}
</script>
    

script setup에서도 동일합니다.

javascript
      <script setup>
import { inject } from 'vue';

const message = inject(키 값, 기본값);
</script>
    

inject에서 값을 변화시키고 싶다면

provide로 제공받은 값을 inject하는 컴포넌트에서 변화시키고 싶을 수 있습니다. 하지만 값을 변경시키는 작업은 emits처럼 상위 컴포넌트에서 해주는 것이 유지보수에 좋습니다.

따라서 값을 변경시키는 작업은 provide를 제공하는 컴포넌트에서 함수로 만들어주고, 이 함수를 inject하는 컴포넌트에 전달하면 됩니다.

html
상위 컴포넌트
      <script setup>
import MyComponent from "./components/MyComponent.vue";
import { provide, ref } from "vue";

const name = ref("vuelogger");

// 업데이트 해주는 함수
function update(newName) {
  name.value = newName;
}

provide("name", {
  name,
  update,
});
</script>

<template>
  <MyComponent />
</template>
    
html
하위 컴포넌트
      <script setup>
import { inject } from "vue";

const { name, update } = inject("name");
</script>

<template>
  <button @click="update('famous man')">{{ name }}</button>
</template>
    

사실 위 함수를 사용할 필요없이 하위 컴포넌트에서 직접 바꿀 수도 있습니다.

html
      <button @click="name = 'Iron man'">{{ name }}</button>
    

하지만 위에서도 언급했듯이 provide로 제공한 값이 어느 컴포넌트에서 변화시켰는지 찾기 어려워질 수 있기 때문에, 업데이트 해주는 함수를 따로 제공받아서 사용해야합니다. 만일 이렇게 전달받은 값을 변화시키지 못하도록 막고 싶다면 readonly를 사용하면 됩니다.

html
      <script setup>
import MyComponent from "./components/MyComponent.vue";
import { provide, ref, readonly } from "vue";

const name = ref("vuelogger");
const readonlyName = readonly(name);

// 업데이트해주는 함수
function update(newName) {
  name.value = newName;
}

provide("name", {
  name: readonlyName,
  update,
});
</script>

<template>
  <MyComponent />
</template>
    

만일 하위 컴포넌트에서 직접 바꾸려하면 다음과 같은 경고가 나타나면서 바뀌지 않습니다.

image

중복 방지를 위한 Symbol

provide를 한 사람이 아닌 여러 사람이 만든다면 동일한 이름의 provide 키를 쓸 가능성이 생깁니다. 그래서 이러한 중복을 막기위해 Symbol을 사용해야합니다.

javascript
keys.js
      export const myInjectionKey = Symbol()
    
javascript
상위 컴포넌트
      import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* 제공할 데이터 */
})
    
javascript
하위 컴포넌트
      import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)