Caching Data
Next.js는 개별요청에 관한 caching과 route segment단위의 caching을 지원한다.
(공식문서에서는 개별요청단위로 caching을 다루기를 추천한다.)
Per-request Chching
fetch()
기본적으로 fetch()의 request는 응답데이터가 cache되며, 중복제거가 된다. 즉, fetch로 두번이상의 같은 요청을 할때 두번째이상의 요청에 대한 응답데이터는 첫번째 요청의 응답데이터를 재사용한 데이터이다.
async function getComments() {
const res = await fetch('https://...') //이 요청의 응답데이터틑 자동 caching된다.
return res.json()
}
// 이 함수는 두번 호출되었지만, 첫번째 요청의 응답데이터만 쓰인다.
const comments = await getComments() // cache MISS
// 두번째 요청은 어플리케이션 내 어디서든 가능하다.
const comments = await getComments() // cache HIT
revalidate하기(초단위 옵션 설정)
export default async function Page() {
// revalidate this data every 10 seconds at most
const res = await fetch('https://...', { next: { revalidate: 10 } })
const data = res.json()
// ...
}
캐시되지 않는 경우들
- 다이나믹 메서드(next/headers, esport const POST, or 비슷한 함수들)가 사용되거나 POST request가 사용되는 경우(혹은 Authorization, coockie headers가 사용되는 경우)
- fetchCache는 기본적으로 캐시를 건너뛰도록 구성된다.
- revalidate옵션이 0이거나 cache: 'no-store'옵션인 경우
React cache()
React의 cache()를 사용하는 경우에도 응답데이터가 cache되며, 중복제거가 된다. 즉, 두번이상의 같은 요청을 할때 두번째이상의 요청에 대한 응답데이터는 첫번째 요청의 응답데이터를 재사용한 데이터이다.
// utils/getUser.ts
import { cache } from 'react'
export const getUser = cache(async (id: string) => {
const user = await db.user.findUnique({ id })
return user
})
//app/user/[id]/layout.tsx
import { getUser } from '@utils/getUser'
export default async function UserLayout({ params: { id } }) {
const user = await getUser(id)
// ...
}
POST requests and cache()
POST 요청은 fetch를 사용할 때 자동으로 중복된 요청이 제거된다. 단, POST 경로 핸들러 내부에 있거나 headers()/cookies()를 읽은 후에 오는 요청은 제외이다. For example, if you are using GraphQL and POST requests in the above cases, you can use cache to deduplicate requests. The cache arguments must be flat and only include primitives. Deep objects won't match for deduplication.
import { cache } from 'react'
export const getUser = cache(async (id: string) => {
const res = await fetch('...', { method: 'POST', body: '...' })
// ...
})
Preload pattern with cache()
preload()패턴을 선택적으로 사용하면 좋을 수 있다. (웹 폰트와 같이 우선적으로 다운받아야하는 데이터에 대해)
import { getUser } from '@utils/getUser'
export const preload = (id: string) => {
// void evaluates the given expression and returns undefined
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
void getUser(id)
}
export default async function User({ id }: { id: string }) {
const result = await getUser(id)
// ...
}
preload를 호출함으로써 필요할 가능성이 있는 데이터를 fetching하는 것을 eagerly 시작할 수 있다.
import User, { preload } from '@components/User'
export default async function Page({
params: { id },
}: {
params: { id: string }
}) {
preload(id) // starting loading the user data now
const condition = await fetchCondition()
return condition ? <User id={id} /> : null
}
Good to know:
- preload() 함수는 어떤 이름이어도 상관 없다. API가 아닌 패턴에 지나지 않기 때문이다.
- 이 패턴은 완전히 선택적인 것이고, 경우에 따라 최적화할 수 있는 도구다. 이 패턴은 parallel data fetching 위에서 발전된 최적화 도구다. 이제 여러분은 Promise를 props로 전달할 필요 없이 preload 패턴을 이용할 수 있다(?)
Combining cache, preload and server-only
cache함수와 preload패턴 그리고 server-onlu패키지가 짬뽕된 데이터 fetching 유틸리티를 어플리케이션 내에서 사용하도록 만들어둘 수 있다.
import { cache } from 'react'
import 'server-only'
export const preload = (id: string) => {
void getUser(id)
}
export const getUser = cache(async (id: string) => {
// ...
})
이러한 유틸리티는 캐시될 것이며, 오직 서버에서만 fetch될 것이며 우선적으로 fetch할 것을 보장한다.
레이아웃, 페이지 또는 컴포넌트에서 getUser.ts 내보내기를 사용하여 사용자 데이터를 가져오는 시기를 제어할 수 있다.
Segment-level Chching
Next.js에서는 개별 request단위로 Caching을 조절할 것을 추천한다.
세그먼트 레벨의 캐싱은 cache와 revalidate의 단위를 route segment단위로 조절할 수 있도록 도와준다.
이 메커니즘을 사용하면 경로의 서로 다른 세그먼트가 전체 경로의 캐시 수명을 제어할 수 있다. 경로 계층 구조의 각 page.tsx 및 layout.tsx는 경로의 재검증 시간을 설정하는 재검증 값을 내보낼 수 있다.
export const revalidate = 60 // revalidate this segment every 60 seconds
good to know:
컴포넌트 내부의 페이지, 레이아웃 및 가져오기 요청이 모두 재검증 빈도를 지정하는 경우 세 가지 중 가장 낮은 값이 사용된다.
고급: 모든 가져오기 요청이 캐싱을 선택하도록 하되 개별 가져오기 요청에 따라 재검증 빈도를 낮출 수 있도록 fetchCache를 'only-cache' 또는 'force-cache'로 설정할 수 있다. 자세한 내용은 fetchCache를 참조할 것
'Next.js(ver.13)' 카테고리의 다른 글
Data Fetching_Fetching (0) | 2023.07.10 |
---|---|
Data Fetching_Overview (0) | 2023.06.27 |
fetch API (0) | 2023.06.27 |
Rendering (0) | 2023.06.27 |