Tina CMS API 과부하 해결기: ISR로 캐싱 최적화
Next.js ISR과 웹훅으로 API 호출량 줄이고 캐싱 성능 개선하기
이전 글에서 Tina CMS를 도입해 컨텐츠 관리 워크플로우를 개선한 이야기를 나눴는데요. 모든 게 순조롭게 돌아가던 어느 날, Tina 측에서 예상치 못한 메일을 받았습니다.
귀하의 프로젝트에서 API 호출이 과도하게 발생하고 있습니다…
사실 부끄러운 이야기지만, generateStaticParams로 모든 페이지를 빌드 타임에 미리 생성하는 방식으로 구현해놨던 게 문제였어요. 마케터들이 글 하나를 완성하기까지 평균 10-15번 저장하는데, 매번 수백 개의 전체 컨텐츠를 다시 빌드하니 API 호출량이 기하급수적으로 늘어났죠.
// 기존 방식: 빌드 시 모든 페이지 생성
export async function generateStaticParams() {
const posts = await getAllPosts() // 매번 전체 컨텐츠 조회
return posts.map((post) => ({ slug: post.slug }))
}
이 문제를 계기로 ISR (Incremental Static Regeneration) 도입을 고민하게 되었습니다. ISR의 핵심 장점들이 우리 상황에 딱 맞았거든요:
- 온디맨드 생성: 빌드 단계에서 컨텐츠를 미리 만들지 않고 요청할 때만 생성
- 선택적 재생성: 변경된 페이지만 다시 생성하므로 API 호출량 대폭 감소
- 캐시 무효화: 웹훅을 통해 수정된 페이지의 캐시만 지워서 즉시 반영
개선 과정
1단계: SSG에서 ISR로 전환
먼저 generateStaticParams를 제거하고 revalidate = false로 설정해서 수동 revalidate만 사용하도록 변경했습니다.
2단계: 웹훅 API 구현
GitHub 웹훅을 받아서 변경된 컨텐츠만 선택적으로 revalidate하는 API를 구현했습니다.
// app/api/webhook/route.ts
export async function POST(request: Request) {
const res: PushEvent = await request.json();
// 변경된 컨텐츠 파일들만 revalidate
res.commits.forEach(commit => {
[...commit.added, ...commit.modified].forEach(async filePath => {
if (filePath.startsWith("content/contents")) {
const { frontmatter } = await parseContentFile(filePath);
revalidatePath(`/content/${frontmatter.slug}`);
}
});
});
return Response.json({ revalidated: true });
}
3단계: 배포 환경 설정
Vercel 배포 시 웹훅 API가 컨텐츠 파일을 읽을 수 있도록 next.config.js에 설정을 추가했습니다. CMS 특성상 컨텐츠를 런타임에 동적으로 읽어야 하는데, Next.js는 import로 직접 참조되는 파일만 번들에 포함하기 때문에 outputFileTracingIncludes 설정으로 미리 알려줘야 해요.
// next.config.js
module.exports = {
outputFileTracingIncludes: {
// 해당 폴더 파일들을 번들에 포함
"/api/webhook": ["./content/contents/**/*"],
},
}
4단계: 검색 인덱스 최적화
기존에는 빌드 시 Tina Cloud API를 호출해서 검색 인덱스를 생성했는데, 로컬 파일 시스템을 직접 읽도록 변경했습니다. 빌드 타임에는 컨텐츠 파일에 접근할 수 있어서 API 호출 없이도 인덱스를 만들 수 있더라구요. (하하)
// 로컬 파일 시스템 직접 읽기
const contents = readdirSync("content/contents").map((file) =>
readFileSync(file, "utf-8"),
)
개선 결과
캐싱 전후 응답 시간이 2.47초에서 437ms로 약 6배 성능이 향상되었습니다!

캐싱 전: 2.47초

캐싱 후: 437ms
ISR과 웹훅 도입으로 API 호출량은 대폭 줄이면서도 사용자 경험은 크게 향상되었어요. 마케터들이 컨텐츠를 수정하면 즉시 프로덕션에 반영되는 것은 물론이고, 더 이상 API 제한을 걱정할 필요도 없어졌습니다.
추가로 Vercel 설정에서 컨텐츠 폴더 변경 시에는 빌드를 하지 않도록 했어요. 기존에는 글 하나만 수정해도 전체 사이트를 다시 빌드했는데, 이제는 빌드 없이 GitHub 웹훅을 통해 해당 페이지만 revalidate하니까 훨씬 효율적이죠.
마무리
사실 Tina에서 API 과부하 메일을 받았을 때는 정말 당황스러웠어요. 수정한 후에도 혹시 또 문제가 생길까 봐 며칠간 메일함을 수시로 확인했던 기억이 나네요. 하지만 이 경험 덕분에 Next.js ISR을 제대로 이해하게 되었고, 더 나은 아키텍처를 구축할 수 있었습니다.