Before you learn
[NextJS] Cache Strategy (캐시 전략)
캐시 전략
각 홈페이지마다 cache 전략을 잘 세워서 작성해야 한다.
유저수가 많은 페이지일수록 더욱 중요하다.
Product에 관련된 Cache 전략을 작성해 보겠다.
(캐시 전략에 대해서만 작성하겠다.)
Home page : `home-product-list`를 60초마다 캐시 재검증한다.
// app/(tabs)/home/page.tsx
import { unstable_cache as nextCache } from "next/cache";
const getCacheProductList = nextCache(getInitialPostList, ["home-product-list"], {
tags: ["home-product-list"],
revalidate: 60,
});
Product detail page : `product-detail-${productId}`, `product-title-${productId}`를 60초마다 캐시를 재검증한다.
// app/product/[id]/page.tsx
import { unstable_cache as nextCache } from "next/cache";
function getCachedProductDetail(productId: number) {
const cachedOperation = nextCache(getProductDetail, [`product-detail-${productId}`], {
revalidate: 60,
tags: [`product-detail-${productId}`],
});
return cachedOperation(productId);
}
function getCachedProductTitle(productId: number) {
const cachedOperation = nextCache(getProductTitle, [`product-title-${productId}`], {
revalidate: 60,
tags: [`product-title-${productId}`],
});
return cachedOperation(productId);
}
Product add page
: product 추가 후 product detail page로 이동하므로, `product-detail-${producttId}`, `product-title-${productId}`를 재검증하고,
만약 유저가 detail page에서 home page로 이동할 수 도 있기에, "home-product-list"도 재검증한다.
// app/add/product/actions.ts
import { redirect } from "next/navigation";
import { revalidateTag } from "next/cache";
export async function uploadProduct(_: any, formData: FormData) {
// db에 product upload 하는 코드...
revalidateTag(`home-product-list`);
revalidateTag(`product-detail-${product.id}`);
revalidateTag(`product-title-${product.id}`);
redirect(`/product/${product.id}`);
}
}
}
Product edit page
: edit page에서 편집 완료 후 product detail page로 이동하므로, `product-detail-${productId}`, `product-title-${productId}`를 재검증하고,
만약 유저가 detail page에서 home page로 이동할 수 도 있기에, "home-product-list"도 재검증한다.
// app/edit/product/[id]/page.tsx
import { unstable_cache as nextCache } from "next/cache";
function getCacheProductDetail(productId: number) {
const cachedOperation = nextCache(getProductDetail, [`product-detail-${productId}`], {
revalidate: 60,
});
return cachedOperation(productId);
}
// app/edit/product/[id]/actions.ts
import { redirect } from "next/navigation";
import { revalidateTag } from "next/cache";
export async function editProductUpload(_: any, formData: FormData) {
// db에 수정된 product 데이터 업로드 하는 코드 ...
revalidateTag(`home-product-list`);
revalidateTag(`product-detail-${product.id}`);
revalidateTag(`product-title-${product.id}`);
redirect(`/product/${product.id}`);
}
}
}
Product delete
: product detail page에서 삭제버튼을 누르면 product가 삭제되고, home page로 이동하므로, "home-product-list"를 재검증하고,
DB에서 삭제는 되었지만 아직 캐시가 남아있고, 해당 페이지에 접근을 차단하는 코드가 없을 경우,
삭제된 product의 detail page로 접속이 될 수도 있기에, `product-detail-${productId}`, `product-title-${productId}`를 모두 재검증한다.
// app/product/[id]/actions.ts
import { revalidateTag } from "next/cache";
export async function onClickDeleteProduct(id: number) {
// db에서 해당 product 삭제하는 코드 ...
revalidateTag("home-product-list");
revalidateTag(`product-detail-${product.id}`);
revalidateTag(`product-title-${product.id}`);
// redirect는 deleteBtn 마지막에 동작하도록 되어있어서 생략함
}
다른 유저가 CRUD를 한 경우에도 최신화된 product를 보여줘야 하기에, 60초마다 전부 재검증하도록 설정해 두었다.
만약 캐시 재검증 전에 다른 유저가 해당 product를 삭제했더라도 404 처리되어 해당 페이지에 접속하지 못하게 설정해 두었다.
revalidate: 60
의문점
Q1. Why Use?
간단히 적자면,
DB의 휴식시간을 주기 위해서이고, 효율적으로 데이터를 사용하고, 더욱 빠르게 보여주기 위함이다.
Q2. Tag를 product-title, product-detail처럼 하지 않고, 뒤에 id값을 넣는 이유?
product-title만 하게 되면 모든 product-title Tag를 가진 캐시가 재검증한다.
그렇기에 유저에게 보여주는 하나의 데이터만 재검증하기 위해 product-title-${productId}처럼 사용하였다.
(굳이 보여주지 않는 부분까지 재검증할 필요는 없고, 보여줄 때 해당부분만 재검증하면 된다.)