Vue 3 – Props로 컴포넌트에 데이터 전달하기

2025. 3. 30. 15:49Front-End/Vue.js

반응형

컴포넌트를 사용하다 보면 화면 구조는 동일하지만 표시되는 데이터만 다르게 처리하고 싶을 때가 많다. 예를 들어 블로그 게시글을 나타내는 PostItem 컴포넌트를 만들고, 게시글의 제목과 내용을 외부에서 받아서 다르게 보여주고 싶을 수 있다.

이런 경우 사용하는 것이 바로 props다.

 

Props란?

props는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 수 있도록 하는 사용자 정의 속성이다.

간단히 말하면, 컴포넌트를 재사용하되 그 안에 표시되는 데이터를 동적으로 다르게 주고 싶을 때 사용하는 메커니즘이다.

 

Props 선언 방법

- 문자열 배열로 선언

// PostItem.vue
export default {
  props: ['title'],
  setup(props) {
    console.log(props.title);
  }
}

 

- 객체 형태로 선언

좀 더 구체적으로 타입이나 기본값, 유효성 검사 등을 추가하고 싶을 때는 객체 형태로 선언한다.

export default {
  props: {
    title: String,
    views: {
      type: Number,
      default: 0
    },
    isPublished: {
      type: Boolean,
      required: true
    },
    tagList: {
      type: Array,
      default: () => []
    },
    status: {
      validator(value) {
        return ['draft', 'public', 'archived'].includes(value);
      }
    }
  }
}
  • type: 기대하는 타입 지정
  • required: 필수 여부
  • default: 기본값 설정 (객체/배열은 함수로 반환)
  • validator: 유효성 검사 함수

 

Props 사용 방법

1. 템플릿에서 직접 사용

<template>
  <article>
    <h2>{{ title }}</h2>
    <p>{{ content }}</p>
  </article>
</template>

 

2. setup() 함수에서 사용

export default {
  props: ['title', 'content'],
  setup(props) {
    console.log(props.title);
    return { ...props };
  }
}

 


Props 이름 규칙

  • 컴포넌트 내부에서는 camelCase 사용 (예: postTitle)
  • 부모에서 전달할 때는 kebab-case 사용 (예: post-title)
<!-- 부모 컴포넌트 -->
<PostItem post-title="Vue 소개" />
// 자식 컴포넌트
export default {
  props: ['postTitle']
}

 

객체 형태로 여러 props 한 번에 전달

v-bind를 이용해 객체 전체를 props로 전달할 수 있다.

const post = reactive({
  title: 'Vue 3 배우기',
  content: '컴포넌트와 반응형 시스템을 익혀보세요.'
});
<!-- 아래 두 줄은 동일한 의미 -->
<PostItem v-bind="post" />
<PostItem :title="post.title" :content="post.content" />

 

단방향 바인딩

Vue의 props는 단방향으로만 전달된다. 부모 → 자식 방향만 가능하며, 자식에서 props를 직접 변경할 수는 없다.

export default {
  props: ['count'],
  setup(props) {
    // ❌ 잘못된 예시 - props는 readonly
    props.count = 10;
  }
}

 

자식에서 변경이 필요한 경우

1. props를 로컬 state로 복사

초기값 용도로 사용하고 이후에는 로컬 상태로 관리한다.

export default {
  props: ['initialValue'],
  setup(props) {
    const value = ref(props.initialValue);
    return { value };
  }
}

2. 변형된 값을 계산해서 사용하는 경우

computed를 활용하면 원본 props는 유지하면서 변형된 값을 만들 수 있다.

export default {
  props: ['username'],
  setup(props) {
    const formatted = computed(() => props.username.trim().toUpperCase());
    return { formatted };
  }
}

 

 

객체/배열 props의 변경

Vue에서는 객체나 배열이 참조 타입이기 때문에 자식 컴포넌트에서 내부 값을 수정할 수는 있지만, 이건 권장되는 방식이 아니다.

export default {
  props: ['user'],
  setup(props) {
    // 가능하지만 추천되지 않음
    props.user.name = '홍길동';
  }
}

이렇게 하면 데이터 흐름을 추적하기 어려워지고, 유지보수가 힘들어진다. 대신 변경이 필요하다면 emit으로 부모에게 알리는 방식이 좋다.

 

Boolean props의 캐스팅

불리언 타입의 props는 HTML 상에서 값 없이 사용할 수 있다.

export default {
  props: {
    disabled: Boolean
  }
}
<!-- true로 전달됨 -->
<MyButton disabled />

<!-- false로 전달됨 -->
<MyButton :disabled="false" />

 

 

toRef, toRefs로 반응형 유지하며 구조 분해

setup() 함수 안에서 props를 구조분해할 경우 반응형 연결이 끊길 수 있다. 이때는 toRef나 toRefs를 사용한다.

import { toRef, toRefs } from 'vue';

export default {
  props: ['message', 'count'],
  setup(props) {
    const message = toRef(props, 'message');
    const { count } = toRefs(props);

    return { message, count };
  }
}

toRef는 특정 key 하나만 반응형으로 연결하고, toRefs는 전체 props 객체를 반응형으로 유지하면서 구조분해할 수 있도록 해준다.


정리

  • props는 부모 → 자식 데이터 전달용
  • 타입, 필수 여부, 기본값, 유효성 검사까지 선언 가능
  • 객체 전체를 v-bind로 전달할 수 있음
  • props는 단방향이며, 자식에서 직접 수정하면 안 됨
  • 수정이 필요할 경우 로컬 변수에 복사하거나 emit으로 부모에게 요청
  • 구조 분해 시 toRef, toRefs로 반응형 유지

컴포넌트를 유연하게 설계하려면 props를 명확히 선언하고, 그 사용 목적에 따라 읽기 전용 데이터인지, 상태로 전환할 데이터인지 구분해서 쓰는 것이 중요하다.

반응형