앱 라우터 버전에서 페이지를 캐시해 성능을 최적화하고 사용자의 요청에 빠르게 응답하는 방법을 살펴본다.
서버와 클라이언트에서 페이지를 각각 캐시한다.

빌드 타임에 저장, 미리 인덱스 페이지의 생성을 요청, 인덱스 페이지에 필요한 데이터를 페칭한다.
생성이 완료된 페이지를 풀 라우트 캐시에 저장한다.
빌드 종료 후 next.js 서버가 가동되면 브라우저가 next.js 서버에서 인덱스 페이지를 요청한다.
사전 렌더링이 풀 라우트 캐시에 보관되어 있으므로 캐시가 hit 된다. next.js 서버는 페이지를 새로 생성하지 않고 캐시 페이지로 즉시 응답한다. = 응답 속도 빠름
조건 1 : 캐시되지 않는 데이터 페칭을 포함하는 페이지
데이터 캐시 옵션 cache: “no-store” 또는 next: {revalidate: 0}으로 설정된 데이터 페칭을 포함하는 페이지들이다.
요청마다 실시간으로 데이터를 불러오기 때문에 빌드 타임에 미리 캐시할 수 없다.
조건 2 : 동적 API를 사용하는 페이지
브라우저 접속 요청과 함께 전달되는 값인 헤더, 쿠키, 쿼리 스트링을 불러오는 메서드이다.
빌드 타임에 미리 캐시할 수 없다.
// 동적 데이터를 사용하는 컴포넌트가 하나도 없는 static page -> 빌드 타임에 미리 생성 풀 라우트 캐시에 보관
그 페이지는 풀 라우트 캐시에 보관하는 페이지(Static Page)
그렇지 않은 페이지는 (Dynamic Page)라고 한다.

데이터 캐시나 리퀘스트 메모이제이션을 이용하면 빠른 속도로 페이지를 제공한다.
페이지 라우터와 다르게 캐시가 독립적으로 동작한다는 점이 특징이다.
내부의 특정 컴포넌트나 api요청 결과까지 개별적으로 유연하게 최적화할 수 있다.
인덱스 페이지는 static page (루트 레이아웃, 검색 폼, 페이지 컴포넌트) : 동적 api 사용 X
dynamic page(검색, 도서상세) : 동적 api를 사용하기 때문이다.
searchParams를 받고 쿼리 스트링 q에서 값을 꺼내 사용 -> 실시간 처리 동적 api 간주
모든 페이지에서 인덱스 페이지만이 스태틱 페이지로 설정된다. 스태틱 페이지로 설정된 페이지는
빌드 타임에 미리 사전 렌더링을 진행하고, 그 결과 html 페이지를 풀 라우트 캐시에 보관한다.
.next/server/app 폴더의 index.html을 클릭하면 확인할 수 있다.
Cache: “no-store”로 캐시 옵션을 설정한다. (다이나믹 페이지)
next: { revalidate: 3} -> (스태틱 페이지)
풀 라우트 캐시에 보관한 페이지도 자동으로 갱신할 수 있다.
데이터 캐시를 갱신하면 해당 데이터를 사용하는 페이지도 자동으로 갱신된다.
{next: { revalidate: 3}, } 을 사용하여 페이지를 Stale(상한) 상태로 설정하면 된다.
검색 페이지는 동적 API를 사용하므로 다이나믹 페이지로 설정된다.
풀 라우트 캐시는 사용하지 못하더라도 데이터 캐시를 최대한 활용하여 성능을 최적화하는 것이 좋다.
const response = await searchParams;
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/book/search?q=${q}`,
{ cache: "force-cache" } // 영구적으로 캐시
);
}
동적 경로가 있는 도서 상세 페이지를 스태틱 페이지로 설정 풀 라우트 캐시에 보관한다.
Export function generateStaticParams() {
return [{ id: “1” }, { id: “2” }, { id: “3” }]; // 반드시 문자열로 작성!
} // 약속된 이름의 함수를 사용해 URL파라미터 값을 미리 정의하기 위해 사용
이 함수에서 내보낸 URL파라미터와 일치하는 페이지를 Static Page로 생성 -> 풀 라우트 캐시에 보관
(페이지 라우터 버전의 getStaticPaths함수와 유사한 기능을 수행)
build하면 (SSG) Static Site Generation로 뜨는 것을 확인할 수 있다.
풀 라우트 캐시에 보관 next/server/app/book 폴더에 저장된다.
경로 이외의 페이지를 없는 페이지로 취급하려면 dynamicParams라는 지정된 이름의 변수를 사용해야 한다.
export const dynamicParams = false;
dynamicParams : 처럼 페이지 설정을 조절하는 변수
dynamic : 페이지의 유형을 결정
revalidate : 페이지의 갱신 주기를 설정
generateStaticParams 함수는 서버의 사전 렌더링 과정에서 딱 한 번 동작한다.
동적 경로를 사용하는 페이지라도 빌드 타임에 미리 스태틱 페이지로 생성할 수 있다.
export async function generateStaticParams() {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/book`);
if (!response.ok) throw new Error(response.statusText);
const books: BookData[] = await response.json();
return books.map((book) => ({
id: String(book.id),
}));
}
1) /book 주소로 API를 호출, 백엔드 서버에서 도서 데이터를 모두 불러온다.
2) 문제가 있으면 예외가 발생하도록 설정한다.
3) 백엔드 서버에서 받은 응답을 JSON 형태로 변환
4) 각 도서의 id값을 문자열로 변환 -> { id: "도서 아이디" } 형태의 객체 배열 반환
// 추가로 8개의 페이지 생성
클라이언트인 브라우저에서 페이지의 데이터를 직접 캐시하는 기능
사용자가 브라우저에서 페이지를 이동 할 때 불필요한 데이터 요청을 방지, 페이지를 빠르게 이동 할 때 불필요한 데이터 요청을 방지해 페이지를 빠르게 이동

