Vue.js 를 배워보자

Vue 3에서 팝업에서 데이터 가져와 자식 1-1에 표시하기

_Blue_Sky_ 2025. 5. 20. 21:06
728x90

 

이 글에서는 Vue 3의 Composition API와 TypeScript를 사용하여 자식 2(팝업)에서 입력된 nametype 데이터를 가져와 자식 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 컴포넌트로, 팝업 형태로 nametype 입력 폼을 제공하고 데이터를 부모로 전송.

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에서 받은 nametype 데이터를 저장하며, 자식 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 컴포넌트는 팝업 호출 버튼을 제공하고, 부모로부터 받은 nametype 데이터를 표시합니다.
<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를 받아 nametype을 표시.
  • 데이터가 없을 경우 기본 메시지를 표시.
  • 버튼 클릭 시 open-popup 이벤트를 발생.

 
6. 자식 2 컴포넌트 (Child2.vue)
자식 2 컴포넌트는 팝업으로, nametype 입력 폼을 제공하고 제출 시 데이터를 부모로 전송합니다.
 
<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>
설명:
  • nametype 입력값을 ref로 관리.
  • 제출 버튼 클릭 시 입력값을 검증하고, submit-popup 이벤트로 데이터를 부모로 전송.
  • 닫기 버튼 클릭 시 close-popup 이벤트를 발생.
  • 입력 폼은 간단한 스타일로 사용자 친화적으로 구성.

7. 전체 흐름 요약
  1. 자식 1-1 (Child1_1): "팝업 열기" 버튼 클릭 시 open-popup 이벤트를 자식 1을 통해 부모로 전달.
  2. 부모 (Parent): open-popup 이벤트를 받아 isPopupVisibletrue로 설정, 자식 2(팝업)를 렌더링.
  3. 자식 2 (Child2): 사용자가 nametype을 입력하고 제출하면 submit-popup 이벤트로 데이터를 부모로 전송, 팝업 닫힘.
  4. 부모 (Parent): submit-popup 이벤트를 받아 displayData에 데이터를 저장하고, 자식 1을 통해 자식 1-1로 전달.
  5. 자식 1-1 (Child1_1): displayData props를 받아 nametype을 표시.

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로 전달하여 표시하는 방법을 살펴보았습니다. emitprops를 활용한 컴포넌트 통신은 복잡한 데이터 흐름을 체계적으로 관리할 수 있는 강력한 도구입니다. 이 코드를 기반으로 입력 폼이나 UI를 커스터마이징하여 실제 애플리케이션에 적용해 보세요! 
 
728x90