본문으로 바로가기

부모 컴포넌트와 자식 컴포넌트

// scr/components/PageTitle.vue
<template>
  <h2>Page Title</h2>
</template>
// src/views/NestedComponent.vue
<template>
  <div>
    <PageTitle />
  </div>
</template>
<script>
  // 컴포넌트 import
  import PageTitle from '@/components/PageTitle.vue'
  
  export default {
    components: {	// 현재 컴포넌트에서 사용할 컴포넌트 목록
      PageTitle
    },
  }
</script>

 - 컴포넌트에서 다른 컴포넌트를 사용하는 방법은 사용할 컴포넌트를 import한 후 현재 컴포넌트의 템플릿에서 사용할 컴포넌트를 components에 등록하면 된다.

 

   : PageTitle 컴포넌트를 import 한다.

import PageTitle from './components/PageTitle'

   : 현재 컴포넌트의 템플릿에서 사용할 컴포넌트를 등록한다.

components: { PageTitle }

   : HTML에서는 import한 컴포넌트를 이용해서 이름을 이용해서 태그를 만들면 된다.

<PageTitle />
컴포넌트는 페이지 하나 전체가 될 수도 있고, 하나의 페이지를 이루고 있는 단위일 수도 있다.
컴포넌트를 어떻게 설계하는가는 전체 애플리케이션 개발에서 매우 중요하다.

 

부모 컴포넌트에서 자식 컴포넌트로 데이터 전달하기 : Props

// src/components/PageTitle.vue
<template>
  <h2>{{ title }}</h2>
</template>
<script>
  export default {
    props: {
      title: {
        type: String,
        default: '기본 페이지 제목입니다.'
      },
    },
  }
</script>

 - 자식 컴포넌트에 아래와 같이 Props를 추가한다. (사용할 변수명으로)

 - Props에는 부모 컴포넌트로 전달받은 데이터가 저장됨

 - Props에 정의된 키는 저장될 데이터의 타입과 부모 컴포넌트로부터 데이터가 전달되지 않았을 때의 default 값을 정의한다.

   : default를 정의하지 안해도 되지만, 값이 없으면 출력이 되지 않거나 에러가 발생할 수도 있다.

 - Props에 정의된 키는 Vue 인스턴스의 데이터 값으로 사용되기 떄문에 이중 중괄호를 사용해서 문자열에 대한 데이터 바인딩 처리를 할 수 있다.

 

// src/views/NestedComponent.vue
<template>
  <div>
    <PageTitle title="부모 컴포넌트에서 자식 컴포넌트로 데이터 전달!" />
  </div>
</template>

 - 부모 컴포넌트에서 사용하고 있는 자식 컴포넌트에 속성으로 다음과 같이 추가를 해줍니다.

 - 위에서는 속성에 지정한 title 값이 자식 컴포넌트에 정의된 Props의 title에 전달된다.

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때는 속성을 정의하고,
정의된 속성과 동일한 이름의 속성명을 자식 컴포넌트의 props에 정의해서 사용하면 된다.

 

정적/동적 Props 전달

 - 자식 컴포넌트인 PageTitle.vue로 title="부모 컴포넌트에서 자식 컴포넌트로 데이터 전달!" 정적 값을 전달하는 것을 확인 했습니다.

 - v-bind나 약어인 : 문자를 사용해서 props에 동적인 값을 전달할 수 있다.

<template>
  <PageTitle :title="title" />
</template>
<script>
  export default {
    data () {
      return {
        title: '동적 페이지 타이틀입니다.',
      }
    },
  }
</script>

 

숫자형(Number) 전달

 - 숫자 값을 Props로 전달하기 위해서는 v-bind를 통해서만 가능하다.

 

 - v-bind를 사용하지 않은 경우는 level 값을 전달한 1은 숫자 1이 아니라, 문자 "1"이 된다.

<blog-post likes="1 />

 

 - 숫자 값으로 전달하기 위해서는 v-bind를 사용해서 정적으로 전달하거나 동적으로 전달해야 한다.

<!-- 42는 정적이지만, v-bind를 사용함으로써 전달되는 데이터가 자바스크립트 표현식이 된다. -->
<blog-post :likes="1" />

<!-- 변수 값에 동적으로 할당한다. -->
<blog-post :likes="post.likes" />

 

논리 자료형(boolean) 전달

 - 논리 자료형 역시 v-bind를 사용하지 않으면 문자열로 전달되기 떄문에 v-bind를 사용해야 한다.

<!-- true는 정적이지만, v-bind를 사용함으로써 전달되는 데이터가 자바스크립트 표현식이 된다. -->
<blog-post :is-true="true" />

<!-- 변수 값에 동적으로 할당한다. -->
<blog-post :is-true="isTrue" />

 

배열(Array) 전달

 - 배열 역시 v-bind을 사용하지 않으면 문자열로 전달되기 때문에 v-bind를 사용해야 한다.

<!-- 배열이 정적이지만, v-bind를 사용함으로써 전달되는 데이터가 자바스크립트 표현식이된다. -->
<blog-post :comment-ids="[123, 456 ,789]" />

