[TIL] Next.js Caching 전략

기성·2024년 10월 3일
1

TIL

목록 보기
66/81

Next.js에서는 Cache가 정말 중요하다 생각될 정도로 모든 데이터를 캐싱해버린다.

이 이미지는 Next.js의 공식 홈페이지에서 자신들이 어떻게 캐시를 관리하는지 보여주는 이미지이다. 이미지상에서만 캐시의 종류가 4개가 존재한다. 하나씩 캐시가 어떻게 동작하는지 알아보자.

RSC라는 단어가 조금씩 나오는데 RSC는 React Server Component의 약자이다.

Full Route Cache

Next.js는 빌드 시점에 페이지를 렌더링 하고 그 결과를 캐싱하는 Full Route Cache 기능을 제공한다.

이 캐시를 이용하여 서버는 매 요청마다 페이지를 다시 렌더링 하지 않고, 미리 생성된 HTML과 데이터를 빠르게 제공할 수 있어 페이지 로딩 속도가 크게 향상된다.

이 과정에서 Next.js는 서버에서 React의 API를 활용해서 렌더링을 진행한다.

  1. React Server Component Payload 생성: React는 서버 컴포넌트들을 스트리밍에 최적화된 특별한 데이터 형식으로 렌더링한다. 이 데이터는 클라이언트에서 사용할 컴포넌트의 정보와 상태를 포함한다.
  2. HTML 생성: Next.js는 이 Payload와 클라이언트 컴포넌트의 JavaScript 코드를 사용하여 서버에서 최종 HTML을 생성한다.

기본적으로 Full Route Cache는 지속적이며, 사용자 요청 간에 렌더링 결과가 캐싱되어 있다. 그래서 동일한 페이지에 대한 요청이 있을 때마다 서버는 빠르게 응답하여 페이지를 보여준다.

그러나 데이터가 변경되거나, 실시간 정보가 필요한 페이지의 경우에는 이 캐시를 무효화하거나 동적 렌더링을 할 수 있다. revalidate 옵션 등의 기능을 통해 제어가능하다.

Data Cache

Next.js의 Data Cachefetch 함수를 기반으로 데이터를 캐싱하여, 서버 요청 간에도 데이터를 지속적으로 활용할 수 있게 해준다. 이는 외부 데이터 소스에 대한 요청 수를 줄이고, 응답 시간을 개선하는 데 큰 도움을 준다.

Next.js의 fetch는 브라우저의 fetch API를 확장한 것으로, 서버 측에서 각 요청에 맞는 캐싱 전략을 설정할 수 있다. 기본적으로 fetch 함수를 사용하면 데이터가 자동으로 캐싱되며, 이는 동일한 데이터에 대한 중복 요청을 방지한다. 이는 후술에서 Request Memoization에서 볼 수 있다.

예를 들어, 다음과 같이 fetch를 사용할 수 있다:

const res = await fetch('<https://api.example.com/data>', { next: { revalidate: 3600 } });
const data = await res.json();

위 코드에서 { next: { revalidate: 3600 } } 옵션은 해당 데이터가 캐시에 저장된 후 3600초(1시간) 동안은 재검증 없이 캐시된 데이터를 사용하도록 설정한다. ISR의 방식으로 볼 수 있다.

만약 특정 데이터를 캐싱하지 않고 매번 최신 데이터를 가져오고 싶다면, 다음과 같이 옵션을 설정할 수 있다:

const res = await fetch('<https://api.example.com/data>', { cache: 'no-store' });
const data = await res.json();

{ cache: 'no-store' } 옵션을 사용하면 해당 요청은 캐싱되지 않는다. 그렇기 때문에 SSR의 방식으로 페이지를 구성할 수 있다.

Request Memoization


Request Memoization은 서버 컴포넌트 렌더링 중 동일한 fetch 요청이 여러 번 호출되더라도, 실제로는 한 번만 실행되도록 하는 기능이다. 이를 통해 불필요한 중복 네트워크 요청을 방지하고, 서버 자원과 응답 시간을 최적화할 수 있다. 이 점이 내가 서버 컴포넌트에서는 어떻게 데이터가 공유를 할 수 있을까 하면서 고민했던 점인데 이미 강의에서도 한번 했던 내용,, 정신채리

예를 들어, 컴포넌트 트리의 여러 곳에서 동일한 데이터를 필요로 할 때:

async function getCommonData() {
  const res = await fetch('<https://api.example.com/common-data>');
  return res.json();
}

// 컴포넌트 A에서 데이터 호출
const dataA = await getCommonData();

// 컴포넌트 B에서 동일한 데이터 호출
const dataB = await getCommonData();

위 코드에서 getCommonData 함수는 두 번 호출되었지만, 실제 네트워크 요청은 한 번만 발생한다. 두 번째 호출에서는 첫 번째 호출의 결과를 재사용한다.

이러한 메모이제이션은 React의 기능으로, 서버 컴포넌트 렌더링 과정에서만 적용된다. 렌더링이 완료되면 메모리에 저장된 캐시는 제거되며, 다음 요청 시에는 다시 초기화된다.

Router Cache

Next.js에는 레이아웃, 로딩 상태 및 페이지별로 분할된 경로 세그먼트의 RSC 페이로드를 저장하는 인메모리 클라이언트 측 라우터 캐시가 있다. 사용자가 경로 사이를 탐색할 때 Next.js는 방문한 경로 세그먼트를 캐시하고 사용자가 탐색할 가능성이 있는 경로를 미리 가져온다. 그 결과 즉각적인 앞,뒤로 탐색하고 탐색 간에 전체 페이지를 다시 로드하지 않으며 React 상태와 브라우저 상태를 보존할 수 있다.

  • 레이아웃은 캐시되어 탐색 시 재사용된다(부분 렌더링).
  • 로딩 상태는 캐시되어 즉시 탐색 시 재사용된다.
  • 페이지는 기본적으로 캐시되지 않지만 브라우저 뒤, 앞으로 탐색 시 재사용된다. 실험 중에 있는 staleTimes 구성 옵션을 사용하여 페이지 세그먼트에 대한 캐싱을 활성화할 수 있다.

어우 Next.js를 사용하기 위해서는 이러한 캐시 전략들을 모두 알고 사용해야 이 프레임워크를 제대로 사용할 줄 안다 라고 할 수 있을 것 같다. 많이 헷갈리지만 어쩔 수 없지

profile
프론트가 하고싶어요

0개의 댓글