Development/Vue.js

[Vue.js] Document 따라하기 - Props

도롱뇽도롱 2022. 7. 18. 18:00
반응형

이 페이지에서는 Component Basics를 이미 읽었다고 가정한다. Component를 처음 사용하는 경우 Component Basics를 먼저 읽고 와야 한다.

 

Props Declaration

Vue component는 명시적인 props 선언이 필요하기에 이를 통해 Vue는 component에 전달된 외부 props 중 어떤 것이 fallthrough 속성으로 처리되어야 하는지 알 수 있다(전용 섹션에서 설명함).

 

props는 props 옵션을 사용하여 선언된다.

export default {
  props: ['foo'],
  created() {
    // props는 `this`에 노출된다
    console.log(this.foo)
  }
}

Props Declaration
Props Declaration
Props Declaration

문자열 배열을 사용하여 props를 선언하는 것 외에도 객체 구문을 사용할 수도 있다.

export default {
  props: {
    title: String,
    likes: Number
  }
}

객체 선언 구문의 각 속성에 대해 키는 props의 이름이고 값은 값이 될 데이터의 타입에 해당하는 생성자 함수(Number, String 등)여야 한다.

Props Declaration using object
Props Declaration using object
Props Declaration using object

타입을 지정하는 것은 component를 가독성 좋게 문서화할 뿐만 아니라 component를 사용하는 다른 개발자가 잘못된 타입을 전달하는 경우 브라우저 콘솔에 경고를 출력한다. 이 페이지에서 prop 유효성 검사에 대한 자세한 내용을 논의할 것이다.

 

참조: Component Props에 타입 지정하기

 

Prop Passing Details

Prop Name Casing

긴 prop 명을 선언할 때 키에 따옴표를 사용할 필요없는 camelCase를 사용한다. camelCase를 사용한 prop 명은 유효한 JavaScript 식별자이기 때문에 템플릿 표현식에서 직접 참조하여 사용할 수 있다.

export default {
  props: {
    greetingMessage: String
  }
}
<span>{{ greetingMessage }}</span>

기술적으로 props를 자식 component에 전달할 때 camelCase를 사용할 수도 있다(DOM 템플릿 제외). 그러나 camelCase로 선언된 props 속성일지라도 관례적으로 HTML 속성 표기법과 동일하게 kebab-case로 표기하여 사용하도록 해야 한다.

<MyComponent greeting-message="hello" />

기본 element와 Vue component를 쉽게 구별하여 템플릿 가독성을 향상하기 위해 component 태그는 되도록 PascalCase를 사용한다. 그러나 props를 전달할 때 camelCase를 사용하면 실질적인 이점이 많지 않으므로 각 언어의 규칙을 따르기로 했다.

Prop Name Casing
Prop Name Casing
Prop Name Casing

 

Static vs. Dynamic Props

지금까지는 아래와 같이 정적인 값으로 전달된 props를 보았다.

<BlogPost title="My journey with Vue" />

다음과 같이 v-bind 또는 축약어(:)를 사용하여 동적으로 할당된 props도 보았다.

<!-- 변수 값을 동적으로 할당 -->
<BlogPost :title="post.title" />

<!-- 복잡한 표현식의 값을 동적으로 할당 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

Static vs. Dynamic Props
Static vs. Dynamic Props

 

Passing Different Value Types

위의 두 가지 예제에서 문자열 값을 전달했지만 모든 타입의 값은 prop에 전달될 수 있다.

 

Number

<!-- `42`는 정적이지만 Vue에 이것이 문자열이 아닌  -->
<!-- JavaScript 표현식임을 알려주려면 v-bind가 필요하다  -->
<BlogPost :likes="42" />

<!-- 변수 값을 동적으로 할당 -->
<BlogPost :likes="post.likes" />

 

Boolean

<!-- 값이 없는 prop은 `true`가 전달된다. -->
<BlogPost is-published />

<!-- `false`는 정적이지만 Vue에 이것이 문자열이 아닌  -->
<!-- JavaScript 표현식임을 알려주려면 v-bind가 필요하다  -->
<BlogPost :is-published="false" />

