Vue3로 Suspense 적용하기

Sal Jeong·2023년 5월 17일
0

Vue로 회사 프로젝트를 작업하는 도중,

Vue도 Suspense 기능을 지원하는 것을 보고 적용해 보았음.

Suspense 기능이 친숙한건 아니고, 저번에 react로 적용해 보았을 때 loading 컴포넌트를 분리하기 편리하겠다 생각했기 때문에 이번에도 적용해 보기로 하였다.

https://vuejs.org/guide/built-ins/suspense.html#loading-state

간단하게, 서버에서 유저 리스트를 불러와서 table 형식으로 만든다고 생각해 보자.

suspense는 요청이 완료될 때까지 로딩을 띄워주고, 성공하면 테이블, 실패하면 에러 컴포넌트를 출력해 준다.

// parent.vue

<script setup lang="ts">
import { defineComponent, ref } from 'vue'
import AsyncAdminMemberTable from "@/components/admin/AsyncAdminMemberTable.vue"
import AdminError from '@/components/admin/adminError.vue'
import LoadingSpinner from '@/components/LoadingSpinner.vue'
import { onErrorCaptured } from 'vue' // 요기서 suspense의 에러 여부를 받는다.

const hasError = ref<boolean>(false)

onErrorCaptured((e: Error) => {
  console.error(e)
  hasError.value = true
  return false
})

defineComponent({
  name: 'AdminMain'
})
</script>

<template>
  <div class="flex w-full">
    <AdminError v-if="hasError" /> // error일 경우 Suspense가 아니라 에러를 표시함
    <Suspense v-else> // error가 아니라면, 실제 원하는 컴포넌트를 출력
      <AsyncMemberTable.vue />
      <template #fallback> // 로딩을 위한 컴포넌트
        <LoadingSpinner />
      </template>
    </Suspense>
  </div>
</template>
// AsyncMemberTable.vue
<script setup lang="ts">
import { defineComponent } from 'vue'

function getTestData() {
  return new Promise((resolve) =>
    resolve({
      data: [
        {
          name: 'Bill',
          email: 'billcollins323@hotmail.com',
          profilePic:
            'https://xf-assets.pokecharms.com/data/attachment-files/2015/10/236934_Squritle_Picture.png'
        },
        {
          name: 'Jihyeon',
          email: 'jihyeonjeong@gmail.com',
          profilePic:
            'https://xf-assets.pokecharms.com/data/attachment-files/2015/10/236933_Charmander_Picture.png'
        },
        {
          name: 'pacho',
          email: 'josemunez@naver.com',
          profilePic:
            'https://xf-assets.pokecharms.com/data/attachment-files/2015/10/236932_Bulbasaur_Picture.png'
        }
      ]
    })
  )
}

const getMembersAsAdmin = async function () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      getTestData()
        .then((res: any) => {
          resolve(res)
        })
        .catch((e) => {
          reject(new Error(e))
        })
    }, 1000)
  })
}

const data = (await getMembersAsAdmin()) as any
const members = data.data

defineComponent({
  name: 'AsyncAdminMemberTable'
})
</script>

<template>
  <div class="w-full flex flex-col gap-y-4">

    <div v-for="{profilePic, name, email}, i in members" :key="i" class="flex flex-col gap-y-2 items-center">
      <img alt="randompokemonpic" :src="profilePic" class="w-32 h-32" />
      <span>{{name}}</span>
      <span>{{email}}</span>
    </div>

  </div>
</template>

와 같은 식으로 만들어 볼 수 있었다.

성공한 화면

실패한 화면

이를 통해서 loading을 state나 ref과 같이 컨트롤하지 않아도 된다는 것은 분명 매력적인 부분이라고 생각한다.

profile
행복하기 위해 코딩합니다. JS 합니다.

0개의 댓글