<!-- 변수 값에 동적으로 할당한다. -->
<blog-post :comment-ids="post.commentIds" />

 

객체(Object) 전달

 - 객체 역시 v-bind을 사용하지 않으면 문자열로 전달되기 떄문에, v-bind를 사용해야한다.

<!-- 객체가 정적이지만, v-bind를 사용함으로써 전달되는 데이터가 자바스크립트 표현식이된다. -->
<blog-post :author="{name: 'HongGilDong', age: 25}" />

<!-- 변수 값에 동적으로 할당한다. -->
<blog-post :author="post.author" />

 

객체(Object)의 속성 전달

 - 다음 두개의 코드는 동일하다.

<blog-post v-bind="post" />
<blog-post :id="post.id" :title="post.title" />
data() {
  return {
    post: {
      id: 1,
      title: 'Vue.js를 공부하고 있습니다.'
    }
  }
}

 

Prop 유효성 검사

 - 자식 컴포넌트에서 props 옵션을 정의할 때, 전달받는 데이터 타입, 기본 값(default), 필수 여부(required) 그리고 유효성 검사 함수(validator)인 함수를 통해서 유효성을 검사할 수 있다.

 - props를 통해 전달받는 데이터에 대한 요구사항을 지정할 수 있기 때문에 컴포넌트를 사용하는 개발자가 데이터를 어떤 타입으로 전달해야 할지 명확히 인지해서 사용하는 이점을 얻을 수 있다.

props: {
  // 기본 타입 체크 ('null'과 'undefined'는 모두 타입 유효성 검사를 통과한다.)
  propA: Number,	// Number 타입 체크
  propB: [String, Number],	// 여러 타입 허용
  propC: {	// 문자형이고 부모 컴포넌트로부터 반드시 데이터가 필수로 전달되어야 함
    type: String,
    required: true
  },
  propD: {	// 기본 값(100)을 갖는 숫자형
    type: Number,
    default: 100
  },
  propE: {	// 기본 값을 갖는 객체 타입
    type: Object,
    default: function() {
      return {
        message: 'Hello'
      }
    }
  },
  propF: {	// 커스텀 유효성 검사 함수
    validator: function(value) {
      값이 꼭 아래 세 문자열 중 하나와 일치해야 한다.
      return ['success', 'warning', 'danger'].indexOf(value) != -1
    }
  },
  propG: {	// 기본 값을 갖는 함수
    type: Function,
    // 객체나 배열과 달리 아래 표현은 팩토리 함수가 아니다. 기본 값으로 사용되는 함수이다.
    default: function() {
      return 'Default Function'
    }
  }
}

 

부모 컴포넌트에서 자식 컴포넌트의 이벤트 직접 발생시키기

 - 부모 컴포넌트에서 자식 컴포넌트의 버튼을 클릭하는 이벤트를 직접 발생시켜 보자

// src/views/ChildComponent.vue
<template>
  <div>
    <button
      type="button"
      @click="childFunc"
      ref="btn"
    >
      Click
    </button>
  </div>
</template>
<script>
  exports default: {
    methods: {
      childFunc() {
        console.log('부모 컴포넌트에서 직접 발생시킨 이벤트')
      }
    }
  }
</script>

 - 자식 컴포넌트에 버튼 객체에 ref="btn"로 접근할 수 있도록 작성되었다.

 - HTML 태그에 ref="id"를 지정하면 Vue 컴포넌트의 함수에서 this.$refs를 통해 접근이 가능하게 된다.

 - ref 속성은 HTML 태그에서 사용되는 id와 비슷한 기능을 한다고 생각하면 된다.

 - ref는 유일한 키 값을 사용해야 한다.

 

// src/views/ParentComponent.vue
<template>
  <child-component
    ref="child_component" 
  />
</template>
<script>
  import ChildComponent from './ChildComponent'
  
  export default {
    components: {
      ChildComponent
    },
    mounted() {
      this.$ref.child_component.$refs.btn.click()
    }
  }
</script>

 - 부모 컴포넌틍서 자식 컴포넌트인 child-component에 ref="child_component"를 지정하여, $refs로 접근할 수 있도록 했다.

 - 이렇게 설정하면 부모 컴포넌트에서 자식 컴포넌트 안에 정의된 HTML 객체에 대한 접근이 가능해지고, 자식 컴포넌트의 버튼 객체에 정의한 ref="btn" 이름으로 버튼 객체에 접근해서 click() 이벤트를 발생시킬 수 있게 된다.

 

부모 컴포넌트에서 자식 컴포넌트의 함수 직접 호출하기

// src/views/ChildComponent2
methods: {
  callFromParent() {
    console.log('부모 컴포넌트에서 직접 호출한 함수')
  }
}

 - 자식 컴포넌트에 함수가 정의되어 있다.

// src/views/ParentComponent2
<template>
  <child-component 
    ref="child_component"
  />
</template>
<script>
  import ChildComponent from './ChildComponent2'

  export default {
    components: {
      ChildComponent
    },
    mounted() {
      this.$refs.child_component.callFromParent()
    }
  }
