useFetch·useAsyncData·$fetch의 차이와 옵션, 에러·로딩 처리.
데이터 패칭
서버에서 데이터를 받아오는 일은 거의 모든 앱의 핵심입니다. Nuxt는 SSR과 클라이언트를 모두 고려한 데이터 패칭 도구를 제공합니다. 이번 레슨에서는 useFetch, useAsyncData, $fetch 의 차이와 주요 옵션, 그리고 로딩·에러 상태 처리를 다룹니다.
학습 목표
useFetch,useAsyncData,$fetch를 언제 쓰는지 구분한다.lazy,server,key같은 옵션의 의미를 이해한다.- 데이터를 다시 불러오는 방법을 안다.
pending,error로 로딩·에러 상태를 처리한다.
세 가지 도구 비교
| 도구 | 용도 | SSR 중복 방지 |
|---|---|---|
useFetch | 컴포넌트에서 URL로 데이터 받기 | O |
useAsyncData | 임의의 비동기 로직 결과를 받기 | O |
$fetch | 이벤트 핸들러 등에서 단발 요청 | X |
핵심은 useFetch / useAsyncData 는 SSR에서 받은 데이터를 클라이언트로 전달 해 재요청을 막는다는 점입니다. $fetch 는 그런 처리가 없는 순수 요청 함수입니다.
useFetch
가장 자주 쓰는 도구입니다. URL을 넘기면 데이터·로딩·에러를 한 번에 돌려줍니다.
<script setup>
const { data, pending, error, refresh } = await useFetch('/api/tasks');
</script>
<template>
<p v-if="pending">불러오는 중...</p>
<p v-else-if="error">에러: {{ error.message }}</p>
<ul v-else>
<li v-for="t in data" :key="t.id">{{ t.title }}</li>
</ul>
</template>
useAsyncData
URL이 아니라 임의의 비동기 함수 결과를 SSR 친화적으로 다루고 싶을 때 씁니다. 첫 인자는 캐시 키, 둘째는 함수입니다.
const { data } = await useAsyncData('tasks', () => $fetch('/api/tasks'));
// 여러 호출을 조합할 수도 있다
const { data: dashboard } = await useAsyncData('dashboard', async () => {
const [tasks, users] = await Promise.all([
$fetch('/api/tasks'),
$fetch('/api/users'),
]);
return { tasks, users };
});
💡 TIP —
useFetch(url)는 사실상useAsyncData(key, () => $fetch(url))의 편의 버전입니다. URL만 있으면useFetch, 로직이 복잡하면useAsyncData를 고르세요.
$fetch
버튼 클릭, 폼 전송처럼 사용자 동작에 반응하는 단발 요청 에는 $fetch 를 씁니다. 컴포넌트 최상단이 아니라 핸들러 안에서 호출합니다.
async function addTask(title: string) {
await $fetch('/api/tasks', {
method: 'POST',
body: { title },
});
}
⚠️ 주의 — 컴포넌트 셋업 단계의 데이터 로딩에
$fetch만 쓰면 SSR과 클라이언트에서 두 번 요청 될 수 있습니다. 이럴 때는useFetch/useAsyncData를 쓰세요.
주요 옵션
const { data, status } = await useFetch('/api/tasks', {
lazy: true, // 네비게이션을 막지 않고 백그라운드로 로딩
server: false, // 서버에서 받지 않고 클라이언트에서만 요청
key: 'tasks', // 캐시 키를 직접 지정
query: { page: 1 },// 쿼리스트링
default: () => [], // 로딩 전 기본값
});
| 옵션 | 효과 |
|---|---|
lazy | 페이지 전환을 막지 않고 비동기로 로딩 (스켈레톤 UI에 적합) |
server | false 면 SSR을 건너뛰고 클라이언트에서만 요청 |
key | 캐시 식별자. 같은 키는 데이터를 공유 |
query / params | 쿼리스트링·경로 파라미터 |
default | 데이터가 도착하기 전의 기본값 |
리패칭 (다시 불러오기)
refresh() 를 호출하면 같은 요청을 다시 보냅니다. 반응형 파라미터를 watch 로 묶어 자동 갱신할 수도 있습니다.
<script setup>
const page = ref(1);
// page 가 바뀌면 자동으로 다시 요청
const { data, refresh } = await useFetch('/api/tasks', {
query: { page },
watch: [page],
});
</script>
<template>
<button @click="refresh()">새로고침</button>
<button @click="page++">다음 페이지</button>
</template>
에러와 로딩 처리
useFetch 가 돌려주는 pending(또는 status)과 error 로 화면 상태를 분기합니다.
<script setup>
const { data, status, error } = await useFetch('/api/tasks');
// status: 'idle' | 'pending' | 'success' | 'error'
</script>
<template>
<Loading v-if="status === 'pending'" />
<ErrorBox v-else-if="error" :message="error.message" />
<TaskList v-else :items="data" />
</template>
요약
useFetch/useAsyncData는 SSR 데이터를 클라이언트로 전달해 중복 요청을 막습니다.- URL만 있으면
useFetch, 복잡한 로직이면useAsyncData를 씁니다. - 사용자 동작에 반응하는 단발 요청은
$fetch가 적합합니다. lazy,server,key,watch옵션으로 동작을 세밀하게 제어합니다.
연습문제
useFetch('/api/tasks')로 일감 목록을 받아pending·error·정상 상태를 모두 화면에 표현해 보세요.pageref와watch옵션을 묶어, 페이지 버튼을 누르면 데이터가 자동 갱신되게 만들어 보세요.- 폼 제출 시
$fetch로 새 일감을 POST하고, 성공하면refresh()로 목록을 갱신해 보세요. lazy: true와server: false를 적용했을 때 화면 동작이 어떻게 달라지는지 비교해 보세요.
힌트 — 셋업 단계 로딩은
useFetch, 핸들러 안 요청은$fetch라는 규칙을 떠올리세요.
💡 연습문제 풀이
불러오는 중…
댓글 0
“Nuxt.js” 강좌에 대한 댓글입니다.