Post

[Vue] 2단계: 컴포넌트 분리 + Props + Emits

[Vue] 2단계: 컴포넌트 분리 + Props + Emits

✅ 3단계: 컴포넌트 분리 + Props + Emits

① 컴포넌트 기본 구조

Vue 3에서는 .vue 파일 하나가 하나의 컴포넌트

기본 구조:

1
2
3
<template> ... </template>
<script setup lang="ts"> ... </script>
<style scoped> ... </style>

② 실습 예제: 이름 입력 + 인사 버튼 → 분리된 컴포넌트로 만들기

🎯 목표

  • 부모 → 자식: 이름을 전달 (props)
  • 자식 → 부모: 버튼 클릭 시 인사 메시지를 전달 (emit)

📁 구조

1
2
3
4
src/
├── App.vue
└── components/
    └── GreetingForm.vue

✅ 1. GreetingForm.vue (자식 컴포넌트)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
  <form @submit.prevent="submitForm">
    <input v-model="inputName" placeholder="이름을 입력하세요" />
    <button>인사 보내기</button>
  </form>
</template>

<script setup lang="ts">
import { ref } from 'vue'

// 부모로부터 props 받기 (기본값 설정도 가능)
const props = defineProps<{
  defaultName?: string
}>()

// emit 정의 (이벤트 이름과 payload 타입)
const emit = defineEmits<{
  (e: 'sendGreeting', greeting: string): void
}>()

const inputName = ref(props.defaultName || '')

function submitForm() {
  if (inputName.value.trim()) {
    emit('sendGreeting', `안녕하세요, ${inputName.value}님!`)
    inputName.value = ''
  }
}
</script>

✅ 2. App.vue (부모 컴포넌트)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
  <GreetingForm @sendGreeting="handleGreeting" />

  <ul>
    <li v-for="(msg, index) in greetings" :key="index"></li>
  </ul>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import GreetingForm from './components/GreetingForm.vue'

const greetings = ref<string[]>([])

function handleGreeting(message: string) {
  greetings.value.push(message)
}
</script>

🔁 데이터 흐름 요약

1
2
3
4
<자식 컴포넌트>
emit('sendGreeting', greeting) ──────▶ @sendGreeting="handleGreeting"
                                                      
<부모 컴포넌트>              <────── handleGreeting(greeting)

즉, greeting은 부모로 전달되는 중요한 인사 메시지 데이터 !


💡 주요 개념 정리

개념설명TypeScript 사용법
props부모 → 자식 데이터 전달defineProps<{ name: string }>()
emit자식 → 부모 이벤트 전달defineEmits<{ (e: '이벤트명', payload: 타입): void }>()
v-model입력 양방향 바인딩ref<string>()
@submit.prevent폼 제출 시 새로고침 방지Vue 내장 디렉티브

💬 면접 포인트 질문

Q1. Vue에서 컴포넌트 간 데이터는 어떻게 전달되나요?

  • 부모 → 자식: props를 통해 전달
  • 자식 → 부모: emit으로 이벤트 전달

Q2. definePropsdefineEmits는 무엇인가요?

Vue 3 Composition API에서 컴포넌트 간 데이터 흐름을 정의할 때 사용되는 함수입니다.

  • defineProps는 부모 → 자식으로 값을 전달받을 때 타입까지 포함해 정의합니다.
  • defineEmits는 자식 → 부모로 이벤트를 전달할 때 이벤트명과 데이터 타입을 정의합니다.
  • 둘 다 TypeScript와 함께 사용할 때 IDE 자동완성, 오류 방지 등에 큰 장점이 있습니다.
함수용도예시
defineProps부모 → 자식 값 받기<GreetingForm :name="'예은'" />
defineEmits자식 → 부모 이벤트 발생@send="..."

Q3. Vue에서 emit의 두 번째 인자는 어떤 역할을 하나요?

이벤트에 함께 전달되는 payload(데이터) 역할을 합니다. emit(‘이벤트명’, 데이터)의 형태로 쓰이며, 부모 컴포넌트에서 이벤트 핸들러의 인자로 전달됩니다.


🎯 미션

  1. GreetingFormplaceholder버튼 라벨을 props로 받아서 다르게 바꿔보기
  2. 이름을 10자 이상 입력하면 emit을 막고 경고 메시지 보여주기
  • app.vue (부모 컴포넌트)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
  <GreetingForm
    placeholder="닉네임을 입력하세요"
    @sendGreeting="handleGreeting"
  />
  <ul>
    <li v-for="(msg, index) in greetings" :key="index"></li>
  </ul>
</template>

<script setup lang="ts">
import { ref } from "vue";
import GreetingForm from "./components/GreetingForm.vue";

const greetings = ref<string[]>([]);

function handleGreeting(message: string) {
  greetings.value.push(message);
}
</script>
  • GreetingForm.vue (자식 컴포넌트)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template>
  <form @submit.prevent="submitForm">
    <input v-model="inputName" :placeholder="placeholder" />
    <button>인사 보내기</button>
  </form>
</template>

<script setup lang="ts">
import { ref } from "vue";

// 부모로부터 props 받기 (기본값 설정도 가능)
const props = defineProps<{
  defaultName?: string;
  placeholder?: string;
}>();

// emit 정의 (이벤트 이름과 payload 타입)
const emit = defineEmits<{
  (e: "sendGreeting", greeting: string): void;
}>();

const inputName = ref(props.defaultName || "");
const placeholder = ref(props.placeholder || "이름을 입력하세요");

function submitForm() {
  if (inputName.value.trim().length > 10) {
    alert("이름을 10자 이하로 입력해주세요.");
    inputName.value = "";
    return;
  }
  if (inputName.value.trim()) {
    emit("sendGreeting", `안녕하세요, ${inputName.value}님!`);
    inputName.value = "";
  }
}
</script>

END

This post is licensed under CC BY 4.0 by the author.