들어가기 전.. 10월 24일 큰 거 올까?

스크린샷 2024-08-27 오후 5.47.13.png

이전 스터디를 하면서 next.js의 app-router가 안정이 된다면 fetch를 사용해야 된다 → axios의 경우에는 직접 캐싱 관련 설정을 해주어야 된다고 한다는 부분을 보았다. 저번에는 axios가 어떻게 node와 브라우저를 인식하는지에 중점을 둬서 작성하였고 fetch로 interceptor 기능을 만들 때는 어떻게 할 지와 관련하여 중점적으로 다루었다. 이번에는 Next.js의 Caching 부분에 관련하여 공부를 좀 더 해보고자 한다. 다음에는 가볍게 라이브러리 관련인 ky와 return-fetch으로..

스크린샷 2024-08-27 오후 6.29.24.png

Caching Mechanisms

Next.js의 성능을 최적화하는 방법을 이해하는 데 도움을 주려는 목적이라고 한다. 페이지 소개에서도 보면 최소한의 설정으로도 최상의 성능을 발휘할 수 있도록 기본값이 제공이 되기 때문에 모르고 사용해도 된다고 한다. 서버에 적용되는 캐시 매커니즘은 다음과 같다.

메커니즘 내용 위치 목적 지속 시간
Request Memoization fetch 함수의 return 값 서버 React 컴포넌트 트리에서 데이터 재사용 요청 생명 주기 동안
Data Cache 데이터 서버 사용자 요청 및 배포 간 데이터 저장 영구적(revalidate 가능)
Full Route Cache HTML 및 RSC(React Server Component) 페이지 로드 서버 렌더링 비용 절감 및 성능 향상 영구적(revalidate 가능)
Router Cache RSC 페이지 로드 클라이언트 네비게이션 시 서버 요청 감소 사용자 세션 또는 시간 기반

caching-overview.avif

그림 요약 : 라우트가 빌드 타임에 정적으로 렌더링될 때와 정적 라우트가 처음 방문될 때의 캐싱 동작

캐싱 동작은 사용 사례에 따라 개별 라우트 및 데이터 요청에 대한 캐싱 동작을 구성할 수 있다.

Request Memoizatioin

웹 서버로 페이지 요청이 들어오면 페이지에 필요한 데이터들을 fetch하게 되는데, 이 때 동일한 URL과 옵션을 가진 요청을 자동으로 메모이제이션(memoization)하기 위해 fetch API를 확장한다.(이건 React에서 확장한 기능이라고 한다.) 같은 데이터를 여러 라우트에서 사용해야 하는 경우(예: 레이아웃, 페이지, 여러 컴포넌트에서), 상위 컴포넌트에서 API fetch 결과를 prop drilling 하는것 대신, 각 컴포넌트에서 fetch를 수행하도록 구현해도 실제 API 요청은 최초 1회만 전송되고 나머지는 응답값을 재사용한다.

스크린샷 2024-08-27 오후 4.07.21.png

// 예시 코드
async function getItem() {
  // `fetch` 함수는 자동으로 memoized되며 결과값이 캐싱된다.
  const res = await fetch('https://.../item/1');
  return res.json();
}

// 컴포넌트 A
const item = await getItem(); // cache MISS

// 컴포넌트 B
const item = await getItem(); // cache HIT : getItem 함수의 실행 없이 캐싱된 return값을 받는다.

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

Data Cache

기본적으로 fetch를 사용하는 데이터 요청은 캐시되지 않기 때문에 캐싱 동작을 구성하려면 fetch의 cache 및 next.revalidate 옵션을 사용하여 동작하게 할 수 있다.  성공적으로 데이터를 가져왔다면 그 응답값을 저장해두었다가 동일한 경로로 fetch 함수를 실행할 때 실제 API 호출은 건너뛰고 저장해놓은 응답값을 반환하는 형태이다.

만약, 하나의 요청 동안만 유효한 Request Memoization과 다르게 Data Cache는 일정 시간 동안에 웹 서버로 들어오는 모든 요청에 대해 동작한다. 만약 **next.revalidate**를 1초로 설정했다면, 1초에 1000명의 사용자가 접속해도 실제 API 요청은 1회 전송된다는 뜻!

스크린샷 2024-08-27 오후 5.30.14.png

주의할 점

revalidate 시간이 지나더라도 첫 요청은 캐싱된 값을 (STALE 상태여도) 반환한다. → 그 이유는 사용자의 빠른 응답 선호 때문, 또한 실시간이 아니라면 서버 부하를 계속해서 줄 필요가 X 반환 후 백그라운드에서 API를 호출해서 값을 업데이트하는데, 개발자 의도와 다르게 동작할 수 있기 때문에 캐시를 적용할 때 주의가 필요하다.

해결방법으로는 router.refresh로는 Data Cache가 revalidate되지 않고, revalidatePath를 사용해야 한다고 합니다. (이때는 즉시 revalidate 되기 때문에, 다음 첫 요청에도 새로운 값을 반환한다.)