Next.js - 페이지 캐시 (Server Page Cache, Client Page Cache)

Stella·2026년 1월 5일

Next.js

목록 보기
4/8
  • 한 입 크기로 잘라 먹는 Next.js 책 스터디 내용

6장 페이지 캐시

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

- 서버의 페이지 캐시 (풀 라우트 캐시)

서버와 클라이언트에서 페이지를 각각 캐시한다.

  • 풀 라우트 캐시란?
    next.js 서버에서 사전 렌더링 결과로 생성한 html페이지를 캐시하는 기능이다.
    이 기능을 이용하면 브라우저의 접속 요청에 서버가 페이지를 다시 생성할 필요 없이 캐시 페이지로 바로 응답할 수 있어
    응답 속도가 크게 향상된다.

빌드 타임에 저장, 미리 인덱스 페이지의 생성을 요청, 인덱스 페이지에 필요한 데이터를 페칭한다.
생성이 완료된 페이지를 풀 라우트 캐시에 저장한다.
빌드 종료 후 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;

- 라우트 세그먼트 컨픽(Route Seg-ment Config)

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개의 페이지 생성

클라이언트의 페이지 캐시 - 라우터 캐시

클라이언트 라우터 캐시란?

클라이언트인 브라우저에서 페이지의 데이터를 직접 캐시하는 기능
사용자가 브라우저에서 페이지를 이동 할 때 불필요한 데이터 요청을 방지, 페이지를 빠르게 이동 할 때 불필요한 데이터 요청을 방지해 페이지를 빠르게 이동

  • client router cache의 주요 목적
    브라우저에서 페이지를 이동할 때 두 페이지가 동일한 레이아웃 컴포넌트 사용하는 경우, 중복 요청을 방지
    RSC Payload를 캐시해 보관하는 방법

레이아웃과 페이지 단위로 나누어 저장한다.

1) 레이아웃 컴포넌트 RSC Payload

새로고침이 이루어지기 전까지 계속 재사용 된다. 동일한 레이아웃을 사용하는 페이지를 이동할 때 중복 요청을 방지한다.

2) 페이지 컴포넌트 RSC Payload

뒤로가기, 앞으로 가기 동작에 사용한다. 페이지 이동 과정에서 최신 데이터를 갱신할 때 사용한다.

클라이언트 라우터 캐시 확인하기

{new Date().toLocaleTimeString()}

인덱스 페이지와 검색 페이지 둘 다 같은 시간으로 확인이 된다.
페이지를 이동할 때 레이아웃 컴포넌트의 불필요한 중복 호출을 방지하기 위한 목적으로 사용

프리페칭과 클라이언트 라우터 캐시

프리페칭한 데이터도 저장할 수 있다. 앱 라우터 버전도 페이지 라우터 버전과 마찬가지로 프리페칭 기능을 제공한다.
앱 라우터 버전에서는 JS Bundle, RSC Payload도 함께 프리페칭한다.
클라이언트 라우터 캐시에는 RSC Payload만 저장된다.
= 페이지를 전환할 때 서버 요청을 최소화, 더 빠른 렌더링을 가능하게 한다.

라우트 세그먼트 컨픽 (동적 경로의 URL 파라미터를 제한을 구현)

특정 페이지의 동작을 명시적으로 설정하는 라우트 세그먼트 컨픽(Route Segment Config)기능을 제공한다.

라우트 세그먼트 컨픽이란?

특정 페이지의 캐시 유무나 갱신 등의 동작을 강제로 설정하는 Next.js의 한 기능으로 약속된 이름의 변수를 레이아웃이나 페이지 파일에서 내보내는 방식으로 설정한다.

  • 해당 페이지는 다이나믹 페이지로 강제 설정된다.
export const dynamic = "force-dynamic"
// 다이나믹 페이지로 설정된다.
  • dynamic : 해당 페이지는 다이나믹 페이지로 강제 설정
  • dynamicParams : false로 설정하면 페이지의 URL 파라미터가 동적으로 생성되지 않도록 제한한다.
    해당 페이지를 404에러 페이지로 반환한다.

dynamic 옵션

  1. dynamic="auto"
    기본값, 아무것도 설정하지 않음
    Next.js가 자체적으로 판단한다.

  2. dynamic="force-dynamic"
    페이지를 강제로 다이나믹 페이지로 설정한다.
    동적 API나 캐시되지 않는 데이터 페칭이 없어도, 특정 페이지를 다이나믹 페이지로 설정하고 싶을 때 사ㅛㅇㅇ

  3. dynamic="force-static"
    페이지를 강제로 스태틱 페이지로 설정
    동적 API(쿠키, 헤더, 쿼리 스트링 등)을 사용하면 undefined로 처리된다.

  4. dynamic="error"
    페이지를 스태틱 페이지로 설정
    페이지에서 동적 API를 사용, 캐시되지 않는 데이터 페칭을 사용하면 빌드 에러가 발생

profile
공부 기록

0개의 댓글