앱 라우터의 데이터 호출 방법
앱 라우터에서는 fetch API로 데이터를 호출합니다.
fetch() API 소개
fetch API는 브라우저에서 기본으로 제공되는 데이터 호출 API입니다. 넥스트에서는 이 fetch API를 확장하고 사용자들이 편하게 사용할 수 있도록 안내합니다.
fetch 관련해서 넥스트가 확장한 기능은 크게 caching, revalidating, memoize 입니다.
fetch() API 기본 문법
fetch API는 서버 컴포넌트에서 async, await 문법과 함께 사용할 수 있습니다.
// app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/...')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
async function Page() {
const data = await getData()
return <div></div>
}
export default Page;
fetch() API 장점
fetch API는 중복된 요청에 대해서 메모이제이션(Memoization)이라고 하는 기술을 적용합니다. 메모이제이션을 활용하면 같은 요청을 여러번 보내지 않죠. 이러한 특징 덕택에 서버 데이터를 한 컴포넌트에서 받아 자식 컴포넌트에 계속 프롭스로 내려주던 번거로운 작업을 하지 않아도 됩니다. 데이터 필요한 컴포넌트에서 같은 요청을 호출하면 되는거죠.
fetch() API 캐싱
fetch를 사용할 때 해당 요청을 캐싱하고 싶다면 다음과 같이 옵션 값을 추가합니다.
fetch('https://...', {
cache: 'force-cache'
})
fetch 요청의 기본 값은 force-cache
이기 때문에 따로 추가할 필요는 없습니다. 만약 캐싱을 하고 싶지 않다면 no-store
옵션을 사용합니다. 자세한 캐시 옵션은 다음 링크를 참고하세요.
참고로, fetch가 서버 액션이나 라우트 핸들러의 POST 요청으로 사용될 때는 캐싱이 되지 않습니다.
fetch() API 데이터 최신성(revalidation)
데이터 최신성은 data revalidation을 의미합니다. 데이터 최신성은 캐싱이 있더라도 얼마나 자주 최신 데이터를 호출할 것이냐를 의미합니다. 이렇게 기존 캐싱을 버리고 새로운 데이터를 요청할 때 특정 주기별 또는 특정 이벤트 별로 갱신할 수 있습니다.
주기별 갱신
먼저 특정 주기(시간)별로 데이터를 갱신하는 코드는 다음과 같습니다.
fetch('https://...', {
next: {
// 3600초
revalidate: 3600
}
})
위 코드는 3600초(1시간) 마다 캐싱이 있더라도 새로운 데이터를 요청하는 코드입니다. 이렇게 fetch API에 옵션을 넣는 방법 말고도 아래와 같이 페이지 컴포넌트, 레이아웃 컴포넌트 레벨에서 일어나는 모든 fetch 요청에 대해서 revalidate 옵션을 추가할 수 있습니다.
// layout.js 또는 page.js
export const revalidate = 3600 // 3600초(1시간) 마다 최신 데이터 요청
이벤트별 갱신
이벤트별 갱신은 크게 path나 tag 기반으로 가능합니다. tag 기반은 다음과 같이 revalidateTag API를 호출하여 갱신할 수 있습니다.
// app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
revalidateTag('collection')
}
이 서버 액션을 실행하게 되면 아래와 같이 fetch API에 같은 tags
값이 지정된 요청에 대해 모두 최신 데이터를 요청하게 됩니다.
// app/page.tsx
export default async function Page() {
const res = await fetch('https://...', {
next: {
tags: ['collection']
}
})
}
revalidateTag('collection')
을 실행했기 때문에 collection
이라는 태그로 지정된 fetch API가 모두 최신 데이터를 요청합니다.
에러 처리
만약 revalidate 옵션으로 최신 데이터를 호출하다가 실패하게 되면 상대적으로 최근에 생성되어 있던 데이터를 반환해 줍니다. 그리고 나서 다음 요청을 할 때 다시 최신 데이터를 요청합니다.
참고 사항
앞에서 살펴봤듯이 revalidate 옵션은 fetch API 별 또는 page, layout 파일 전체에 설정이 가능 합니다. 넥스트에서는 각 fetch API 별로 revalidate 옵션을 설정하기를 권장합니다. 이렇게 해야 좀 더 캐싱 동작을 세밀하게 조정할 수 있기 때문입니다.
fetch가 아닌 외부 라이브러리를 사용하는 경우
넥스트의 fetch API가 아니라 다른 데이터 호출 라이브러리(Tanstack Query, SWR, axios 등)을 사용하는 경우에는 cache 속성을 이용하여 캐싱할 수 있습니다.
// app/utils.ts
import { cache } from 'react'
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id })
return item
})
// app/item/[id]/layout.tsx
import { getItem } from '@/utils/get-item'
export const revalidate = 3600 // revalidate the data at most every hour
export default async function Layout({
params: { id },
}: {
params: { id: string }
}) {
const item = await getItem(id)
// ...
}
다만, cache
API는 현재 실험 단계의 API로 실무에서 사용을 권장하지 않습니다.