레이아웃과 페이지 단위로 나누어 저장한다.
새로고침이 이루어지기 전까지 계속 재사용 된다. 동일한 레이아웃을 사용하는 페이지를 이동할 때 중복 요청을 방지한다.
뒤로가기, 앞으로 가기 동작에 사용한다. 페이지 이동 과정에서 최신 데이터를 갱신할 때 사용한다.
인덱스 페이지와 검색 페이지 둘 다 같은 시간으로 확인이 된다.
페이지를 이동할 때 레이아웃 컴포넌트의 불필요한 중복 호출을 방지하기 위한 목적으로 사용
프리페칭한 데이터도 저장할 수 있다. 앱 라우터 버전도 페이지 라우터 버전과 마찬가지로 프리페칭 기능을 제공한다.
앱 라우터 버전에서는 JS Bundle, RSC Payload도 함께 프리페칭한다.
클라이언트 라우터 캐시에는 RSC Payload만 저장된다.
= 페이지를 전환할 때 서버 요청을 최소화, 더 빠른 렌더링을 가능하게 한다.
특정 페이지의 동작을 명시적으로 설정하는 라우트 세그먼트 컨픽(Route Segment Config)기능을 제공한다.
특정 페이지의 캐시 유무나 갱신 등의 동작을 강제로 설정하는 Next.js의 한 기능으로 약속된 이름의 변수를 레이아웃이나 페이지 파일에서 내보내는 방식으로 설정한다.
export const dynamic = "force-dynamic"
// 다이나믹 페이지로 설정된다.
dynamic="auto"
기본값, 아무것도 설정하지 않음
Next.js가 자체적으로 판단한다.
dynamic="force-dynamic"
페이지를 강제로 다이나믹 페이지로 설정한다.
동적 API나 캐시되지 않는 데이터 페칭이 없어도, 특정 페이지를 다이나믹 페이지로 설정하고 싶을 때 사ㅛㅇㅇ
dynamic="force-static"
페이지를 강제로 스태틱 페이지로 설정
동적 API(쿠키, 헤더, 쿼리 스트링 등)을 사용하면 undefined로 처리된다.
dynamic="error"
페이지를 스태틱 페이지로 설정
페이지에서 동적 API를 사용, 캐시되지 않는 데이터 페칭을 사용하면 빌드 에러가 발생