Nuxt로 작업할 때, 프론트엔드를 만들면서 외부 API를 호출하게 될 수 있고, 이때 API 호출에 사용할 기본 옵션들을 설정하고 싶을 수 있습니다.
$fetch 유틸리티 함수( useFetch 컴포저블에서 사용됨)는 의도적으로 전역 설정이 불가능하게 되어 있습니다. 이는 애플리케이션 전반의 fetch 동작이 일관되게 유지되도록 하고, 모듈과 같은 다른 통합 기능들이 $fetch 같은 코어 유틸리티의 동작에 의존할 수 있도록 하기 위해 중요합니다.
하지만 Nuxt는 여러분의 API를 위한 커스텀 fetcher(또는 여러 개의 API를 호출해야 한다면 여러 fetcher)를 만들 수 있는 방법을 제공합니다.
$fetchNuxt 플러그인을 사용해 커스텀 $fetch 인스턴스를 만들어 봅시다.
$fetch는 ofetch를 설정한 인스턴스로, Nuxt 서버의 기본 URL을 추가하는 것뿐만 아니라 SSR 중에 직접 함수 호출을 지원합니다(HTTP 왕복을 피함).여기서는 다음과 같이 가정해 봅시다:
401 상태 코드로 응답하면 사용자를 /login 페이지로 리다이렉트한다export default defineNuxtPlugin((nuxtApp) => {
const { session } = useUserSession()
const api = $fetch.create({
baseURL: 'https://api.nuxt.com',
onRequest ({ request, options, error }) {
if (session.value?.token) {
// ofetch >= 1.4.0에 의존한다는 점에 유의하세요 - lockfile을 새로 고쳐야 할 수도 있습니다
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
}
},
async onResponseError ({ response }) {
if (response.status === 401) {
await nuxtApp.runWithContext(() => navigateTo('/login'))
}
},
})
// useNuxtApp().$api로 노출
return {
provide: {
api,
},
}
})
이 Nuxt 플러그인을 사용하면, Vue 컴포넌트에서 직접 API 호출을 하기 위해 useNuxtApp()에서 $api를 사용할 수 있습니다:
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
useAsyncData로 감싸면 서버 사이드 렌더링 시 데이터가 두 번 fetch되는 것을 방지할 수 있습니다(서버 & 클라이언트 hydration 시).useFetch/useAsyncData이제 $api에 원하는 로직이 들어갔으니, useAsyncData + $api 사용을 대체할 useAPI 컴포저블을 만들어 봅시다:
import type { UseFetchOptions } from 'nuxt/app'
export function useAPI<T> (
url: string | (() => string),
options?: UseFetchOptions<T>,
) {
return useFetch(url, {
...options,
$fetch: useNuxtApp().$api as typeof $fetch,
})
}
이제 새 컴포저블을 사용해 깔끔한 컴포넌트를 만들어 봅시다:
<script setup>
const { data: modules } = await useAPI('/modules')
</script>
반환되는 에러의 타입을 커스터마이즈하고 싶다면, 다음과 같이 할 수도 있습니다:
import type { FetchError } from 'ofetch'
import type { UseFetchOptions } from 'nuxt/app'
interface CustomError {
message: string
statusCode: number
}
export function useAPI<T> (
url: string | (() => string),
options?: UseFetchOptions<T>,
) {
return useFetch<T, FetchError<CustomError>>(url, {
...options,
$fetch: useNuxtApp().$api,
})
}
useFetch를 사용하는 방법을 보여주지만, 동일한 구조를 그대로 커스텀 useAsyncData에도 적용할 수 있습니다.