Skip to content

데이터 가져오기

라우트가 활성화될 때 서버에서 데이터를 가져와야 하는 경우가 있습니다. 예를 들어 유저 프로필을 렌더링하기 전에 서버에서 유저 데이터를 가져와야 합니다. 이를 달성하는 방법에는 두 가지가 있습니다:

  • 탐색 후 데이터 가져오기: 먼저 탐색을 수행한 다음, 새로 사용되는 컴포넌트의 생명주기 훅에서 데이터를 가져옵니다. 데이터를 가져오는 동안 로딩 상태를 표시합니다.

  • 탐색 전 데이터 가져오기: 탐색 전에 라우트 엔터 가드에서 데이터를 가져오고, 데이터를 가져온 후에 탐색을 수행합니다.

기술적으로 두 방법 모두 유효하며, 궁극적으로는 목표로 하는 사용자 경험(UX)에 따라 달라집니다.

탐색 후 가져오기

이 접근 방식은 즉시 탐색하고 새로 사용되는 컴포넌트를 렌더링하면, 컴포넌트 자체에서 데이터를 가져옵니다. 네트워크를 통해 데이터를 가져오는 동안 로딩 상태를 표시할 필요가 있으며, 각 뷰마다 로딩을 다르게 처리할 수도 있습니다.

route.params.id를 기반으로 게시물 데이터를 가져와야 하는 Post 컴포넌트가 있다고 가정해봅시다:

vue
<template>
  <div class="post">
    <div v-if="loading" class="loading">Loading...</div>

    <div v-if="error" class="error">{{ error }}</div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { getPost } from './api.js'

const route = useRoute()

const loading = ref(false)
const post = ref(null)
const error = ref(null)

// 라우트의 파라미터를 감시하여 데이터를 다시 가져옵니다.
watch(() => route.params.id, fetchData, { immediate: true })

async function fetchData(id) {
  error.value = post.value = null
  loading.value = true
  
  try {
    // `getPost`를 데이터를 가져오는 유틸리티나 API 래퍼로 변경하세요.
    post.value = await getPost(id)  
  } catch (err) {
    error.value = err.toString()
  } finally {
    loading.value = false
  }
}
</script>
vue
<template>
  <div class="post">
    <div v-if="loading" class="loading">Loading...</div>

    <div v-if="error" class="error">{{ error }}</div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>

<script>
import { getPost } from './api.js'

export default {
  data() {
    return {
      loading: false,
      post: null,
      error: null,
    }
  },
  created() {
    // 라우트의 파라미터를 감시하여 데이터를 다시 가져옵니다.
    this.$watch(
      () => this.$route.params.id,
      this.fetchData,
      // fetch the data when the view is created and the data is
      // already being observed
      { immediate: true }
    )
  },
  methods: {
    async fetchData(id) {
      this.error = this.post = null
      this.loading = true

      try {
        // `getPost`를 데이터를 가져오는 유틸리티나 API 래퍼로 변경하세요.
        this.post = await getPost(id)
      } catch (err) {
        this.error = err.toString()
      } finally {
        this.loading = false
      }
    },
  },
}
</script>

탐색 전 가져오기

이 접근 방식은 새로운 라우트로 탐색하기 전에 데이터를 가져옵니다. 새로 사용되는 컴포넌트의 beforeRouteEnter 가드에서 데이터 가져오기를 수행하고, 가져오기가 완료된 후에만 next를 호출할 수 있습니다. next에 전달된 콜백은 컴포넌트가 마운트된 후 호출됩니다:

js
export default {
  data() {
    return {
      post: null,
      error: null,
    }
  },
  async beforeRouteEnter(to, from, next) {
    try {
      const post = await getPost(to.params.id)
      // `setPost`는 아래에 정의된 메서드입니다.
      next(vm => vm.setPost(post))
    } catch (err) {
      // `setError`는 아래에 정의된 메서드입니다.
      next(vm => vm.setError(err))
    }
  },
  // 이 컴포넌트가 이미 렌더링 되어있고 라우트가 변경되는 경우,
  // 로직이 약간 다릅니다.
  beforeRouteUpdate(to, from) {
    this.post = null
    getPost(to.params.id).then(this.setPost).catch(this.setError)
  },
  methods: {
    setPost(post) {
      this.post = post
    },
    setError(err) {
      this.error = err.toString()
    }
  }
}

유저는 새로 사용되는 뷰의 리소스를 가져오는 동안 이전 뷰에 머물게 됩니다. 따라서 데이터를 가져오는 동안 로딩 상태를 표시하는 것이 좋습니다. 데이터 가져오기가 실패한 경우, 전역 경고 메시지를 표시하는 것도 필요합니다.