Next.js에서는 Cache를 통해 성능향상과 비용 절감을 가능하게 합니다. Next.js에서는 Cache는 Request Memoization, Data Cache, Full Route Cache, Router Cache 4개의 매커니즘이 존재합니다.

4-0. Next.js 캐싱 매커니즘

매커니즘 대상 위치 목적 지속 시간
Request Memoization 함수의 반환 값 서버 React 컴포넌트 트리 내에서 데이터를 재사용 요청당 수명 주기
Data Cache 데이터 서버 사용자 요청 및 배포 간에 데이터를 저장 지속적 (재검증 가능)
Full Route Cache HTML 및 RSC 페이로드 서버 렌더링 비용 감소 및 성능 향상 지속적 (재검증 가능)
Router Cache RSC 페이로드 클라이언트 탐색 시 서버 요청 감소 사용자 세션 또는 시간 기반

4-1. Request Memoization

설명

웹 서버로 페이지 요청이 들어오면 페이지에 필요한 데이터들을 fetch하게 되는데, 이때 동일한 endpoint로의 API fetch를 여러 컴포넌트에서 수행할 필요가 있다면 Request Memoization이 동작합니다. 상위 컴포넌트에서 API fetch 결과를 중복 요청 하지 않고, 실제 API 요청은 최초 1회만 전송되고 나머지는 응답값을 재사용합니다.

https://nextjs.org/_next/image?url=%2Fdocs%2Flight%2Fdeduplicated-fetch-requests.png&w=1920&q=75

Request Memoization은 서버에서 호출되는 GET 메서드에만 적용되므로, POST나 DELETE API 또는 클라이언트에서 호출되는 API에는 적용되지 않습니다. 그리고 한 번의 서버 렌더링 동안만 유효하기 때문에 따로 재검증할 필요가 없을 뿐 아니라 할 수도 없습니다.

예시

Request Memoization를 살펴보기 위해 별도의 벡엔드 서버를 만들어 벡엔드로 가는 요청을 살펴보겠습니다. 요청을 보낼때 헤더 값에 현재 페이지명 page, layout명을 추가하여 요청을 보냈습니다.

코드는 아래와 같이 같습니다.

backend

// app.js
import express from 'express';

const app = express();

app.get('/messages', (req, res) => {
  const requestSource = req.headers['x-id'];
  console.log(`${new Date().toISOString()}: EXECUTING /messages on backend from ${requestSource}`);
  res.json([
    { id: 1, text: 'Hello World' },
    { id: 2, text: 'Another message from the separate backend' },
  ]);
});

app.listen(8080);

client

// app/messages/layout.js

export default async function MessagesLayout({ children }) {
  const response = await fetch('<http://localhost:8080/messages>', {
    headers: {
      'X-ID': 'layout',
    },
  });
  const messages = await response.json();
  const totalMessages = messages.length;

  return (
    <>
      <h1>Important Messages</h1>
      <p>{totalMessages} messages found</p>
      <hr />
      {children}
    </>
  );
}

// app/messages/page.js
import Messages from '@/components/messages';

export default async function MessagesPage() {
  const response = await fetch('<http://localhost:8080/messages>', {
    headers: {
      'X-ID': 'page',
    },
  });
  const messages = await response.json();

  if (!messages || messages.length === 0) {
    return <p>No messages found</p>;
  }

  return <Messages messages={messages} />;
}