[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.
defineProps
와defineEmits
는 무엇인가요?
Vue 3 Composition API에서 컴포넌트 간 데이터 흐름을 정의할 때 사용되는 함수입니다.
defineProps
는 부모 → 자식으로 값을 전달받을 때 타입까지 포함해 정의합니다.defineEmits
는 자식 → 부모로 이벤트를 전달할 때 이벤트명과 데이터 타입을 정의합니다.- 둘 다 TypeScript와 함께 사용할 때 IDE 자동완성, 오류 방지 등에 큰 장점이 있습니다.
함수 | 용도 | 예시 |
---|---|---|
defineProps | 부모 → 자식 값 받기 | <GreetingForm :name="'예은'" /> |
defineEmits | 자식 → 부모 이벤트 발생 | @send="..." |
Q3. Vue에서 emit의 두 번째 인자는 어떤 역할을 하나요?
이벤트에 함께 전달되는 payload(데이터) 역할을 합니다. emit(‘이벤트명’, 데이터)의 형태로 쓰이며, 부모 컴포넌트에서 이벤트 핸들러의 인자로 전달됩니다.
🎯 미션
GreetingForm
에placeholder
나버튼 라벨
을 props로 받아서 다르게 바꿔보기- 이름을 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.