NextJS14버전에서는 다양한 캐싱기능을 지원하고 있다.
따라서 reactQuery, SWR같은 라이브러리를 사용하지 않아도 한번 불러온 데이터들을 캐싱할 수 있다는 이야기이다.
아래 다양한 캐싱기능을 나열해 보겠다.
1. unstable_cache
unstable_cache를 사용하면 데이터베이스 쿼리와 같이 비용이 많이 드는 작업의 결과를 캐시하여 여러 요청에 걸쳐 재사용할 수 있습니다. - NextJS docs
const data = unstable_cache(fetchData, keyParts, options)()
기본 코드는 위와 같다.
- fetchData : Promise를 반환하는 함수를 사용한다(async, await). 예로는 db통신 함수를 들 수 있다.
- keyParts : 배열로 사용해야 하고, 캐싱되는 데이터의 ‘키’값(닉네임)을 지정해 준다.
- options : tags, revalidate 2가지 기능이 존재한다.
1) revalidate
우선 쉬운 revalidate를 먼저 살펴보자.
const getCachedProducts = unstable_cache(getInitialProducts, ["home-products"], { revalidate: 60, });
사용방법은 위와 같다. unstable_cache안에 인자로 값을 지정해주면 된다.
여기서 주의해야 할 점이 있다.
revalidate를 60초로 설정했다고 60초마다 함수가 재실행되서 revalidate되는 것이 아니다.
60초동안은 기존 cache값을 이용하다 60초 이후에 다시 getCachedProducts이 값이 호출이 되면 기존 cache 값은 stale하다 판단하고 새로 데이터 값을 받아 온다.
2) tags
시간을 정해서 revalidate을 하는 방법이 아닌 태그를 지정해서 그에 맞게 revalidate을 하는 방법이다.
예제코드
const getCachedProductTitle = unstable_cache(getProductTitle, ["product-title"], { tags: ["product-title", "xxxx"], });
우선 사용할 unstable_cache에 tag값을 사용해 준다.
그리고
const revalidate = async () => { "use server"; revalidateTag("xxxx"); };
위와 같이 태그값을 통해 revalidate을 진행해준다.
이렇게 하면 이 태그값이 있는 모든 unstable_cache의 함수가 다시 실행되서 revalidate 된다.
3) revalidatePath
추가적으로 이 함수도 알아보겠다.
const revalidate = async () => { "use server"; revalidatePath("/home"); };
사용방법은 위와 같고 revalidatePath를 사용하면 말 그대로 특정 url에 존재하는 모든 cache값을 전부 revalidate를 한다는 것이다.
2. NextJS의 production
NextJS에서
npm run dev로 사용할 때는 우리가 생각한데로 코드가 동작한다.
하지만 npm run build → npm run start 즉 실제 production시 생각과는 다른 동작이 나타난다.
NextJS에서 기본적으로 static page만드는 것을 좋아한다. 그래서 웬만한 페이지를 build시 static page로 만들어 버린다. 아래와 같이 생각하면 쉽다.
특정 페이지가 보는 사람에 따라 내용이 달라지는가? 혹은 dynamic parameter를 사용하는가?
→ 쿠키, 헤더사용, 동적 url값 사용시
ex) 프로필 페이지, 각 물건 페이지(동적 라우팅),,,,
위 문장에 걸리는 내용이 있는 페이지는 dynamic page가 되고 그 외에 페이지들은 static page로 build된다.
따라서 dev모드로 개발할 때 cache기능을 사용하지 않으면 당연히 새로고침할 때마다 계속 db호출이 존재할 것이다.
하지만 실제 build, 배포를 진행해보면 static page에서는 처음 build시에 db호출해서 정적 페이지가 생성이 되어버리고 새로고침해도 다시 db호출을 하지 않는다. 위에 설명한 unstable_cache기능을 사용해도 말이다.
위와 같은 동작방식 때문에 문제가 생긴다면 다음과 같은 해결방법을 사용해야 한다.
export const dynamic = 'auto' export const revalidate = false export default function MyComponent() {}
위 세팅이 기본 default로 설정되어 있는 값들이다.
하나씩 설명해보겠다.
1) dynamic : auto로 설정되어 있으면 위에 설명했듯이 NextJS마음대로 static page, dynamic page를 생성한다. 여기서 force-dynamic으로 설정해주면 무조건 dynamic page로 만들어 줄 수 있다.
2) revalidate : 마찬가지이다. 이 값을 초로 설정해주면 특정 초가 지났을 때 이 페이지 요청이 새로 들어오면 다시 이 페이지를 생성해서 저장한다. 즉 static page이긴 하지만 특정 초가 지나면 다시 만들어 주는 것이다.

- 처음 요청이 들어오면 revalidate와 함께 fetch가 요청된다.
- revalidate시간 안에 다음 요청이 들어오면 여전히 cache된 내용이 보여진다.
- revalidate시간이 지나서 처음 요청이 들어오면 여전히 cache된 내용이 보여지지만 이제는 cache가 stale하다고 판단하여 NextJS가 background에서 revalidation을 trigger해서 cache를 새로운 내용으로 바꿔준다.
- 그 다음 요청에 경우 새로 갱신된 data를 볼 수 있다.
3. 그럼 동적 URL은 어떻게 static page로 만들어 줄 수 있을까?
UX를 향상시키는 것은 항상 중요한 부분이다.
dynamic page가 많다는 것은 로딩이 필요한 페이지가 많다는 것이고 이 또한 사용자의 경험을 해치는 부분이 될 수 있다. 위에 여러 설정들을 통해 우리가 필요한 만큼 페이지의 build 방식을 조절 해 줄 수 있었지만 dynamic params의 경우에는 설정할 수 없었다…
하지만 NextJS는 신이기 때문에 이 또한 방법이 존재한다. 바로 설명들어가겠다.
1) generateStaticParams
export async function generateStaticParams() { const products = await db.product.findMany({ select: { id: true, }, }); return products.map((product) => ({ id: product.id + "" })); }
위에 예제코드를 한번 살펴보겠다.
generateStaticParams라는 함수를 이용해서 동적 라우팅이 될 만한 페이지를 미리 생성해주는 과정이다.
위에서는 url을 product의 id값으로 만들었기 때문에 db에 product id값을 전부 불러와서 모든 제품 상세페이지를 미리 만들어주는 방법을 사용했다.
이렇게 하면 모든 제품페이지가 미리 생성이 되어 장점이 있겠지만 제품의 개수가 굉장히 많아지면 build시 서버가 터져버릴 수 있다는 단점이 있기 때문에 잘 조절해서 사용해야 한다.
Q. 그럼 일부만 생성하고 일부는 생성하지 않아도 되는 걸 까?
A. 물론 가능하다
애초에 NextJS에서 default 세팅값으로
export const dynamicParams = true
위와 같은 구조를 가지고있다.
여기서 false로 바꿔 세팅하면 미리 생성한 일부의 동적 라우트를 제외한 나머지 동적 라우트들은 404 page로 가게된다. 그래서 웬만하면 이 기본 세팅을 바꿀일은 없겠지만 그래도 알아두자.
위처럼 NextJS는 다양한 기능을 지원하는 만큼 제약조건, default 세팅값들이 많이 존재한다.
공식문서, 실제 코드들을 잘 참고해서 이런 좋은 기능들을 적재적소에 활용할 수 있게 노력해야 될 것 같다.