2023년 3월 8일

코딩 - Vue 3 + Typescript + Vite + Vuetify 3.x 로 시작하는 웹 프로그램 : 사용자 정의 콤포넌트

컴포넌트를 사용하면 UI를 독립적이고 재사용 가능한 조각으로 분할하고 각 조각을 독립적으로 생각할 수 있다. 일반적으로 앱은 중첩된 컴포넌트의 트리로 구성된다.

사용자 정의 콤포넌트를 작성할떄 가장 고민되는 것이 부모 콤포넌트와 자식 콤포넌트 사이에 정보 교환인데 보통 아래 규칙을 따른다.

❶ 부모 > 컴포넌트 : Props 를 통하여 전달한다.
❷ 컴포넌트 > 부모 : 이벤트를 발생 시켜 데이터를 전달한다.
❸ 전역 : 전체 프로그램에서 공유가 필요한 데이터의 경우는 pinia Store 를 사용한다.

다음 예시는 이미지 목록을 사용자 정의 컴포넌트 사용하여 구현한 예이다.
  • /views/Photos.vue : 이미지 목록.
  • /components/PhotoCard.vue : 목록에 보여지는 사용자 정의 컴포넌트.
  • /store/photos.store.ts : 이미지 데이터를 위한 Store.

❶ Props 

 v-bind  를 사용해서 부모의 값을 props 에 바인딩 할 수 있다. 부모의 값이 변경되면 동적으로 하위(자식 컴포넌트)  값도 변경된다.  
 
<PhotoCard v-bind:image="image" @imageEditEvent="edit" @imageViewEvent="view"></PhotoCard>

 v-bind  를 단축하여 아래와 같이 사용할 수 도 있다.

<PhotoCard :image="image" @imageEditEvent="edit" @imageViewEvent="view"></PhotoCard>

props 는 단반향 바인딩 만을 지원하는데, 이는 자식 컴포넌트가 실수로 부모 콤포넌트의 값을 변경하는 것을 막기 위함이다.  props 값을 변경해야 할 필요가 있는 경우 ⑴ 자식 콤포넌트는 초기 값으로 만 사용하고 로컬 값을 생성하여 사용하거나 ⑵ prop 값으로 부터 계산되는 값을 사용하는 것이다. 

⑴ prop 값으로 로컬 값을 초기화하여 사용

const props = defineProps({
image: Object,
})
const localImage = ref(props.image)

⑵ prop 값으로 부터 계산되는 값을 사용

const props = defineProps({ attachment: Object })
const emit = defineEmits(['previewEvent'])
const thumbnailUrl = computed(() => {
return attachments.isAudio(props.attachment) ? attachments.thumbnails.AUDIO :
attachments.getAttachmentUrl(props.attachment,
{ thumbnail: true, width: 300, height: 300 });
})
 
 

❷ 커스텀 이벤트 

커스텀 이벤트를 사용하면 자식 컴포넌트에서 부모에게 값을 전달 할 수 있다. 

자식 컴포넌트 
<template>
<v-btn size="small" color="white" variant="text" icon="mdi-eye"
@click="emit('imageViewEvent', image)"></v-btn>
</template>
<script setup lang="ts">
defineProps({
image: Object,
})
const emit = defineEmits(['imageEditEvent', 'imageViewEvent'])
</script>

부모 컴포넌트
<PhotoCard :image="image" @imageEditEvent="edit" @imageViewEvent="view"></PhotoCard>

⓷ Pinia Store 

Vuex 와 다르게 Pinia 를 라이브러리를 사용하면 아주 쉽게 저장소를 생성하고 전역으로 공유할 수 있다. 
이미지 목록을 가져오는 Photos.vue 컴포넌트에서 fetch 함수를 사용하여 데이터를 가져온다. Store 의 상태는 전역으로 공유가 되기 때문에 다른 뷰로 이동하고 다시 돌아오는 경우에도 Store 가 가지고 있는 정보는 유지되어 사용할 수 있다. 

onMounted 함수 내에서 store에서 정보를 가져오는 코드를 포함하여 구현하는 경우 불필요한 서버 통신을 최소화할 수 있어 편리하게 사용할 수 있다.

import { usePhotosStore } from '@/store/photos.store'
const photos = usePhotosStore()
const images = ref([])
onMounted(async () => {
if (!photos.isLoaded) {
overlay.value = true
photos.setPage(1)
await photos.fetch()
overlay.value = false
}
images.value = photos.photos
})

참고자료

  1. How to use Watch in Vue 3 in Composition API
  2. https://vuejs.org/guide/essentials/watchers.html#basic-example
  3. https://www.zhenghao.io/posts/verify-image-url
  4. 12 VueJS Best Practices for Pro Developers
  5. How To Pass Data Between Components In Vue.js

댓글 없음:

댓글 쓰기