</script>

 - 부모 컴포넌트에서는 자식 컴포넌트를 $refs를 사용하여 접근하게 되며 자식 컴포넌트 내에 정의된 모든 함수를 호출할 수 있다.

 

부모 컴포넌트에서 자식 컴포넌트의 데이터 옵션 값 직접 변경하기

 - 부모 컴포넌트에서는 자식 컴포넌트의 데이터 옵션 값을 직접 변경할 수 있다.

// src/views/ChildComponent3.vue
<template>
  <h1>{{ msg }}</h1>
</template>
<script>
  export default {
    data() {
      return {
        msg: ''
      }
    }
  }
</script>

 - 자식 컴포넌트에는 데이터 옵션에 msg가 정의되어 있습니다.

// src/views/ParentComponent3.vue
<template>
  <div>
    <child-component 
      ref="child_component"
    />
    <button
      type="button"
      @click="changeChildData"
    >
      Change Child Data
    </button>
  </div>
</template>
<script>
  import ChildComponent from './ChildComponent3' 
  
  export default {
    components: { ChildComponent },
    methods: {
      changeChildData() {
        this.$refs.child_component.msg = "부모 컴포넌트가 변경한 데이터"
      }
    }
  }
</script>

 - $refs를 통해서 자식 컴포넌트에 접근하고 나면 자식 컴포넌트에 정의된 데이터 옵션을 직접 변경할 수 있게 된다.

 

자식 컴포넌트에서 부모 컴포넌트로 이벤트/데이터 전달하기 (커스텀 이벤트)

 - 자식 컴포넌트에서 부모 컴포넌트로 이벤트를 전달하기 위해서는 $emit을 사용한다.

// src/views/ChildComponent4.vue
data() {
  return {
    msg: '자식 컴포넌트에서 보내는 메시지'
  }
},
methods: {
  this.$emit('send-message', this.msg)
}

 - 자식 컴포넌트가 mounted 되면 $emit을 통해 부모 컴포넌트의 send-message 이벤트를 호출한다.

 - 이때 msg 데이터를 파라미터로 전송한다.

// src/views/ParentComponent4.vue
<template>
  <child-component
    @send-message="sendMessage" 
  />
<template>
<script>
  import ChildComponent from './ChildComponent4'
  export default {
    components: { ChildComponent },
    methods: {
      sendMessage(data) {
        console.log(data)
      }
    }
  }
</script>

 - 부모 컴포넌트가 mounted되면 #emit을 통해 부모 컴포넌트의 send-message 이벤트를 호출한다.

 - 이때 msg 데이터를 파라미터로 전송한다.

부모 컴포넌트에서 자식 컴포넌트인 ChildComponent를 import하고, 커스텀 이벤트(@send-message)를 정의한다
커스텀 이벤트인 send-message는 자식 컴포넌트에서 $emit으로 호출하게 된다.
이때 부모 컴포넌트에 정의된 sendMesaage 함수가 실행되고, 자식 컴포넌트로부터 전달받은 데이터를 부모 컴포넌트에서 사용할 수 있다.

 

부모 컴포넌트에서 자식 컴포넌트의 데이터 옵션 값 동기화하기

 - 부모 컴포넌트에서 computed를 이용하면 자식 컴포넌트에 정의된 데이터 옵션 값의 변경사항을 항상 동기화시킬 수 있다.

// src/views/ChildCompoent5.vue
<template>
  <button
    type="button"
    @click="childFunc"
    ref="btn"
  >
    자식 컴포넌트 데이터 변경
  </button>
</template>
<script>
  export default {
    data() {
      return {
        msg: '메시지'
      }
    },
    methods: {
      childFunc() {
        this.msg = '변경된 메시지'
      }
    }
  }
</script>

 - 자식 컴포넌트에는 데이터 옵션에 msg가 정의되어 있다.

// src/views/ParentComponent5.vue
<template>
  <div>
     <button
       type="button"
       @click="checkChild"
     >
       자식 컴포넌트 데이터 조회
     </button>
     <child-component
       ref="child_component"
     />
  </div>
</template>
<script>
  import ChildComponent from './ChildComponent5'
  export default {
    components: { ChildComponent },
    computed: {
      msg() {
        return this.$refs.child_component.msg
      }
    },
    methods: {
      checkChild() {
        alert(this.msg)
      }
    },
  }
</script>

 - 부모 컴포넌트에는 computed 옵션을 사용해서, 자식 컴포넌트의 msg 값을 감지하도록 했다.

 - computed는 참조되고 있는 데이터의 변경사항을 바로 감지하여 반영할 수 있다.

Computed 옵션을 이용하면 자식 컴포넌트의 데이터가 변경될 때마다 $emit를 통해 변경된 데이터를 전송하지 않아도 변경된 데이터 값을 항상 확인할 수 있다.
반응형

'JavaScript > Vue.js' 카테고리의 다른 글

컴포넌트 심화 학습 (Provide/Inject, Template refs)  (0) 2021.07.16
컴포넌트 심화 학습 (Slot)  (0) 2021.07.16
Computed와 Watch  (0) 2021.07.15
이벤트 처리 (v-on)  (0) 2021.07.15
랜더링(v-for, v-if, v-show)  (0) 2021.07.15