백엔드 다락방
[Vue] - Components Communication(props, emit) 본문
Components Communication(props, emit)
1. Component란?
화면의 영역을 구분하여 개발할 수 있는 뷰의 기능으로 Component를 사용하면 UI를 독립적이고 재사용이 가능하도록 분할하여 각 부분을 개별적으로 다룰 수 있게 됩니다. 때문에 Component 기반으로 화면을 개발하게 되면 코드의 재사용성이 증가하게 됩니다.

1-1. Component 사용
부모 Component에서 자식 Component를 사용할 수 있습니다. 자식 Component를 template에 노출하기 위해서는 components 옵션을 사용하여 컴포넌트를 등록한 후 등록된 키를 사용하여 태그로 사용할 수 있습니다.
// 자식 컴포넌트
export default {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
당신은 {{ count }} 번 클릭했습니다.
</button>`
}
// 부모 컴포넌트
import ButtonCounter from './ButtonCounter.vue'
export default {
components: {
// key: component 에서 key와 component의 명이 같을 ES6 문법으로 단축이 가능합니다.
// 'ButtonCounter': ButtonCounter 와 동일
ButtonCounter
}
}
<template>
<h1>아래에 자식 컴포넌트가 있습니다.</h1>
<ButtonCounter /> <!-- 자식 컴포넌트 사용 -->
</template>
components 옵션에 등록한 컴포넌트들은 원하는 만큼 재사용이 가능하며 사용할 때마다 해당 컴포넌트의 새로운 인스턴스가 생성됩니다.
<h1부모 컴포넌트에 많은 자식 컴포넌트를 등록할 수 있습니다!</h1>
<!-- ButtonCounter 컴포넌트의 count는 독립적입니다. -->
<button-counter />
<button-counter />
<button-counter />
또한 v-for 를 사용하여 각각을 자식 Component로 렌더링 하는 경우 다음과 같이 사용할 수 있습니다.
<blog-post
v-for="post in posts"
:key="post.id"
:title="post.title"
/>
2. Components Communication - 컴포넌트 통신
컴포넌트 통신을 위해서는 다음과 같은 규칙을 따라야 합니다.
- 상위 컴포넌트 → 하위 컴포넌트 : props (데이터 전달)
- 하위 컴포넌트 → 상위 컴포넌트 : event (이벤트 전달)
위와 같이 규칙을 따르지 않고 컴포넌트 통신을 하게 되면 컴포넌트 간 N방향 통신이 발생하게 되어 데이터 추적이 어려워지게 되기 때문에 규칙을 따르는 것을 추천합니다.

3. Props
3-1. Props를 이용한 정적 데이터 전달
부모 Component에서 자식 Component에 props 정적 데이터의 전달은 다음과 같습니다.
// 자식 컴포넌트
export default {
props: {
greetingMessage: String
}
}
<!-- 부모 컴포넌트 -->
<my-component 하위 컴포넌트의 프롭스 속성 이름 = "상위 컴포넌트의 데이터 이름 or 값"/>
<my-component greetingMessage="안녕!" />
3-2. Props를 이용한 동적 데이터 전달
상위 Component에서 하위 Component에 props 동적 데이터의 전달은 다음과 같습니다.
<!-- 상위 컴포넌트 -->
<app-header v-bind:하위 컴포넌트의 프롭스 속성 이름 = "상위 컴포넌트의 데이터 이름"></app-header>
<!-- ex -->
<app-header v-bind:itemList = "todoItemList"/>
<!-- 참고로 v-bind는 생략이 가능하다. -->
<app-header :itemList = "todoItemList"/>
3-3. Props 단방향 데이터 흐름
props는 자식 속성과 부모 속성 사이에 하향식 단방향 바인딩으로 통신해야 합니다. 부모 속성이 업데이트가 되면 자식으로 전파가 되어 업데이트를 합니다. 하지만 그 반대의 경우(자식→부모)는 자식 컴포넌트가 실수로 부모의 상태를 변경하여 앱의 데이터 흐름을 이해하기 어렵게 만들 수 있기 때문에 자식 컴포넌트 내부에서 props를 변경하지 말아야 합니다.
export default {
props: {
foo,
},
created() {
// 자식 컴포넌트에서 props를 변경할 경우 콘손에 다음과 같이 경고 메시지가 나옵니다.
// ❌ warning, props are readonly!
// (경고, props는 읽기 전용입니다!)
this.foo = 'bar'
}
}
부모 Component에 영향을 주지 않고 props를 변경하고자 하는 경우에는 다음과 같은 두 가지 방법을 사용할 수 있습니다.
- prop가 초기 값을 전달하기 위해 사용하고, 자식 컴포넌트는 props를 로컬 데이터 속성으로 사용하고자 하는 경우
export default {
props: {
initialCounter,
data() {
return {
// props.initialCounter는 counter의 초기 값으로 사용됩니다.
// 추후 props가 갱신되어도 counter 값이 업데이트 되지 않습니다.
counter: this.initialCounter
}
}
}
- prop가 변환이 필요한 경우 prop의 값을 사용하여 계산된 속성을 사용하고자 하는 경우
export default {
props: {
size,
},
computed: {
// prop이 변경될 때, 계산된 속성은 자동으로 업데이트 됩니다.
normalizedSize() {
return this.size.trim().toLowerCase()
}
}
}
3-4. Prop 유효성 검사
컴포넌트는 props에 타입과 유효성 관련 요구 사항을 지정할 수 있으며 지정한 요구 사항이 충족되지 않으면 Vue는 콘솔에서 경고 메시지가 출력됩니다.
required: true가 지정되지 않은 모든 prop는 기본적으로 optional 입니다.- prop의 타입이
Boolean이 아니고 선택사항인 경우, 누락되면undefined값을 가집니다. - prop의 타입이
Boolean이고 누락된 경우,false가 기본값이 됩니다. - prop가 누락되었거나 명시적으로 선언된 값이
undefined이고default값이 정의되어 있다면default값이 사용됩니다.
props는 컴포넌트 인스턴스가 생성되기 이전에 유효성 검사를 실행하기 때문에default또는 validator 함수 내에서 인스턴스 속성(ex -data,computed등)을 사용할 수 없습니다.
export default {
props: {
// 기본 타입 체크
// (`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 |
| String |
| Number |
| Array |
| Object |
| Date |
| Function |
| Symbol |
4. $emit
4-1. 이벤트 emit
컴포넌트는 내장 메서드인 $emit 을 사용하여 템플릿 표현식(예: v-on 핸들러에서)에서 직접 사용자 정의 이벤트를 발신할 수 있다.
// 하위 컴포넌트
<template>
<button v-on:click="passEvent">Click</button>
<!-- 템플릿 표현식에서 직접 사용자 정의 이벤트를 발신할 수 있습니다. -->
<!-- <button v-on:click="$emit('pass')">Click</button>
</template>
export default {
data: function() {
return {
newTodoItem: ''
}
},
methods: {
passEvent: function() {
this.$emit('pass');
}}
}
}
v-on:click="passEvent"- click 이벤트 발생 시 해당 컴포넌트의 passEvent 메서드 호출this.$emit('pass')- 발생한 이벤트를 상위 컴포넌트에 전달
// 상위 컴포넌트
<template>
<div id="app">
<!-- <Emit v-on:하위 컴포넌트에서 발생한 이벤트명 = "상위 컴포넌트의 메서드명"></Emit> -->
<!-- v-on은 @로 축약 가능 -->
<Emit @pass="logText"></Emit>
</div>
</template>
<script>
import Emit from './components/emit.vue'
export default {
components: {
Emit
},
methods: {
logText() {
console.log('logText 호출');
}
},
}
</script>
v-on:pass="logText"- 하위 컴포넌트에서 전달받은 이벤트로 해당 컴포넌트의 logText 메서드 호출
4-2. 이벤트 인자
$emit 을 이용하여 이벤트와 함께 특정 값을 인자로 전달할 수 있습니다.
<!-- 자식 컴포넌트 -->
<button @click="$emit('increaseBy', 1)">
Increase by 1
</button>
<!-- 부모 컴포넌트 -->
<my-button @increaseBy="(n) => count += n" />
<my-button @increaseBy="increaseCount" />
// 부모 컴포넌트
methods: {
increaseCount(n) {
this.count += n
}
}
4-3. 이벤트 유효성 검사
props 타입 유효성 검사와 유사하게 발신되는 이벤트는 배열 대신 객체 구문으로 정의된 경우 유효성을 검사할 수 있습니다. 유효성 검사를 추가하기 위해 이벤트에는 this.$emit 호출 시 전달되는 인자를 수신하고, 이벤트가 유효한지 여부를 나타내는 불리언 값을 반환하는 함수가 할당됩니다.
export default {
emits: {
// click 이벤트 유효성 검사 없음
click: null,
// submit 이벤트 유효성 검사
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('submit 이벤트 페이로드가 옳지 않음!')
return false
}
}
},
methods: {
submitForm(email, password) {
this.$emit('submit', { email, password })
}
}
}'Front-End > Vue.js' 카테고리의 다른 글
| [Vue] - 템플릿 참조 (0) | 2023.02.12 |
|---|---|
| [Vue] - 감시자(watcher) (0) | 2023.02.12 |
| [Vue] - 컴포넌트 생명주기(created, mounted, updated) (0) | 2023.02.12 |
| Vue - Form 입력 바인딩 (0) | 2023.02.03 |
| Vue - 조건부, 리스트 렌더링과 이벤트 핸들링 (0) | 2023.01.29 |