본문 바로가기

Frontend/Vue3

[Vue3] 컴포넌트 간 통신 (props, emit)

728x90
반응형

동일한 사진 데이터가 한 화면에 다양한 위치에서 여러 번 출력될 수 있다.

하지만 해당 페이지를 구성하는 컴포넌트가 여러 개라면 각 컴포넌트가 개별적으로 동일한 데이터를 관리해야 할까?

그렇다면 사진을 변경해야 할 때 모든 컴포넌트에 대해 변경 요청을 해야한다.

=> "공통된 부모 컴포넌트에서 관리하자"

 

부모는 자식에게 데이터를 전달(pass props)하며, 자식은 자신에게 일어난 일을 부모에게 알림(emit events)


Props

- 부모 컴포넌트로부터 자식 컴포넌트로 데이터를 전달하는데 사용되는 속성

- 모든 props는 자식 속성과 부모 속성 사이에 하향식 단방향 바인딩(one-way-down binding) 형성

  • 부모 속성이 업데이트되면 자식으로 흐르지만 그 반대는 안됨
  • 자식 컴포넌트 내부에서 props를 변경하려고 시도해서는 안 되며 불가능
  • 부모 컴포넌트가 업데이트될 때마다 자식 컴포넌트의 모든 props가 최신 값으로 업데이트됨
  • 단방향인 이유: 하위 컴포넌트가 실수로 상위 컴포넌트의 상태를 변경하여 데이터 흐름을 이해하기 어렵게 만드는 것 방지

예시를 위해 먼저 App > Parent > ParentChild 컴포넌트 관계를 작성한다.

<!-- App.vue -->
<script setup>
import Parent from '@/components/Parent.vue'
</script>

<template>
  <div>
    <Parent/>
  </div>
</template>

 

<!-- Parent.vue -->
<script setup>
import ParentChild from '@/components/ParentChild.vue'
</script>

<template>
    <div>
        <ParentChild/>
    </div>
</template>

 

<!-- ParentChild.vue -->
<script setup>
</script>

<template>
    <div>
    </div>
</template>

Props 작성

부모 컴포넌트 Parent에서 자식 컴포넌트 ParentChild에 보낼 props를 작성한다.

<!-- Parent.vue -->
<script setup>
import ParentChild from '@/components/ParentChild.vue'
</script>

<template>
    <div>
        <ParentChild my-msg="message"/>
    </div>
</template>

이 때 prop 이름은 my-msg, prop 값은 messsage이다.

Props 선언

부모 컴포넌트에서 보낸 props를 사용하기 위해서는 자식 컴포넌트에서 명시적인 props 선언이 필요하다.

Props 선언에는 2가지 방식이 있다.

 

1. 문자열 배열을 사용한 선언

- defineProps()를 사용하여 props를 선언

<!-- ParentChild.vue -->
<script setup>
defineProps(['myMsg'])
</script>

 

2. 객체를 사용한 선언 (사용권장)

- 각 객체 속성의 키는 props의 이름이 되며, 객체 속성의 값은 값이 될 데이터의 타입에 해당하는 생성자 함수(Number, String, ...)여야 함

<!-- ParentChild.vue -->
<script setup>
defineProps({
    myMsg: String
})
</script>

Prop 데이터 사용

- 템플릿에서 반응형 변수와 같은 방식으로 활용한다.

<!-- ParentChild.vue -->
<template>
    <div>
        <p>{{ myMsg }}</p>
    </div>
</template>

 

- props를 객체로 반환하므로 필요한 경우 Javascript에서 접근 가능하다.

<!-- ParentChild.vue -->
<script setup>
const props = defineProps({
    myMsg: String
})
console.log(props)
</script>

Props 세부사항

1. Props Name Casing (Props 이름 컨벤션)

- 선언 및 템플릿 참조 시: camelCase

<p>{{ myMsg }}</p>
defineProps({
    myMsg: String,
})

 

- 자식 컴포넌트로 전달 시: kebab-case (HTML 속성 표기법과 동일)

