개발새발 로그

Vue - Props - 정적/동적 props, 단방향 데이터 흐름, 데이터 유효성 검사 본문

Vue

Vue - Props - 정적/동적 props, 단방향 데이터 흐름, 데이터 유효성 검사

이즈흐 2023. 11. 25. 18:13

 

정적 vs. 동적 Props

지금까지는 정적인 값으로 전달된 props 예제들을 보았습니다:

<BlogPost title="Vue와 함께한 나의 여행" />

그리고 v-bind(:: 축약어)를 사용하여 동적으로 할당된 props도 보았습니다:

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

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

다양한 타입의 값 전달

위의 두 가지 예제에서 문자열 값을 전달했지만, 사실 어떠한 타입의 값도 prop로 전달할 수 있습니다.

숫자

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

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

불리언

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

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

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

배열

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

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

객체

<!-- 객체가 정적이더라도 Vue에 이것이 문자열이 아닌         -->
<!-- JavaScript 표현식임을 알려주려면 v-bind가 필요합니다. -->
<BlogPost
  :author="{
    name: '신형만',
    company: '떡잎 상사'
  }"
 />

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

객체로 여러 속성 바인딩하기

객체의 모든 속성을 props로 전달하려면 인자 없이 v-bind를 사용할 수 있습니다. 예를 들어, post 객체가 주어지면

const post = {
  id: 1,
  title: 'Vue와 함께하는 나의 여정'
}

다음 템플릿은

<BlogPost v-bind="post" />

다음과 동일합니다

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

 

 

 

단방향 데이터 흐름

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

또한 부모 컴포넌트가 업데이트될 때마다 자식 컴포넌트의 모든 props가 최신 값으로 업데이트 됩니다. 따라서 자식 컴포넌트 내부에서 props를 변경하려 하면 안 됩니다. 그렇지 않을 경우, Vue는 콘솔에서 다음과 같이 경고합니다.

const props = defineProps(['foo'])

// ❌ warning, props are readonly!
//   (경고, props는 읽기 전용입니다!)
props.foo = 'bar'

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

  1. prop은 초기 값을 전달하는 데 사용되며, 자식 컴포넌트는 나중에 이를 로컬 데이터 속성으로 사용하려고 합니다. 이 경우 prop을 초기 값으로 사용하는 로컬 데이터 속성을 정의하는 것이 가장 좋습니다:
    const props = defineProps(['initialCounter'])
    
    // props.initialCounter는 counter의 초기 값으로 사용됩니다.
    // 추후 props가 갱신되어도 counter 값이 업데이트 되지 않습니다.
    const counter = ref(props.initialCounter)
  2. prop은 변환이 필요한 원시 값으로 전달됩니다. 이 경우 prop의 값을 사용하여 계산된 속성을 정의하는 것이 가장 좋습니다:
    const props = defineProps(['size'])
    
    // prop이 변경될 때, 계산된 속성은 자동으로 업데이트 됩니다.
    const normalizedSize = computed(() => props.size.trim().toLowerCase())

객체/배열 props 변경에 관하여

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

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

 

 

Prop 유효성 검사

defineProps({
  // 기본 타입 체크
  //  (`null`과 `undefined`는 모든 타입에서 허용됩니다)
  propA: Number,
  // 여러 타입 허용
  propB: [String, Number],
  // 문자열 필수
  propC: {
    type: String,
    required: true // 필수인지 확인
  },
  // 기본 값을 가지는 숫자형
  propD: {
    type: Number,
    default: 100 
  },
  // 기본 값을 가지는 객체
  propE: {
    type: Object,
    // 객체 또는 배열 기본값은 팩토리 함수에서 반환되어야 합니다.
    // 함수는 컴포넌트에서 받은 rawProps를 인자로 받습니다.
    // (rawProps: 부모 컴포넌트에게 받은 props 전체 객체)
    default(rawProps) {
      return { message: '안녕!' }
    }
  },
  // 사용자 정의 유효성 검사 함수
  propF: {
    validator(value) {
      // 값은 다음 문자열 중 하나와 일치해야 합니다.
      return ['성공', '경고', '위험'].includes(value)
    }
  },
  // 기본값이 있는 함수
  propG: {
    type: Function,
    // 기본값 객체나 배열을 정의하는 팩토리 함수가 아니라
    // 기본값으로 사용할 함수입니다.
    default() {
      return 'Default function'
    }
  }
})

또한 type은 사용자 정의 클래스 또는 생성자 함수일 수도 있으며, instanceof를 사용하여 검사를 실시합니다. 예를 들어, 다음 클래스가 주어졌을 때:

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

props 타입으로 사용할 수 있습니다:

defineProps({
  author: Person
})

 

 

 

만약 props된 데이터에서 하위컴포넌트가 선언한 데이터가 없다면?

- 원래라면 오류가 뜨게 된다.

- 하지만 아래처럼 해당 데이터를 객체로 만들고 default속성을 넣어 값을 넣어주면 props에 해당데이터가 없을 때 default값을 출력한다.

 

 

 

추가 설명

팩토리 함수?

객체를 반환하는 함수

 

기본값을 갖는 객체 타입에서 기본 값을 팩토리함수로 사용하는 이유는?

현재 vue에서 데이터는 항상 함수여야한다.

배열이나 객체는 참조형 데이터이다.

참조형 데이터는 할당을 통해서 전달되는 메모리 주소가 같은 곳을 바라보기 때문에 한쪽이 수정되면 다른 쪽도 수정되기 때문이다.

 

 

HTML 속성에서는 케밥케이스, Props명은 카멜케이스!

728x90
반응형
LIST