Props / Emits 는 이전 글 참조
[JavaScript][Vue.js] Props와 Emits 정리 - 부모·자식 컴포넌트 통신
Vue는 화면을 여러 개의 컴포넌트로 나누어 구성한다.예를 들어 하나의 페이지가 다음과 같이 구성되어 있다고 해보자.ParentComponent ├─ HeaderComponent ├─ SearchBar ├─ WeatherCard └─ FooterComponent
daniellee09.tistory.com
Vue에서는 부모와 자식 컴포넌트 사이의 기본적인 데이터 전달 방식으로 Props와 Emits를 사용한다.
하지만 컴포넌트 계층이 깊어지면 단순한 Props 전달만으로는 구조가 복잡해질 수 있다.
예를 들어 다음과 같은 구조가 있다고 해보자.
GrandParent
↓
Parent
↓
Child
↓
GrandChild
GrandParent가 가진 데이터를 GrandChild에서 사용해야 한다면, Props만으로는 중간 컴포넌트들을 계속 거쳐야 한다.
GrandParent
↓ Props
Parent
↓ Props
Child
↓ Props
GrandChild
이때 Parent나 Child는 실제로 그 데이터를 사용하지 않더라도, 단순히 아래 컴포넌트로 전달하기 위해 Props를 받아야 한다.
이런 현상을 Props Drilling이라고 한다.
Props Drilling
Props Drilling은 데이터를 필요로 하지 않는 중간 컴포넌트까지 계속 Props를 전달해야 하는 상황을 의미한다.
예를 들어 최상위 컴포넌트에서 테마 정보를 가지고 있다고 해보자.
const theme = ref('dark')
실제로 이 데이터를 사용하는 것은 가장 아래의 MenuItem 컴포넌트뿐인데, 구조가 다음과 같다면
App
│
└─ Layout
│
└─ Sidebar
│
└─ Menu
│
└─ MenuItem
Props 방식에서는 다음처럼 전달해야 한다.
App
│ theme
↓
Layout
│ theme
↓
Sidebar
│ theme
↓
Menu
│ theme
↓
MenuItem
중간 컴포넌트들은 theme을 사용하지 않더라도 계속 전달해야 한다.
이런 구조가 커질수록 Props 관리가 번거로워질 수 있다.
provide / inject란?
provide / inject는 상위 컴포넌트의 데이터를 중간 컴포넌트를 거치지 않고 깊은 하위 컴포넌트에서 직접 사용할 수 있게 하는 기능이다.
역할은 다음과 같이 나뉜다.
provide
→ 상위 컴포넌트가 데이터 제공
inject
→ 하위 컴포넌트가 데이터 사용
구조를 보면 다음과 같다.
GrandParent
│
│ provide
│
├─────────────────┐
│ │
Parent │
│ │
Child │
│ │
GrandChild ◀─────────┘
inject
중간에 있는 Parent, Child는 데이터를 전달할 필요가 없다.
즉 provide / inject의 핵심은 컴포넌트 계층을 건너뛰어 데이터를 전달할 수 있다는 것이다.
provide()
상위 컴포넌트에서는 provide()를 이용해 데이터를 제공한다.
기본 형태는 다음과 같다.
provide('key', value)
예를 들어 테마 데이터를 전달하고 싶다면 다음과 같이 작성할 수 있다.
<script setup>
import { ref, provide } from 'vue'
const theme = ref('dark')
provide('theme', theme)
</script>
여기서
provide('theme', theme)
은
theme이라는 이름으로 이 데이터를 하위 컴포넌트들에게 제공한다.
는 의미다.
첫 번째 인자는 데이터를 찾기 위한 key이고, 두 번째 인자는 실제로 전달할 값이다.
provide(
'theme',
theme
)
│
├─ key
└─ value
inject()
하위 컴포넌트에서는 inject()를 이용해 상위 컴포넌트가 제공한 데이터를 가져온다.
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
</script>
<template>
<p>현재 테마: {{ theme }}</p>
</template>
inject()의 key는 provide()에서 사용한 key와 같아야 한다.
provide('theme', theme)
와
inject('theme')
이 서로 연결된다.
전체 구조는 다음과 같다.
상위 Component
provide('theme', theme)
↓
중간 Component
↓
하위 Component
inject('theme')
전체 예제
최상위 컴포넌트에서 사용자 이름을 제공하고, 가장 아래에 있는 컴포넌트에서 해당 값을 사용하는 구조를 만들어보자.
GrandParent.vue
<script setup>
import { ref, provide } from 'vue'
import ParentComponent from './ParentComponent.vue'
const userName = ref('홍길동')
provide('userName', userName)
</script>
<template>
<ParentComponent />
</template>
ParentComponent.vue
<script setup>
import ChildComponent from './ChildComponent.vue'
</script>
<template>
<ChildComponent />
</template>
ParentComponent에서는 userName과 관련된 코드를 작성하지 않는다.
ChildComponent.vue
<script setup>
import { inject } from 'vue'
const userName = inject('userName')
</script>
<template>
<p>사용자: {{ userName }}</p>
</template>
데이터 흐름은 다음과 같다.
GrandParent
userName = '홍길동'
provide('userName', userName)
│
│
▼
ParentComponent
│
│ 별도 전달 없음
▼
ChildComponent
inject('userName')
↓
홍길동
중간 컴포넌트가 데이터를 전달할 필요가 없다는 것이 가장 큰 특징이다.
반응형 데이터도 전달할 수 있다
provide()를 통해 ref나 reactive 같은 반응형 데이터를 전달할 수도 있다.
예를 들어 상위 컴포넌트에서 다음과 같이 선언했다고 해보자.
<script setup>
import { ref, provide } from 'vue'
const theme = ref('dark')
provide('theme', theme)
</script>
하위에서는 그대로 받을 수 있다.
<script setup>
import { inject } from 'vue'
const theme = inject('theme')
</script>
상위 컴포넌트에서 값을 변경하면
theme.value = 'light'
하위 컴포넌트에도 변경 사항이 반영된다.
상위
theme = dark
↓
provide
↓
inject
↓
하위
theme = dark
이후 상위에서
dark
↓
light
로 변경하면 하위에서도 같은 반응형 데이터를 참조하고 있기 때문에 화면이 함께 변경된다.
기본값 지정
inject()를 사용할 때 해당 key를 제공하는 상위 컴포넌트가 없을 수도 있다.
이때 기본값을 지정할 수 있다.
const theme = inject('theme', 'light')
동작은 다음과 같다.
상위에서 theme 제공
→ 제공된 값 사용
상위에서 theme 제공 안 함
→ 'light' 사용
컴포넌트를 독립적으로 사용할 가능성이 있다면 기본값을 지정하는 방식이 도움이 된다.
Props와 provide / inject 차이
Props와 provide / inject는 모두 컴포넌트 간 데이터 전달에 사용하지만 목적이 다르다.
| 구분 | Props | provide / inject |
| 기본 방향 | 부모 → 자식 | 조상 → 하위 |
| 중간 컴포넌트 | 계속 전달 필요 | 건너뛸 수 있음 |
| 데이터 흐름 확인 | 명확함 | 상대적으로 숨겨짐 |
| 사용 목적 | 일반적인 부모·자식 통신 | 깊은 계층의 공통 데이터 전달 |
Props는 다음처럼 바로 연결된 관계에 적합하다.
Parent
↓ Props
Child
반면 provide / inject는 다음처럼 계층이 깊은 경우에 유용하다.
GrandParent
│
├───────────────┐
↓ │
Parent │
↓ │
Child │
↓ │
GrandChild ◀───────┘
따라서 모든 Props를 provide / inject로 바꾸는 것이 아니라, 각각의 목적에 맞게 사용하는 것이 중요하다.
provide / inject와 Pinia의 차이
provide / inject와 Pinia는 모두 여러 컴포넌트에서 데이터를 사용할 수 있게 하지만 범위와 역할이 다르다.
provide / inject는 특정 컴포넌트 계층을 기준으로 동작한다.
App
│
└─ Layout
│
└─ Sidebar
│
└─ Menu
이 구조 안에서 공통으로 사용할 테마나 설정값을 전달하는 데 적합하다.
Pinia는 별도의 전역 Store를 만들어 컴포넌트 계층과 관계없이 데이터를 공유한다.
Component A
│
│
▼
Store
▲
│
Component B
│
Component C
단순하게 구분하면 다음과 같다.
| 구분 | provide / inject | Pinia |
| 범위 | 특정 컴포넌트 트리 | 애플리케이션 전체 |
| 목적 | 계층 간 데이터 전달 | 전역 상태 관리 |
| 구조 | 조상 → 하위 | Store 중심 |
| 예 | 테마, 언어, 폼 설정 | 로그인, 장바구니, 전역 사용자 상태 |
예를 들어
특정 화면 내부 테마
특정 Form의 설정
공통 UI 옵션
등은 provide / inject로 처리할 수 있다.
반면
로그인 상태
사용자 정보
장바구니
전역 알림
같은 애플리케이션 전체 상태는 Pinia와 같은 상태 관리 도구가 적합하다.
provide / inject 전체 흐름
전체 구조를 하나로 보면 다음과 같다.
GrandParent
const theme = ref('dark')
provide('theme', theme)
│
│
┌─────────┴─────────┐
│ │
▼ │
Parent │
│ │
▼ │
Child │
│ │
▼ │
GrandChild ◀────────────┘
inject('theme')
중간 컴포넌트는 해당 데이터의 존재를 몰라도 된다.
이것이 Props Drilling을 줄일 수 있는 이유다.
정리
Vue의 일반적인 부모·자식 데이터 전달은 Props를 사용한다.
Parent
↓ Props
Child
하지만 컴포넌트 계층이 깊어지면 Props를 여러 단계에 걸쳐 전달해야 하는 Props Drilling 문제가 생길 수 있다.
GrandParent
↓
Parent
↓
Child
↓
GrandChild
provide / inject를 사용하면 중간 단계를 건너뛸 수 있다.
상위 Component
│
│ provide
▼
·
·
·
▼
하위 Component
│
└─ inject
각 역할은 다음과 같다.
provide()
→ 상위 컴포넌트에서 데이터 제공
inject()
→ 하위 컴포넌트에서 데이터 사용
따라서 한 문장으로 정리하면 다음과 같다.
provide / inject는 상위 컴포넌트가 데이터를 제공하고, 깊은 하위 컴포넌트가 중간 컴포넌트를 거치지 않고 직접 받아 사용할 수 있게 하는 Vue의 의존성 주입 방식이다.
Props는 일반적인 부모·자식 통신에 사용하고, provide / inject는 깊은 컴포넌트 계층에서 공통 데이터를 전달해야 할 때 사용하는 방식으로 구분하면 된다.
'개발 > JavaScript' 카테고리의 다른 글
| [JavaScript][Vue.js] Pinia와 Store 정리 - 전역 상태 관리 이해하기 (0) | 2026.08.26 |
|---|---|
| [JavaScript][Vue.js] Slot 정리 - 부모가 자식 컴포넌트에 UI를 주입하는 방법 (0) | 2026.08.26 |
| [JavaScript][Vue.js] Props와 Emits 정리 - 부모·자식 컴포넌트 통신 (0) | 2026.08.26 |
| [JavaScript][Vue.js] Component Lifecycle과 Lifecycle Hook 정리 (0) | 2026.08.26 |
| [JavaScript][Vue.js] Composition API란? - Options API와의 차이부터 ref, computed, watch까지 (0) | 2026.08.26 |