Next.js는 데이터의 캐싱을 내장하고 있으며, 요청 단위로 (권장되는 방식) 또는 전체 라우트 세그먼트에 대해 캐싱을 지원한다.
fetch()
기본적으로 모든 fetch()
요청은 자동으로 캐싱되고 중복 제거된다. 이는 동일한 요청을 두 번 시도하면 두 번째 요청은 첫 번째 요청의 결과를 재사용한다는 것을 의미한다.
async function getComments() {
const res = await fetch('https://...'); // The result is cached
return res.json();
}
// This function is called twice, but the result is only fetched once
const comments = await getComments(); // cache MISS
// The second call could be anywhere in your application
const comments = await getComments(); // cache HIT
👇 다음과 같은 경우에는 요청이 캐시되지 않는다.
동적 메소드(next/headers
, export const POST
또는 유사한)가 사용되고 fetch가 POST
요청인 경우 (또는 Authorization
또는 cookie
헤더를 사용하는 경우)
fetchCache
가 기본적으로 캐시를 건너뛰도록 구성된 경우
개별 fetch
에서 revalidate: 0
또는 cache: 'no-store'
가 구성된 경우
fetch
를 사용하여 수행하는 요청은 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();
// ...
}
React cache()
React는 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);
// ...
}
// app/user/[id]/page.tsx
import { getUser } from '@utils/getUser';
export default async function Page({
params: { id },
}: {
params: { id: string };
}) {
const user = await getUser(id);
// ...
}
위 예시에서 getUser()
함수가 두 번 호출되지만, 데이터베이스에는 한 번의 쿼리만 실행된다. getUser()
함수가 cache()
로 감싸여 있기 때문에 두 번째 요청은 첫 번째 요청의 결과를 재사용할 수 있다.
💡 알아두면 좋은 사항
fetch()
는 요청을 자동으로 캐시하므로 fetch()
를 사용하는 함수를 cache()
로 감싸지 않아도 된다.
이 새로운 모델에서는 여러 컴포넌트에서 동일한 데이터를 요청하더라도 데이터를 props로 컴포넌트 간에 전달하는 대신 필요한 컴포넌트에서 데이터를 직접 가져오는 것을 권장한다.
서버 데이터 패칭 함수가 클라이언트에서 사용되지 않도록 하기 위해 서버 전용 패키지를 사용하는 것을 권장한다.
cache()
fetch
를 사용할 때 POST
요청은 자동으로 중복 제거된다. 단, POST
Route Handler 내부에 있거나 headers()
/cookies()
를 읽은 후에 온 경우에는 중복 제거되지 않는다. 위의 경우에서 GraphQL과 POST
요청을 함께 사용하는 경우, cache
를 사용하여 요청을 중복 제거할 수 있다. cache
인자는 평면적이고 기본형(primitives)만 포함해야 한다. 깊은 객체는 중복 제거를 위해 일치하지 않는다.
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
const res = await fetch('/graphql', { method: 'POST', body: '...' });
// ...
});
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
를 호출함으로써 필요한 데이터를 미리 가져오기를 시작할 수 있다.
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;
}
💡 알아두면 좋은 사항
preload()
함수의 이름은 자유롭게 지정할 수 있다. 이는 패턴이며 API가 아니다.cache
, preload
, and server-only
cache
함수, preload
패턴 및 server-only
패키지를 결합하여 앱 전체에서 사용할 수 있는 데이터 패칭 유틸리티를 만들 수 있다.
// app/getUser.ts
import { cache } from 'react';
import 'server-only';
export const preload = (id: string) => {
void getUser(id);
};
export const getUser = cache(async (id: string) => {
// ...
});
이 접근 방식을 통해 데이터를 미리 가져오고 응답을 캐시하며, 이 데이터 가져오기가 서버에서만 수행되도록 보장할 수 있다.
getUser.ts
의 export는 레이아웃, 페이지 또는 컴포넌트에서 사용하여 사용자 데이터를 언제 가져올지 제어할 수 있다.
캐싱의 세분화와 제어를 향상시키기 위해 요청별 캐싱을 사용하는 것을 권장한다.
세그먼트 수준의 캐싱을 사용하면 경로 세그먼트에서 사용되는 데이터를 캐시하고 재유효화할 수 있다.
이 메커니즘을 통해 경로의 각 page.tsx
와 layout.tsx
에서 라우트의 재유효화 시간을 설정하는 revalidate
값을 내보낼 수 있다. 이를 통해 경로의 다른 세그먼트가 전체 경로의 캐시 유효기간을 제어할 수 있다.
// app/page.tsx
export const revalidate = 60; // revalidate this segment every 60 seconds
💡 알아두면 좋은 사항
만약 페이지, 레이아웃, 그리고 fetch 요청에 모두 revalidate
를 지정한다면, 세 개 중 가장 작은 값이 사용된다.
fetchCache
를 'only-cache'
나 'force-cache'
로 설정하여 모든 fetch 요청이 캐싱을 사용하도록 할 수 있지만, 개별 fetch 요청에 의해 재유효화 빈도가 여전히 낮아질 수 있다.
[출처]
https://nextjs.org/docs/app/building-your-application/data-fetching/caching