[ Data Fetching ] Caching

차차·2023년 5월 19일
0

Next Docs

목록 보기
20/34
post-thumbnail

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로 컴포넌트 간에 전달하는 대신 필요한 컴포넌트에서 데이터를 직접 가져오는 것을 권장한다.

  • 서버 데이터 패칭 함수가 클라이언트에서 사용되지 않도록 하기 위해 서버 전용 패키지를 사용하는 것을 권장한다.


GraphQL and 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: '...' });
  // ...
});

Preload pattern with 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가 아니다.
  • 이 패턴은 완전히 선택적이며, 경우에 따라 최적화하는 데 사용할 수 있는 것이다.
  • 이 패턴은 병렬 데이터 패칭의 상위 최적화다. 이제 프로미스를 props로 전달할 필요가 없으며 preload 패턴에 의존할 수 있다.

Combining cachepreload, 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.tsxlayout.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

profile
나는야 프린이

0개의 댓글