<!-- 변수 값을 동적으로 할당 -->
<BlogPost :is-published="post.isPublished" />

 

Array

<!-- 배열은 정적이지만 Vue에 이것이 문자열이 아닌  -->
<!-- JavaScript 표현식임을 알려주려면 v-bind가 필요하다  -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- 변수 값을 동적으로 할당 -->
<BlogPost :comment-ids="post.commentIds" />

 

Object

<!-- 객체는 정적이지만 Vue에 이것이 문자열이 아닌  -->
<!-- JavaScript 표현식임을 알려주려면 v-bind가 필요하다  -->
<BlogPost
  :author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
  }"
 />

<!-- 변수 값을 동적으로 할당 -->
<BlogPost :author="post.author" />

 

Binding Multiple Properties Using an Object

객체의 모든 속성을 props로 전달하려면 인자 없이 v-bind(:prop-name 대신 v-bind)를 사용할 수 있다.

예를 들어, post 객체가 주어졌을 때:

export default {
  data() {
    return {
      post: {
        id: 1,
        title: 'My Journey with Vue'
      }
    }
  }
}

다음 템플릿은:

<BlogPost v-bind="post" />

다음과 동일하다.

<BlogPost :id="post.id" :title="post.title" />

Binding Multiple Properties Using an Object
Binding Multiple Properties Using an Object
Binding Multiple Properties Using an Object

 

One-Way Data Flow

모든 props는 자식 속성과 부모 속성 사이에 하향식 단방향 바인딩을 형성한다. 부모 속성이 업데이트되면 자식으로 흐르지만 그 반대는 안된다. 이렇게 하면 자식 component가 실수로 부모의 상태를 변경하여 앱의 데이터 흐름을 이해하기 어렵게 만드는 것을 방지할 수 있다.

 

또한 부모 component가 업데이트될 때마다 자식 component의 모든 props가 최신 값으로 업데이트된다. 그렇기에 자식 component 내부에서 props를 변경하려고 시도해서는 안된다. 만약 자식 component에서 props를 변경하려 하면 Vue는 콘솔에서 다음과 같이 경고한다.

export default {
  props: ['foo'],
  created() {
    // ❌ warning, props are readonly!
    this.foo = 'bar'
  }
}

일반적으로 prop을 변경하고 싶은 두 가지 경우가 있다.

1. prop은 초기 값을 전달하는 데 사용되며 자식 component는 나중에 이를 로컬 데이터 속성으로 사용하려고 한다.

→ 이 경우 prop을 초기 값으로 사용하는 로컬 데이터 속성을 정의하는 것이 가장 좋다.

export default {
  props: ['initialCounter'],
  data() {
    return {
      // this.initialCounter는 counter의 초기 값으로 사용된다
      // 추후 props가 갱신되어도 counter 값이 업데이트 되지 않는다
      counter: this.initialCounter
    }
  }
}

2. prop은 변환이 필요한 원시 값으로 전달된다.

 이 경우 prop의 값을 사용하여 computed 속성을 정의하는 것이 가장 좋다.

export default {
  props: ['size'],
  computed: {
    // prop이 변경될 때 computed 속성은 자동으로 업데이트 된다
    normalizedSize() {
      return this.size.trim().toLowerCase()
    }
  }
}

One-Way Data Flow
One-Way Data Flow
One-Way Data Flow

 

Mutating Object / Array Props

객체와 배열이 props로 전달되면 자식 component는 바인딩된 prop을 변경할 수 없지만 객체 또는 배열의 중첩 속성을 변경할 수는 있다. 이것은 JavaScript에서 객체와 배열이 참조로 전달되고 Vue가 이러한 변경까지 방지하는 것은 너무 큰 비용이 들기 때문이다.

 

이러한 구현의 주요 단점은 자식 component가 명확하지 않은 방식으로 부모 component의 상태에 영향을 미치게 하여 향후 데이터 흐름에 대해 추론을 어렵게 만든다는 것이다. 가장 좋은 방법은 부모와 자식이 의도적으로 밀접하게 연결되어 있지 않는 한 이러한 변경을 피하는 것이며 필요 시 자식은 부모가 변경을 수행할 수 있도록 emit 이벤트를 호출하는 방식으로 구현해야 한다.

 

