1. 데이터 캐시(Data Cache)
먼저 데이터 캐시란 Fetch 메서드를 활용해서 api 서버로 부터 불러온 데이터를 넥스트 서버 측에서 캐싱해두는 그런 기능을 말한다. 그래서 이러한 데이터 캐시를 이용하며 영구적으로 데이터를 보관하여 불필요한 데이터 패칭의 요청을 방지하거나 특정 시간을 주기로 데이터를 갱신시킬 수 있다.
const response = await fetch(`~api`, { cache: "force-cache" });
그래서 위와 같이 fetch 메서드의 두 번째 인수로 객체 형태의 추가적인 옵션을 설정하여 캐시를 적용할 수 있게된다.
캐시 옵션에는 위와 같이 다양한 옵셥이 존재한다. 참고로 next 앱에서 데이터 캐시의 옵션은 Fetch 가 아닌 Axios 등의 다른 HTTP Request 라이브러리는 활용할 수 없다. 왜냐하면 next 앱에서 Fetch 메서드는 자체적으로 데이터 캐시와 관련된 여러 기능을 확장한 메서드이기 때문이다.
데이터 캐시 옵션
1. { cache : no-store } 옵셥
Next 15+ 부터 기본 값으로 제공, no-store는 캐싱 기능을 사용하지 않도록 설정하는 값이다. 데이터캐시 기능을 사용하지 않고, skip하여 Api에서 매번 새로운 데이터를 요청한다.
cache 옵션이 no-store으로 설정된 경우 그림과 같이 접속 요청으로 사전 렌더링을 진행 시 캐싱된 데이터를 사용하지 않기 때문에 데이터 캐시는 스킵이 되고 백엔드 서버에 데이터를 요청하여 응답받은 데이터로 페이지를 생성한다.
캐시 옵션에 의해 데이터 캐시 설정이 잘 되었는지 확인하기 위햇 페칭이 발생할 때마다 로그를 출력하도록 next.config.mjs 파일을 아래와 같이 수정한다.
/** @type {import('next').NextConfig} */
const nextConfig = {
logging: {
fetches: {
fullUrl: true,
},
},
};
export default nextConfig;
그런 다음 새로고침 후, VS Code의 터미널을 확인해보면
cache skip 이 표시된 걸 확인할 수 있다. 그리고 위에 설명한대로 Next 15+ 기본값이 no-store 이기 때문에 따로 캐시설정을 안해도 위와 같이 표시된다.
2. { cache : force-cache } 옵션
force-cache 캐시 옵션은 요청의 결과를 무조건 캐싱하여, 한번 호출된 이후에는 다시는 호출하지 않는다.
force-cache 옵션을 이용하여 API를 통해 데이터를 요청하면, 초기 요청에는 캐싱된 데이터가 없어 miss 라고 판정이 나고, 백엔드 서버에게 데이터를 요청하고 응답받은 데이터는 데이터 캐시에 저장(set)하고 페이지를 생성하여 브라우저에 반환한다. 그리고 그 이후에 같은 접속 요청이 들어오면 데이터 캐시의 데이터를 찾아(hit) 해당 데이터를 활용해서 빠르게 데이터를 생성해서 브라우저에게 반환하게 된다.
force-cache 옵션을 적용하면
force-cache 옵션이 적용된 api 요청에는 cache hit 라는 표시가 나오는걸 확인할 수 있다.
3. { next : { revaildate : 시간 }} 옵션
특정 시간마다 초기화 시키는 방법이다. 서버에서 특정 시간마다 캐싱데이터를 최신데이터로 초기화 시켜 주기 때문에 갱신된 데이터를 반환 받을 수 있다. 다만 이 방법은 "일정시간"에 초기화 되기 때문에 CRUD같은 액션에는 바로 피드백을 받아야 되는 액션에는 적합하지 않는다.
4. { next : { tags: ['a'] } } 옵션
Ondemand Revaildate 방식처럼 요청이 들어왔을 때 데이터를 최신화 하는 옵션이다.
이는 Next.js 13 이후 도입된 기능으로, 자동 캐싱 및 데이터 불러오기 최적화를 보다 세밀하게 제어할 수 있다. 특히 태그를 이용해 캐시를 그룹화하거나 특정 태그를 기준으로 캐시를 무효화할 수 있다.
// 데이터 패칭 시 태그 'a'를 설정
const res = await fetch('https://api.example.com/products', {
next: { tags: ['a'] }
});
// 캐시 무효화: 특정 태그를 가진 캐시를 모두 무효화
import { revalidateTag } from 'next/cache';
// 태그 'a'와 연관된 캐시 데이터 무효화
revalidateTag('a');
번외 : react-query의 useQuery 캐시 기능과 Next.js의 fetch 캐시의 차이점
react-query는 클라이언트 측 상태 관리와 API 요청을 위한 캐시이고, Next.js fetch는 서버 사이드 렌더링과 정적 페이지 생성을 위한 캐시이다.
2. Request Memoization
Request Memoization은 페이지를 서버 측에서 렌더링하는 과정에서 이러한 레이아웃 컴포넌트나 페이지 컴포넌트처럼
하나의 페이지를 이루고 있는 여러 개의 컴포넌트에서 발생한는 이러한 다양한 API 요청 중에 중복적으로 발생하는 요청들을 캐싱해서 단 한 번만 요청할 수 있도록 자동으로 데이터 패칭을 최적화해주는 기능이다.
예를 들어
그림과 같이 캐싱 설정이 되지 않은 상황에서 동일한 API 요청을 보내면 요청마다 중복된 데이터를 반환하는 비휼적인 동작을 하게 된다.
Next 는 이를 방지하기 위해 Request Memoization 이라는 기능을 사용해서 중복된 API 요청을 하나의 요청으로 자동으로 합쳐주는 기능을 제공하게 된다.
그래서 Request Memoization 를 활용한다면 위와 똑같은 상황에서
동일한 API 호출이 반복된다 하더라도 이제는 next 서버 측에서 Request Memoization 라는 이름으로 하나의 페이지를 렌더링하는 도중에 발생하는 중복된 API 요청들을 자동으로 캐싱해준다.
그래서 더붙여 그림을 통해 Request Memoization 설명하면 먼저 API 요청이 오면 백엔드 서버로 요청이 가지만 요청의 결과를 Request Memoization에 저장(SET)된 이후에는 동일한 요청이 발생했을 때, 데이터 캐시나 백엔드 서버에게 요청을 보내는게 아니라 먼저 Request Memoization 안에 캐시된 데이터가 있는지 먼저 확인하고 캐시가 존재하면 HIT 가 됐다면, 즉 지금 찾고 있는 데이터가 존재한다면 캐시된 데이터를 그대로 사용해서 렌더링하게 된다.
참고로 Data Cache 와 Request Memoization 차이점은
- Request Memoization는 하나의 페이지를 렌더링 하는 동안에 중복된 API 요청을 캐싱하기 위해 존재하므로 렌더링이 종료되면 모든 캐시가 소멸이 된다.
- Data Cache는 백엔드 서버로부터 불러온 데이터를 거의 영구적으로 보관하기 위해 사용되며 서버 가동 중에는 영구적으로 보관된다.
Request Memoization 를 next가 제공하는 이유는 앱 라우터에서 서버 컴포넌트의 도입으로 데이터 페칭하는 방식이 변화했기 때문이다.
그림처럼 페이지 라우터 버전에서 데이터 페칭을 위해서는 getServerSideProps 나 getStaticProps 같은 서버 측에서만 실행되는 함수를 통해서 데이터를 불러와서 이 페이지 컴포넌트에게는 이러한 props로 데이터를 넘겨주는 방식으로 동작했지만
서버 컴포넌트가 도입된 앱 라우터 버전의 next 앱에서는 각각의 컴포넌트 필요한 데이터를 직접 페칭해오는 방식으로 데이터 페칭으로 진행된다. 그래서 덕분에 앱 라우터에서는 컴포넌트 구조의 복잡성과 상관 없이 필요한 데이터를 손쉽게 가져와 독립적으로 사용할 수 있다.
그러나 이러한 패턴을 사용하다 보니 어쩔 수 없이 서로 다른 컴포넌트에서 동일한 데이터를 필요로 하는 예외적인 경우가 생길 수 밖에 없게되었다. 그래서 Request Memoization 를 통해 동일한 API 요청을 최적화 해주는게 필요하다.
출처 : [한 입 크기로 잘라먹는 Next.js(15+) 강의]
'Next.js' 카테고리의 다른 글
NextJS : 클라이언트 라우터 캐시 (0) | 2024.10.01 |
---|---|
NextJs : 풀 라우트 캐시(Full Route Cache) (1) | 2024.09.27 |
NextJs : 앱 라우터의 데이터 페칭 (0) | 2024.09.26 |
NextJs : App Router 네비게이팅 (0) | 2024.09.25 |
NextJS : 리액트서버 컴포넌트 이해하기 (2) | 2024.09.24 |