Vue.js 를 배워보자
Vue 3에서 팝업에서 데이터 가져와 자식 1-1에 표시하기
_Blue_Sky_
2025. 5. 20. 21:06
728x90

이 글에서는 Vue 3의 Composition API와 TypeScript를 사용하여 자식 2(팝업)에서 입력된 name과 type 데이터를 가져와 자식 1-1에 표시하는 방법을 설명합니다. 이전 예제를 수정하여 자식 2에서 데이터를 입력하고, 이를 부모를 통해 자식 1-1로 전달하는 흐름을 다룹니다.
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 컴포넌트로, 자식 1-1을 포함.
-
Child1_1.vue: 자식 1-1 컴포넌트로, 팝업 호출 버튼을 제공하고 팝업에서 받은 데이터를 표시.
-
Child2.vue: 자식 2 컴포넌트로, 팝업 형태로 name과 type 입력 폼을 제공하고 데이터를 부모로 전송.
3. 부모 컴포넌트 (Parent.vue)
부모 컴포넌트는 팝업의 표시 여부와 자식 2에서 받은 데이터를 관리하며, 이를 자식 1-1에 전달합니다.
<script setup lang="ts">
import { ref } from 'vue';
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';
// 팝업 표시 여부
const isPopupVisible = ref(false);
// 자식 1-1에 표시할 데이터
const displayData = ref<{ name: string; type: string } | null>(null);
// 자식 1-1에서 팝업 호출
const openPopup = () => {
isPopupVisible.value = true;
};
// 자식 2에서 데이터 수신 및 팝업 닫기
const handlePopupSubmit = (data: { name: string; type: string }) => {
displayData.value = data;
isPopupVisible.value = false;
};
// 자식 2에서 팝업 닫기 (데이터 없이)
const closePopup = () => {
isPopupVisible.value = false;
};
</script>
<template>
<div>
<h1>부모 컴포넌트</h1>
<!-- 자식 1: 자식 1-1 포함, 데이터 전달 -->
<Child1 :display-data="displayData" @open-popup="openPopup" />
<!-- 자식 2: 팝업 컴포넌트 -->
<Child2
v-if="isPopupVisible"
@submit-popup="handlePopupSubmit"
@close-popup="closePopup"
/>
</div>
</template>
<style scoped>
/* 스타일은 필요에 따라 추가 */
</style>
설명:
-
isPopupVisible은 팝업의 표시 여부를 관리.
-
displayData는 자식 2에서 받은 name과 type 데이터를 저장하며, 자식 1-1에 props로 전달.
-
openPopup은 자식 1-1의 요청으로 팝업을 염.
-
handlePopupSubmit은 자식 2에서 받은 데이터를 displayData에 저장하고 팝업을 닫음.
-
closePopup은 데이터를 저장하지 않고 팝업을 닫음.
4. 자식 1 컴포넌트 (Child1.vue)
자식 1 컴포넌트는 자식 1-1을 포함하고, 부모로부터 받은 데이터를 자식 1-1로 전달하며 이벤트를 중계합니다.
<script setup lang="ts">
import { defineEmits, defineProps } from 'vue';
import Child1_1 from './Child1_1.vue';
// props 정의
const props = defineProps<{
displayData: { name: string; type: string } | null;
}>();
// emit 정의
const emit = defineEmits<{
(e: 'open-popup'): void;
}>();
// 자식 1-1에서 받은 이벤트를 부모로 전달
const handleOpenPopup = () => {
emit('open-popup');
};
</script>
<template>
<div>
<h2>자식 1 컴포넌트</h2>
<!-- 자식 1-1: 데이터 표시 및 팝업 호출 -->
<Child1_1 :display-data="displayData" @open-popup="handleOpenPopup" />
</div>
</template>
<style scoped>
/* 스타일은 필요에 따라 추가 */
</style>
설명:
-
displayData props를 받아 자식 1-1에 전달.
-
open-popup 이벤트를 자식 1-1에서 받아 부모로 전달.
5. 자식 1-1 컴포넌트 (Child1_1.vue)
자식 1-1 컴포넌트는 팝업 호출 버튼을 제공하고, 부모로부터 받은 name과 type 데이터를 표시합니다.
<script setup lang="ts">
import { defineProps, defineEmits } from 'vue';
// props 정의
const props = defineProps<{
displayData: { name: string; type: string } | null;
}>();
// emit 정의
const emit = defineEmits<{
(e: 'open-popup'): void;
}>();
// 팝업 호출
const handleClick = () => {
emit('open-popup');
};
</script>
<template>
<div>
<h3>자식 1-1 컴포넌트</h3>
<button @click="handleClick">팝업 열기</button>
<div v-if="displayData">
<p><strong>Name:</strong> {{ displayData.name }}</p>
<p><strong>Type:</strong> {{ displayData.type }}</p>
</div>
<p v-else>데이터가 없습니다.</p>
</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;
}
div {
margin-top: 10px;
}
</style>
설명:
-
displayData props를 받아 name과 type을 표시.
-
데이터가 없을 경우 기본 메시지를 표시.
-
버튼 클릭 시 open-popup 이벤트를 발생.
6. 자식 2 컴포넌트 (Child2.vue)
자식 2 컴포넌트는 팝업으로, name과 type 입력 폼을 제공하고 제출 시 데이터를 부모로 전송합니다.
<script setup lang="ts">
import { ref, defineEmits } from 'vue';
// 입력 데이터
const name = ref('');
const type = ref('');
// emit 정의
const emit = defineEmits<{
(e: 'submit-popup', data: { name: string; type: string }): void;
(e: 'close-popup'): void;
}>();
// 데이터 제출
const handleSubmit = () => {
if (name.value && type.value) {
emit('submit-popup', { name: name.value, type: type.value });
name.value = '';
type.value = '';
} else {
alert('모든 필드를 입력하세요.');
}
};
// 팝업 닫기
const handleClose = () => {
emit('close-popup');
};
</script>
<template>
<div class="popup-overlay">
<div class="popup-content">
<h2>자식 2 컴포넌트 (팝업)</h2>
<form @submit.prevent="handleSubmit">
<div>
<label>Name:</label>
<input v-model="name" type="text" placeholder="Enter name" />
</div>
<div>
<label>Type:</label>
<input v-model="type" type="text" placeholder="Enter type" />
</div>
<button type="submit">제출</button>
<button type="button" @click="handleClose">닫기</button>
</form>
</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;
}
form {
display: flex;
flex-direction: column;
gap: 10px;
}
input {
padding: 8px;
margin-left: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 8px 16px;
margin: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
}
button[type="submit"] {
background-color: #28a745;
color: white;
}
button[type="submit"]:hover {
background-color: #218838;
}
button[type="button"] {
background-color: #dc3545;
color: white;
}
button[type="button"]:hover {
background-color: #c82333;
}
</style>
설명:
-
name과 type 입력값을 ref로 관리.
-
제출 버튼 클릭 시 입력값을 검증하고, submit-popup 이벤트로 데이터를 부모로 전송.
-
닫기 버튼 클릭 시 close-popup 이벤트를 발생.
-
입력 폼은 간단한 스타일로 사용자 친화적으로 구성.
7. 전체 흐름 요약
-
자식 1-1 (Child1_1): "팝업 열기" 버튼 클릭 시 open-popup 이벤트를 자식 1을 통해 부모로 전달.
-
부모 (Parent): open-popup 이벤트를 받아 isPopupVisible을 true로 설정, 자식 2(팝업)를 렌더링.
-
자식 2 (Child2): 사용자가 name과 type을 입력하고 제출하면 submit-popup 이벤트로 데이터를 부모로 전송, 팝업 닫힘.
-
부모 (Parent): submit-popup 이벤트를 받아 displayData에 데이터를 저장하고, 자식 1을 통해 자식 1-1로 전달.
-
자식 1-1 (Child1_1): displayData props를 받아 name과 type을 표시.
8. 추가 팁
-
입력 검증: 자식 2에서 더 엄격한 입력 검증(예: 길이 제한, 형식 체크)을 추가할 수 있습니다.
-
애니메이션: <transition> 태그로 팝업의 등장/사라짐에 애니메이션을 추가.
-
타입 강화: 데이터 인터페이스를 정의하여 타입 안정성을 높일 수 있습니다:
728x90
interface PopupData {
name: string;
type: string;
}
const displayData = ref<PopupData | null>(null);
-
재사용성: 자식 2를 다양한 데이터 구조에 맞게 일반화하여 재사용 가능하도록 수정.
9. 결론
이 예제에서는 Vue 3의 Composition API와 TypeScript를 사용하여 팝업에서 입력된 데이터를 자식 1-1로 전달하여 표시하는 방법을 살펴보았습니다. emit과 props를 활용한 컴포넌트 통신은 복잡한 데이터 흐름을 체계적으로 관리할 수 있는 강력한 도구입니다. 이 코드를 기반으로 입력 폼이나 UI를 커스터마이징하여 실제 애플리케이션에 적용해 보세요!
728x90