Prop Validation

이미 본 것과 같이 component는 props에 대한 요구 사항을 지정할 수 있다. 요구 사항이 충족되지 않으면 Vue는 브라우저의 JavaScript 콘솔에서 경고 메시지를 출력한다. 이것은 다른 사람들이 사용하도록 의도된 component를 개발할 때 특히 유용하다.

 

Props에 유효성 검사를 지정하려면 props 옵션에 문자열 배열 대신 유효성 검사 요구 사항이 있는 객체를 제공하면 된다. 예를 들어:

export default {
  props: {
    // 기본 타입 체크
    //  (`null`과 `undefined`는 모든 타입에서 허용된다)
    propA: Number,
    // 복수 타입 허용
    propB: [String, Number],
    // 필수 문자열
    propC: {
      type: String,
      required: true
    },
    // 기본 값을 갖는 숫자
    propD: {
      type: Number,
      default: 100
    },
    // 기본 값을 갖는 객체
    propE: {
      type: Object,
      // 객체 또는 배열 기본값은 팩토리 함수에서 반환되어야 한다.
      // 함수는 component가 인수로 받은 rawProps를 받는다.
      // rawProps는 부모 component에게서 받는 props 전체 객체
      default(rawProps) {
        return { message: 'hello' }
      }
    },
    // 사용자 정의 유효성 검사 함수
    propF: {
      validator(value) {
        // 값은 다음 문자열 중 하나와 일치해야 한다.
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 기본 값이 있는 함수
    propG: {
      type: Function,
      // 객체 또는 배열 기본값과 달리 이것은 팩토리 함수가 아니라
      // 기본값으로 사용하는 함수이다.
      default() {
        return 'Default function'
      }
    }
  }
}

추가 세부 사항:

  • required: true가 지정되지 않은 모든 prop은 기본적으로 선택 사항(optional)이다.
  • prop의 타입이 Boolean이 아니고 optional인 경우 값이 누락되면 undefined 값을 갖는다.
  • prop의 타입이 Boolean이고 누락되었다면 false로 캐스팅된다. 의도에 따라 기본 값을 설정해야 할 수 있다.
  • prop이 누락되었거나 명시적으로 선언된 값이 undefined이고 기본 값이 지정되어 있다면 기본 값이 사용된다.

prop 유효성 검사가 실패하면 Vue는 콘솔에 경고를 출력한다(개발 빌드를 사용하는 경우).

 

props는 component 인스턴스가 생성되기 전에 유효성 검사가 실행되기에 default 또는 validator 함수 내에서 인스턴스 속성(예: data, computed 등)을 사용할 수 없다.

 

Runtime Type Checks

type은 다음 기본 생성자 중 하나일 수 있다.

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

또한 type은 사용자 정의 클래스 또는 생성자 함수일 수도 있으며 instanceof를 사용하여 검사를 실시한다.

예를 들어, 다음 클래스가 주어졌을 때:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

props 타입으로 사용할 수 있다.

export default {
  props: {
    author: Person
  }
}

author prop 값이 new Person으로 생성되었는지 확인한다.

 

Boolean Casting

Boolean type의 props는 네이티브 boolean 속성의 동작을 모방하는 특별한 캐스팅 규칙이 있다. 다음 선언과 함께 <MyComponent>가 제공된다.

export default {
  props: {
    disabled: Boolean
  }
}

Component는 다음과 같이 사용될 수 있다.

<!-- :disabled="true"를 전달하는 것과 같음 -->
<MyComponent disabled />

<!-- :disabled="false"를 전달하는 것과 같음 -->
<MyComponent />

Boolean Casting
Boolean Casting
Boolean Casting

예를 들어 prop이 여러 타입을 허용하도록 선언된 경우

export default {
  props: {
    disabled: [Boolean, Number]
  }
}

Boolean에 대한 캐스팅 규칙은 타입 선언 순서에 관계없이 적용된다.

 

 

관련 Repo

https://github.com/galaxyuliana/VueExercises/tree/master/first-vue-project/src/components/Exercise/14

 

참고 자료

https://vuejs.org/guide/components/props.html

반응형