<ParentChild my-msg="message"/>

 

2. Static Props & Dynamic Props

- 지금까지 작성한 것은 정적 props이다!

- v-bind를 사용하여 동적으로 할당된 props를 사용할 수 있다.

 

1) Dynamic props 정의

<!-- Parent.vue -->
<script setup>
import { ref } from 'vue'
const name = ref('Alice')
</script>

<template>
    <div>
        <ParentChild my-msg="message" :dynamic-props="name"/>
    </div>
</template>

 

2) Dynamic props 선언 및 출력

<!-- ParentChild.vue -->
<script setup>
defineProps({
    myMsg: String,
    dynamicProps: String
})
</script>

<template>
    <div>
        <p>{{ dynamicProps }}</p>
    </div>
</template>

Emit

$emit() : 자식 컴포넌트가 이벤트를 발생시켜 부모 컴포넌트로 데이터를 전달하는 역할의 메서드

'$' 표기는 Vue 인스턴스나 컴포넌트 내에서 제공되는 전역 속성이나 메서드를 식별하기 위한 접두어!

emit 메서드 구조

$emit(event, ...args)

- event: 커스텀 이벤트 이름

- args: 추가 인자

이벤트 발신 및 수신

- ParentChild에서 $emit을 사용하여 someEvent라는 이름의 사용자 정의 이벤트를 발신

<!-- ParentChild.vue -->
<template>
    <div>
        <button @click="$emit('someEvent')">클릭</button>
    </div>
</template>

 

- Parent는 v-on을 사용하여 발신된 이벤트 수신

<!-- Parent.vue -->
<script setup>
import ParentChild from '@/components/ParentChild.vue'

const someCallback = function () {
    console.log('ParentChild가 발생한 이벤트를 수신했어요.')
}
</script>

<template>
    <div>
        <ParentChild @some-event="someCallback"/>
    </div>
</template>

emit 이벤트 선언

- defineEmits()를 사용하여 명시적으로 발신할 이벤트를 선언할 수 있다.

- script에서 $emit 메서드에 접근할 수 없기 때문에 defineEmits()는 $emit 대신 사용할 수 있는 동등한 함수를 반환한다.

<!-- ParentChild.vue -->
<script setup>
const emit = defineEmits(['someEvent'])
const buttonClick = function () {
    emit('someEvent')
}
</script>

<template>
    <div>
        <button @click="buttonClick">클릭</button>
    </div>
</template>

이벤트 인자

- 이벤트 발신 시 추가 인자를 전달하여 값을 제공할 수 있다.

<!-- ParentChild.vue -->
<script setup>
const emit = defineEmits(['emitArgs'])

const emitArgs = function () {
    emit('emitArgs', 1, 2, 3)
}
</script>

<template>
    <div>
        <button @click="emitArgs">추가 인자 전달</button>
    </div>
</template>
<!-- Parent.vue -->
<script setup>
import ParentChild from '@/components/ParentChild.vue'

const getNumbers = function (...args) {
    console.log(args)
    console.log(`ParentChild가 전달한 추가인자 ${args}를 수신했어요.`)
}
</script>

<template>
    <div>
        <ParentChild @emit-args="getNumbers"/>
    </div>
</template>

Event 세부사항

Event Name Casing

- 선언 및 발신 시: camelCase

<button @click="$emit('someEvent')">클릭</button>
const emit = defineEmits(['someEvent'])
emit('someEvent')

 

- 부모 컴포넌트에서 수신 시: kebab-case

<ParentChild @some-event="..."/>
728x90
반응형

'Frontend > Vue3' 카테고리의 다른 글

[Vue3] 상태 관리 라이브러리 Pinia  (0) 2024.05.14
[Vue3] Vue Router로 페이지 이동하기  (0) 2024.05.08
[Vue3] Lifecycle Hooks  (0) 2024.05.06
[Vue3] watch  (0) 2024.05.06
[Vue3] 리스트 렌더링 (v-for)  (0) 2024.05.06