dev.syw

컴포넌트 등록, props 전달, emit으로 이벤트 올려보내기.

컴포넌트 — props 와 emit

앱이 커지면 화면 전체를 한 파일에 담을 수 없습니다. 버튼, 카드, 목록 같은 조각을 컴포넌트로 나누고 조립하죠. 이때 부모는 자식에게 데이터를 props 로 내려주고, 자식은 부모에게 사건을 emit 으로 올려보냅니다. 이 단방향 흐름이 Vue 컴포넌트 통신의 기본입니다.

학습 목표

  • 자식 컴포넌트를 import 해서 사용한다.
  • defineProps 로 부모에게서 데이터를 받는다.
  • defineEmits 로 부모에게 이벤트를 올려보낸다.
  • 단방향 데이터 흐름의 원칙과 props 검증을 이해한다.

컴포넌트 등록과 사용

<script setup> 에서는 자식 컴포넌트를 import 만 하면 바로 template 에서 쓸 수 있습니다. 별도 등록 코드가 필요 없습니다.

<!-- Parent.vue -->
<script setup>
import UserCard from './UserCard.vue';
</script>

<template>
  <UserCard name="민수" :age="20" />
</template>

props 받기 — defineProps

자식은 defineProps 로 받을 데이터를 선언합니다. defineProps 는 import 없이 쓸 수 있는 컴파일러 매크로입니다.

<!-- UserCard.vue -->
<script setup>
const props = defineProps({
  name: String,
  age: Number,
});

console.log(props.name); // script 에서 접근
</script>

<template>
  <div class="card">
    <h3>{{ name }}</h3>   <!-- template 에선 바로 -->
    <p>{{ age }}살</p>
  </div>
</template>

💡 TIP — 부모에서 name="민수" 처럼 문자열을 그냥 넘기면 문자열로, 숫자·불리언·배열·객체는 :age="20" 처럼 v-bind(:)를 붙여 넘겨야 실제 타입으로 전달됩니다.

단방향 데이터 흐름

데이터는 항상 부모 → 자식 한 방향으로 흐릅니다. 자식이 받은 props를 직접 바꾸면 안 됩니다.

// X 금지 — props 는 읽기 전용
props.age = 30;
JavaScript

값을 바꿔야 한다면 이벤트로 부모에게 요청하고, 부모가 원본을 바꿔서 다시 내려보냅니다.

emit — 부모에게 이벤트 올리기

자식은 defineEmits 로 발생시킬 이벤트를 선언하고, 호출해서 부모에게 알립니다.

<!-- UserCard.vue -->
<script setup>
defineProps({ name: String });
const emit = defineEmits(['select', 'delete']);
</script>

<template>
  <div class="card">
    <h3>{{ name }}</h3>
    <button @click="emit('select', name)">선택</button>
    <button @click="emit('delete')">삭제</button>
  </div>
</template>

부모는 @이벤트명 으로 받습니다.

<!-- Parent.vue -->
<template>
  <UserCard
    name="민수"
    @select="onSelect"
    @delete="onDelete"
  />
</template>

emit('select', name) 의 두 번째 인자는 부모 핸들러 onSelect(payload) 의 인자로 전달됩니다.

props 검증

타입뿐 아니라 필수 여부·기본값·커스텀 검증을 줄 수 있습니다.

const props = defineProps({
  title: { type: String, required: true },
  count: { type: Number, default: 0 },
  status: {
    type: String,
    validator: (v) => ['ok', 'warn', 'error'].includes(v),
  },
});
JavaScript

⚠️ 주의 — 객체·배열의 default 는 함수로 반환해야 합니다: default: () => []. 그렇지 않으면 모든 인스턴스가 같은 참조를 공유해 버그가 생깁니다.

요약

  • <script setup> 에선 자식 컴포넌트를 import 만 하면 쓸 수 있다.
  • defineProps 로 부모 데이터를 받고, 객체/숫자 등은 : 로 넘긴다.
  • props는 읽기 전용이며 데이터는 부모→자식 단방향으로 흐른다.
  • defineEmits 로 이벤트를 선언·발생시켜 부모에게 알린다.

연습문제

  1. name, price 를 props로 받아 표시하는 ProductItem.vue 를 만들어 보세요.
  2. 1번 컴포넌트에 "담기" 버튼을 두고, 클릭 시 add 이벤트로 상품 이름을 부모에게 emit 해 보세요.
  3. price props에 required: true 와 타입 검증을 추가해 보세요.
  4. 자식이 props를 직접 수정하면 왜 안 되는지, 대신 어떻게 해야 하는지 설명해 보세요.

힌트defineEmits(['add'])emit('add', name). 부모에서 @add="cart.push($event)" 처럼 받을 수 있습니다.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

Vue.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.