Next.js의 캐싱 매커니즘 두 번째 ‘Data Cache’

Cache

이 글은 2024-05-12에 작성되었어요.

프롤로그


오늘은 Data Cache에 대해서 배워보겠습니다.

이전 포스팅에서 말씀드렸던 Request Memoization은 ‘Next.js’의 기능이 아닌 ‘React.js’의 기능이었는데요. 이번에 다룰 Data Cache의 경우 Next.js에서 제공하는 기능 중 하나입니다.

이전부터 느꼈지만 Next.js는 Turborepo, Next Image, Next Router 등 Cache를 적극적으로 활용하는 느낌을 받았는데요. 정말 똑똑한 개발자 분들이 많이 계신 것 같습니다. 😀 같이 일해보고 싶네요.

Data Cache


데이터 캐시는 여전히 중복된 요청을 방지하는 것이 핵심인데요. 그 중에서도 네트워크 요청을 통해 가져온 결과물(데이터)를 저장하는 방식이에요.

우선 코드로 한번 보겠습니다.

// reference. https://blog.webdevsimplified.com/2024-01/next-js-app-router-cache/ export default async function Page({ params }) { const city = params.city // 해당 요청 결과물은 재요청 시 캐시된 데이터를 활용합니다. const res = await fetch(`https://api.globetrotter.com/guides/${city}`) const guideData = await res.json() return ( <div> <h1>{guideData.title}</h1> <p>{guideData.content}</p> {/* Render the guide data */} </div> ) }

Request Memoization(이하 RM)이랑 작동 방식이 비슷해 보이지만, 데이터 캐시는 기본 동작으로 새롭게 애플리케이션을 배포하지 않는 이상 캐시가 지워지지 않는다는 점이 중요합니다.

이미 눈치 채신 분들도 계시겠지만, 이번에는 네트워크 요청을 시도한 컴포넌트는 ‘하나’ 임에도 캐시된 데이터를 활용한다고 되어 있네요. 🙂

이 뜻은, 같은 렌더링 주기가 아니어도 캐싱된 데이터를 활용한다고 볼 수 있겠습니다.

하지만 유의하실 점이 있는데요.

캐시가 ‘지워지지 않는’다는 것은 의도치 않게 유저가 최신 데이터를 볼 수 없을 수도 있다는 점을 생각해야 합니다.

그래서 이렇게 경우에 따라 ‘영구히’ 저장되는 캐시는 항상 데이터가 얼마나 ‘유지’시킬 것인지 그리고 최신 데이터를 요청해야하는 시점이 언제인지 ‘검증’을 하는 과정이 꼭 필요합니다.

유지 그리고 검증


앞서 말씀드렸던 유지와 검증은 어떻게 결정하면 좋을까요?

제 짧은 지식으로는 서비스 제공자의 기획 의도에 따라 결정해야 한다고 생각하는데요.

예를 들어 보겠습니다.

블로그 포스팅과 같은 페이지는 작성자가 작성 내용을 수정하지 않는 이상 내용이 변할 리는 없습니다.

이 경우, 요청한 포스팅에 대한 결과물을 배포 주기 내에 저장한 뒤에 항상 같은 결과물을 보여주도록 할 수 있습니다.

또는 작성한 포스팅에 대해 수정하게 될 여지가 있다면 ‘일정 주기’마다 새롭게 데이터를 요청하고 다시금 캐싱하는 방식도 있겠습니다.

이를 ‘시간 기반 재검증’이라고 하는데요. Next.js는 이 과정을 간단한 옵션으로 제공하고 있습니다.

const res = fetch(`https://api.globetrotter.com/guides/${city}`, { // revalidate 단위는 '초' 단위 next: { revalidate: 3600 }, })

정말 간단하지 않나요?

그런데 생각해보니 예상과 다르게 ‘수정’을 시도하지 않게되는 경우 불필요하게 요청을 다시 하게 되는 문제는 해결을 못했습니다.

이럴 때는 ‘주문형 재검증’ 방식을 사용하면 되겠습니다.

이 방식은 ‘원하는 시점’에만 캐시된 데이터를 지우고, 새롭게 네트워크를 요청하게 합니다.

아래 코드는 ‘두 가지’ 방식을 제안합니다.

// 첫 번째 : 문자열 경로를 Key로 정함 import { revalidatePath } from "next/cache" export async function publishArticle({ city }) { createArticle(city) revalidatePath(`/guides/${city}`) } // 두 번째 : 커스텀 Key(Tag)를 정해서 요청함 const res = fetch(`https://api.globetrotter.com/guides/${city}`, { next: { tags: ["city-guides"] }, }) import { revalidateTag } from "next/cache" export async function publishArticle({ city }) { createArticle(city) revalidateTag("city-guides") }

만약 새롭게 포스팅을 추가하거나 수정하는 경우에는 revalidatePath를 통해 캐시된 데이터를 지워서 최신 데이터를 유지하도록 해보세요.

항상 최신 데이터를 유지


반대로 항상 최신 데이터를 요청해야 하는 경우가 있을 수도 있습니다.

이 경우에는 cache 옵션의 ‘no-store’를 사용하거나 revalidate의 값을 0으로 설정하면 됩니다.

하지만 최신 데이터를 유지해야하는 ‘의도된’ 상황이 아니라면 사용을 지양하는 것이 좋다는 것이 제 짧은 식견입니다.

참고 이미지


Untitled.png

Reference.


👉🏻 Finally Master Next.js's Most Complex Feature - Caching