- CLAUDE.md 운영 규칙 - wiki/ 정리된 지식 페이지 (Nuxt + Claude Code) - raw/ 원본 자료 - reference/ Nuxt 4.x 공식 문서 Co-authored-by: Cursor <cursoragent@cursor.com>
6.1 KiB
6.1 KiB
Nuxt 데이터 패칭
카테고리: 핵심 개념 최종 수정: 2026-05-13 관련: nuxt-lifecycle, nuxt-state-management, nuxt-rendering-modes
요약
$fetch, useFetch, useAsyncData 세 가지가 있다. SSR 환경에서 이중 패칭을 막으려면 useFetch 또는 useAsyncData를 써야 한다. $fetch는 이벤트 핸들러(사용자 액션) 전용.
세 가지 비교
$fetch |
useFetch |
useAsyncData |
|
|---|---|---|---|
| SSR 중복 방지 | ❌ | ✅ | ✅ |
| 캐싱/재사용 | ❌ | ✅ (URL이 key) | ✅ (명시적 key) |
| 반응형 반환값 | ❌ | ✅ | ✅ |
| 사용 위치 | 어디서나 | setup 함수 내부 | setup 함수 내부 |
| 적합한 상황 | 폼 제출, 버튼 클릭 | 일반 데이터 로딩 | CMS/서드파티 쿼리 레이어 |
$fetch
ofetch 기반. 전역 자동 임포트.
// 이벤트 핸들러에서 사용 (올바른 패턴)
async function submitForm() {
const result = await $fetch('/api/submit', {
method: 'POST',
body: { name: '홍길동' },
})
}
⚠️ 주의:
<script setup>의 최상위 레벨에서$fetch를await하면 서버·클라이언트 양쪽에서 각각 실행되어 이중 패칭 발생. 반드시useFetch또는useAsyncData사용.
useFetch
useAsyncData + $fetch의 편의 래퍼. URL 자체가 캐시 key.
<script setup lang="ts">
// 기본 사용
const { data, status, error, refresh, execute, clear } = await useFetch('/api/posts')
// 타입 지정
const { data: posts } = await useFetch<Post[]>('/api/posts')
</script>
주요 반환값
| 값 | 설명 |
|---|---|
data |
응답 데이터 (ref) |
status |
'idle' | 'pending' | 'success' | 'error' |
error |
에러 객체 (ref) |
refresh() / execute() |
수동 재패칭 |
clear() |
data를 undefined로 초기화 |
useAsyncData
CMS, 서드파티 쿼리 레이어, 복수 요청 묶기에 적합. 명시적 key 권장.
<script setup lang="ts">
// 기본
const { data } = await useAsyncData('users', () => myGetFunction('users'))
// 동적 key (라우트 파라미터 활용)
const { id } = useRoute().params
const { data: user } = await useAsyncData(`user:${id}`, () => fetchUser(id))
// 복수 요청 병렬화
const { data } = await useAsyncData('cart', async (nuxtApp, { signal }) => {
const [coupons, offers] = await Promise.all([
$fetch('/cart/coupons', { signal }),
$fetch('/cart/offers', { signal }),
])
return { coupons, offers }
})
⚠️ 주의:
useAsyncData는 데이터 패칭·캐싱 전용. Pinia 액션 호출 같은 사이드 이펙트에 쓰면 null 값으로 반복 실행됨 →callOnce사용.
주요 옵션
lazy — 네비게이션 차단 해제
기본값은 데이터 로딩 완료까지 페이지 전환 차단. lazy: true면 차단 해제, 수동 로딩 상태 처리 필요.
<script setup lang="ts">
const { status, data: posts } = useFetch('/api/posts', { lazy: true })
// 또는 useLazyFetch('/api/posts')
</script>
<template>
<div v-if="status === 'pending'">로딩 중...</div>
<div v-else>{{ posts }}</div>
</template>
server: false — 클라이언트 전용 패칭
// hydration 완료 후에만 패칭
const { status, data } = useFetch('/api/comments', {
lazy: true,
server: false,
})
pick / transform — 페이로드 크기 최소화
// 필요한 필드만
const { data } = await useFetch('/api/mountains/everest', {
pick: ['title', 'description'],
})
// 변환
const { data } = await useFetch('/api/mountains', {
transform: (mountains) => mountains.map(m => ({ title: m.title })),
})
watch — 반응형 재패칭
const id = ref(1)
const { data } = await useFetch('/api/users', { watch: [id] })
// id 변경 시 자동 재패칭
Computed URL — 동적 URL 재패칭
<script setup lang="ts">
const id = ref(null)
// query 파라미터로 동적 URL
const { data } = useLazyFetch('/api/user', {
query: { user_id: id },
})
// URL 자체가 동적인 경우
const { data } = useLazyFetch(() => `/api/users/${id.value}`, {
immediate: false,
})
</script>
immediate: false — 수동 실행
<script setup lang="ts">
const { data, execute, status } = await useLazyFetch('/api/comments', {
immediate: false,
})
</script>
<template>
<button v-if="status === 'idle'" @click="execute">데이터 불러오기</button>
<div v-else-if="status === 'pending'">로딩 중...</div>
<div v-else>{{ data }}</div>
</template>
캐싱과 키 공유
같은 key를 쓰는 컴포넌트는 동일한 data, error, status ref를 공유.
// 같은 key → 동일 인스턴스 (옵션도 일관되어야 함)
const { data: users1 } = useAsyncData('users', fetchUsers, { deep: false })
const { data: users2 } = useAsyncData('users', fetchUsers, { deep: false }) // ✅
// 독립 인스턴스가 필요하면 다른 key 사용
const { data: users1 } = useAsyncData('users-1', fetchUsers)
const { data: users2 } = useAsyncData('users-2', fetchUsers)
반응형 key:
const userId = ref('123')
const { data } = useAsyncData(
computed(() => `user-${userId.value}`),
() => fetchUser(userId.value),
)
// userId 변경 시 자동 재패칭 + 이전 캐시 정리
헤더·쿠키 전달
// useFetch는 서버에서 useRequestFetch()로 클라이언트 헤더·쿠키 자동 프록시
const { data } = await useFetch('/api/echo')
// $fetch 직접 사용 시 수동으로 전달 필요
const headers = useRequestHeaders(['cookie'])
const data = await $fetch('/api/me', { headers })
자주 쓰는 유틸
| 유틸 | 용도 |
|---|---|
refreshNuxtData(key?) |
특정 key 또는 전체 캐시 무효화 후 재패칭 |
clearNuxtData(key?) |
캐시 데이터만 삭제 |
useNuxtData(key) |
캐시된 데이터 읽기 전용 접근 |
참고 / 출처
reference/1.getting-started/10.data-fetching.mdreference/4.api/2.composables/use-fetch.mdreference/3.guide/5.recipes/3.custom-usefetch.md