Files
gil-wiki/wiki/nuxt-data-fetching.md
gil 5f664546cf feat: 위키 저장소 초기 커밋
- CLAUDE.md 운영 규칙
- wiki/ 정리된 지식 페이지 (Nuxt + Claude Code)
- raw/ 원본 자료
- reference/ Nuxt 4.x 공식 문서

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 00:31:51 +09:00

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>의 최상위 레벨에서 $fetchawait하면 서버·클라이언트 양쪽에서 각각 실행되어 이중 패칭 발생. 반드시 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.md
  • reference/4.api/2.composables/use-fetch.md
  • reference/3.guide/5.recipes/3.custom-usefetch.md