
Next.js는 기본적으로 성능과 비용을 줄이기 위해 cache를 통해 데이터를 저장한다. 즉 라우트는 Statically rendered 된다.
이게 무슨 뜻이냐면, 데이터 요청을 하면 Next.JS는 Re-Rendering 하는 대신, cache에 저장된 데이터를 활용한다. 임의적으로 Opt-out 하지않는 이상 cache를 활용한다는 뜻이다.

위의 그림처럼 라우트가 동적 또는 정적 또는 정적으로 렌더링 되는지를 Caching으로 처리한다. Cache 된 상태이면 그 데이터를 그래로 활용하고 그렇지 않으면 Cache를 적용한다.
클라이언트 측에서 저장되는 Route Cache이다. 레이아웃, 로딩 상태, 페이지와 같은 React Server Component payload를 저장한다.
공식문서에 따르면 Layout과 Loading 상태는 navigation 할 때 마다 Cache되지만 Page의 경우 기본적으로 캐시되지 않지만 브라우저를 뒤로가기, 앞으로 가기 할 경우 Cache 데이터를 이용한다고 한다.
캐시는 브라우저의 임시 메모리에 저장.
캐시는 Session과 Automatic Invalidation Period 두 가지 요소에 의해 유지된다.
Session: cache는 navigation 동안 지속 refresh할 경우 사라짐
Automatic Invalidation Period: layouts과 loading states는 특정 시간 뒤면 invalidates
기본적으로 Prefetched 속성에 따라 얼마나 지속되는지 영향 받음
revalidatePath, revalidateTag 로 캐시 무효화 할 수 있다.
// app/route_cache/a/page.tsx
import { revalidatePath } from "next/cache";
import Link from "next/link";
export default function page() {
revalidatePath("/route_cache/a");
console.log("a page is rendered.");
return (
<>
<h1>a page</h1>
<Link href="/">Home</Link>
</>
);
}
// app/route_cache/b/page.tsx
import Link from "next/link";
export default function page() {
console.log("b page is rendered.");
return (
<>
<h1>b page</h1>
<Link href="/">Home</Link>
</>
);
}
// app/page.tsx
import { revalidatePath } from "next/cache";
import Link from "next/link";
export default function page() {
revalidatePath("/");
return (
<>
<h1>Home Page</h1>
<Link className="border border-gray-500" href="/route_cache/a">
a Page
</Link>
<Link className="border border-gray-500" href="/route_cache/b">
b Page
</Link>
</>
);
}

a와 home에 revalidatePath를 통해 cache를 무효화 시켰더니
정적 라우트에서 동적으로 렌더링 되도록 바뀐 것을 확인 할 수 있다.
실제로 a와 b page에 방문한 경우
렌더링 될 때 메시지를 출력해본 결과
a페이지가 렌더링 된다.

참고로 npm run build 상태여야 revalidate 된다.
앞의 Route Cahche와 달리 Server에서 작동하는 Cache이다.
Cache가 영향을 주는 곳으로 총 4곳이 있다고 한다.
RSC Payload로 렌더링
Client Component Javascript 명령어를 사용하여 HTML 렌더링
-> 브라우저가 업데이트 될 경우 렌더링되는데
이를 캐시에 저장하여 리렌더링 할 필요가 없어진다.

/a 페이지의 Full Route Cache가 없을 경우 원본 데이터를 Fetch 한다. 이후 RSC payload, HTML로 렌더링 하여 Cache로 set.
build 타임 동안 정적으로 렌더링되어 이후는 Re-Rendering 할 필요 없음

fetch API에 자동적으로 같은 url로 여러번 요청할 때 요청을 memoize하여 한 번만 요청하여 실행하도록 함. Next.js 기능이 아닌 React 기능, Fetch 요청의 GET 메소드에서만 Memoization 적용

같은 데이터 요청이 반복되도 성능 하락 없이 memoization을 통해 데이터를 효율적으로 fetch 가능하다.

위의 그림과 같이 같은 url로 fetch 요청을 할 경우 처음 cache가 안된 경우는 miss 상태이다. 그러면 Data를 Cache하고 이후의 요청에서는 Request Memoization이 set 되어 있으므로 url을 요청하지 않고 저장된 데이터를 사용한다.
// src/components/ServerComponents.tsx
"use server";
import { revalidateTag } from "next/cache";
export async function getUsers() {
const res = await fetch("https://66a9d583613eced4eba65dd8.mockapi.io/users", {
method: "GET",
next: {
revalidate: 60, // 60초 이후에 자동으로 revalidate
tags: ["users"], // revalidateTag를 사용하기 위해 tag 지정
},
cache: "force-cache", // 강제로 cache 진행
});
return await res.json();
}
export async function revalidateUsers() {
revalidateTag("users");
}
// src/app/requset_memoization/page.tsx
"use client";
import { getUsers, revalidateUsers } from "@/components/ServerComponents";
import { useState } from "react";
export default function RequestMemo() {
const [users, setUsers] = useState([]);
const onChangeHandler = async () => {
setUsers(await getUsers());
};
const Revalidate = async () => {
await revalidateUsers();
};
console.log(users);
return (
<>
<h1>User length: {users.length}</h1>
<button
className="inline-block border border-slate-500"
onClick={onChangeHandler}
>
Get user length
</button>
<button
className="inline-block border border-slate-500"
onClick={Revalidate}
>
Revalidate
</button>
</>
);
}

데이터를 처음 요청한 이후 Request Memoization으로 인해 서버에 직접 요청하지 않아 시간이 감소한 것을 확인 할 수 있다.

Revalidate하여 데이터를 재요청한 모습
내장된 데이터 캐시는 데이터 fetch 할 때 동안 지속.
Next.js는 native한 fetch API를 확장하여 각각의 요청이 서버에 설정되도록 영구적인 caching 하도록 만들었다.
기본적으로 fetch를 사용하는 데이터 요청은 cache 되지 않으므로 cache와 next.revalidate option을 사용하여야 한다.

'force-cache'를 속성을 가진 fetch 요청이 가면, Data Cache를 확인한다.'no-store')의 경우 항상 data source로 부터 fetch 되어 memoizedRequest와 Deployments에 걸쳐 지속적
사용자가 강제적으로 cache하고 임의적으로 revalidate 하도록 주기나 트리거를 설정할 수 있다.
요청의 lifetime 동안 지속한다. (only lasts the lifetime of a request.)
같은 렌더 경로에서 중복 요청의 수를 줄이기 위함.
원래 데이터 소스로의 요청의 수를 줄임.
시간 간격을 두고 데이터 revalidate
next.revalidate 옵션 사용

revalidatePath, revalidateTag를 이용

Github: https://github.com/foopky/NextJS-Caching-Practice
공식문서: https://nextjs.org/docs/app/building-your-application/caching