최근에 모바일 청첩장을 Vercel에 배포하는 프로젝트를 진행했습니다. 디자인이 자주 바뀌고, 업데이트된 내용이 즉시 반영되어야 하는 환경에서 **ISR(Incremental Static Regeneration)**를 선택했는데, 예상 외의 문제가 있었습니다.
getStaticProps + revalidate/invitation/[id], /event/[slug] 등 동적 경로 다수초기에는 getStaticProps에 revalidate: 60을 설정해, 최대 1분 주기로 콘텐츠를 갱신하도록 했습니다.
export const getStaticProps = async () => {
const data = await fetchData();
return {
props: { data },
revalidate: 60,
};
};그런데 문제는 배포 직후, 또는 데이터가 변경된 직후에도 반영이 수 분 이상 지연된다는 것이었습니다. 특히 모바일에서 캐시된 페이지가 유지되어 이전 정보가 계속 노출되는 현상이 발생했습니다.
Cache-Control 직접 제어export const getServerSideProps = async ({ res }) => {
res.setHeader(
'Cache-Control',
'public, max-age=0, s-maxage=60, stale-while-revalidate=30'
);
const data = await fetchData();
return { props: { data } };
};문제는 ISR이 아닌 SSR을 사용하는 방식이 되어, 정적 페이지의 장점을 잃는다는 점이었습니다.
// middleware.ts
import { NextResponse } from 'next/server';
export function middleware(req) {
const res = NextResponse.next();
// 특정 경로에 대해 캐시 무효화 헤더 적용
if (req.nextUrl.pathname.startsWith('/invitation')) {
res.headers.set('Cache-Control', 'no-cache, no-store');
}
return res;
}이를 통해 CDN의 캐시를 강제로 무효화하거나, 경로 단위로 더 정밀하게 캐시 정책을 설정할 수 있었습니다.
| 항목 | 개선 전 | 개선 후 |
|---|---|---|
| 데이터 변경 반영 속도 | 최대 수 분 | 수 초 ~ 1분 이내 |
| 모바일 캐시 문제 | 자주 발생 | 거의 없음 |
| 초기 페이지 로딩 속도 | 빠름 | 약간 느림 (SSR fallback 시) |
Next.js의 ISR은 분명 강력하지만, 즉시성 또는 실시간 반영이 중요한 서비스에는 단독으로 쓰기엔 부족한 점이 있습니다.
실시간 반영이 필요한 경우엔 ISR + Edge Function, 또는 SSR + 캐시 정책 조절이 현실적인 선택이었습니다.
정적 빌드의 퍼포먼스를 원하면서도, 실시간성도 잡고 싶다면 “캐시 전략”이 핵심입니다.
