Vue.js 를 배워보자
Vue 3에서 자식 컴포넌트 간 팝업 호출 구현하기
_Blue_Sky_
2025. 5. 19. 20:52
728x90

Vue 3에서 Composition API와 TypeScript를 사용하여 부모 컴포넌트 아래 자식 컴포넌트 간의 통신을 구현하는 방법을 알아보겠습니다. 이 글에서는 부모 컴포넌트에 자식 1과 자식 2가 있고, 자식 1에서 자식 2(팝업)를 호출하는 예제를 다룹니다. 코드와 함께 단계별로 설명하겠습니다.
1. 프로젝트 설정
먼저, Vue 3 프로젝트를 TypeScript로 설정합니다. Vite를 사용하여 프로젝트를 생성하는 명령어는 다음과 같습니다:
npm create vite@latest my-vue-app -- --template vue-ts
cd my-vue-app
npm install
npm run dev
프로젝트가 준비되었다면, 이제 컴포넌트를 구성해 보겠습니다.
2. 컴포넌트 구조
아래와 같은 컴포넌트 구조를 가정합니다:
-
Parent.vue: 부모 컴포넌트로, 자식 1과 자식 2를 포함.
-
Child1.vue: 자식 1 컴포넌트로, 버튼 클릭 시 자식 2(팝업)를 호출.
-
Child2.vue: 자식 2 컴포넌트로, 팝업 형태로 표시.
3. 부모 컴포넌트 (Parent.vue)
부모 컴포넌트는 자식 1과 자식 2를 관리하며, 팝업의 표시 여부를 제어하는 상태를 가집니다. 자식 1에서 팝업 호출 요청을 받으면 자식 2를 표시합니다.
<script setup lang="ts">
import { ref } from 'vue';
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';
// 팝업 표시 여부
const isPopupVisible = ref(false);
// 자식 1에서 팝업 호출 시 실행
const openPopup = () => {
isPopupVisible.value = true;
};
// 자식 2에서 팝업 닫기 시 실행
const closePopup = () => {
isPopupVisible.value = false;
};
</script>
<template>
<div>
<h1>부모 컴포넌트</h1>
<!-- 자식 1: 팝업 호출 버튼 포함 -->
<Child1 @open-popup="openPopup" />
<!-- 자식 2: 팝업 컴포넌트, 표시 여부에 따라 렌더링 -->
<Child2 v-if="isPopupVisible" @close-popup="closePopup" />
</div>
</template>
<style scoped>
/* 스타일은 필요에 따라 추가 */
</style>
설명:
-
isPopupVisible은 팝업의 표시 여부를 관리하는 반응형 상태입니다.
-
openPopup은 자식 1에서 호출되며, isPopupVisible을 true로 설정합니다.
-
closePopup은 자식 2에서 호출되며, isPopupVisible을 false로 설정합니다.
-
v-if를 사용하여 자식 2를 조건부 렌더링합니다.
4. 자식 1 컴포넌트 (Child1.vue)
자식 1 컴포넌트는 버튼을 클릭하면 부모 컴포넌트에 팝업 호출을 요청합니다. 이를 위해 emit을 사용합니다.
<script setup lang="ts">
import { defineEmits } from 'vue';
// emit 정의
const emit = defineEmits<{
(e: 'open-popup'): void;
}>();
// 버튼 클릭 시 부모에게 팝업 열기 요청
const handleClick = () => {
emit('open-popup');
};
</script>
<template>
<div>
<h2>자식 1 컴포넌트</h2>
<button @click="handleClick">팝업 열기</button>
</div>
</template>
<style scoped>
button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>
설명:
-
defineEmits를 사용하여 TypeScript로 open-popup 이벤트를 타입 안전하게 정의합니다.
-
버튼 클릭 시 handleClick 함수가 호출되고, emit('open-popup')을 통해 부모 컴포넌트에 이벤트를 전송합니다.
5. 자식 2 컴포넌트 (Child2.vue)
자식 2 컴포넌트는 팝업 형태로 표시되며, 닫기 버튼을 통해 부모 컴포넌트에 팝업 닫기를 요청합니다.
<script setup lang="ts">
import { defineEmits } from 'vue';
// emit 정의
const emit = defineEmits<{
(e: 'close-popup'): void;
}>();
// 닫기 버튼 클릭 시 부모에게 팝업 닫기 요청
const handleClose = () => {
emit('close-popup');
};
</script>
<template>
<div class="popup-overlay">
<div class="popup-content">
<h2>자식 2 컴포넌트 (팝업)</h2>
<p>이것은 팝업입니다!</p>
<button @click="handleClose">닫기</button>
</div>
</div>
</template>
<style scoped>
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.popup-content {
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
button {
margin-top: 10px;
padding: 8px 16px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #c82333;
}
</style>
설명:
-
defineEmits로 close-popup 이벤트를 정의합니다.
-
닫기 버튼 클릭 시 handleClose가 호출되어 emit('close-popup')을 통해 부모 컴포넌트에 이벤트를 전송합니다.
-
CSS를 사용하여 팝업의 오버레이와 콘텐츠 스타일을 지정합니다.
6. 전체 흐름 요약
-
자식 1 (Child1): 사용자가 "팝업 열기" 버튼을 클릭하면 open-popup 이벤트를 부모 컴포넌트로 전송.
-
부모 (Parent): open-popup 이벤트를 받아 isPopupVisible을 true로 설정, 자식 2가 렌더링됨.
-
자식 2 (Child2): 팝업이 표시되고, 사용자가 "닫기" 버튼을 클릭하면 close-popup 이벤트를 부모로 전송.
-
부모 (Parent): close-popup 이벤트를 받아 isPopupVisible을 false로 설정, 자식 2가 사라짐.
7. 추가 팁
-
재사용성: 팝업 컴포넌트를 재사용 가능하도록 props로 제목이나 내용을 동적으로 받게 수정할 수 있습니다.
-
애니메이션: CSS 또는 Vue의 transition을 사용하여 팝업의 등장/사라짐에 애니메이션을 추가할 수 있습니다.
-
TypeScript 강화: props나 emit에 더 복잡한 타입을 정의하여 타입 안정성을 높일 수 있습니다.
8. 결론
이 예제에서는 Vue 3의 Composition API와 TypeScript를 활용하여 부모-자식 컴포넌트 간 통신을 통해 팝업을 호출하는 방법을 살펴보았습니다. emit과 반응형 상태를 사용하면 컴포넌트 간 데이터 흐름을 명확히 관리할 수 있습니다. 이 패턴은 다양한 인터랙티브 UI를 구축할 때 유용하게 활용될 수 있습니다.
728x90