Next.js + Supabase 블로그 성능 최적화: 정적 생성의 함정과 ISR
Vercel 배포 후 DB 수정이 반영되지 않고, 페이지 로딩도 느려진 문제를 ISR로 해결한 과정
상황
Next.js 16 + Supabase로 블로그를 만들고 Vercel에 배포했다. 로컬에서는 잘 동작하는데, 프로덕션에서 두 가지 문제가 발생했다.
문제 1: DB 수정사항이 반영되지 않음
- Supabase에서 포스트를 수정해도 사이트에 변화 없음
- 새 포스트를 추가해도 목록에 안 보임
문제 2: 페이지 로딩이 느림
- 첫 페이지 로딩에 체감상 1초 이상 소요
- 새로고침할 때마다 느림
첫 번째 시도: force-dynamic
처음에는 "DB 연결 문제인가?"라고 생각했다. 하지만 API는 정상 작동했다.
실제 원인은 Next.js의 정적 생성(Static Generation) 이었다.
// 이렇게만 작성하면 빌드 시점에 정적으로 생성됨
export default async function BlogPage() {
const posts = await getAllPostsAsync()
return <PostList posts={posts} />
}Next.js App Router는 기본적으로 페이지를 정적으로 생성한다. 빌드할 때 Supabase에서 데이터를 가져와서 HTML로 만들어두고, 이후 요청에는 그 HTML을 그대로 제공한다.
그래서 빌드 이후에 DB를 수정해도 반영되지 않았던 것이다.
해결: force-dynamic 적용
// 매 요청마다 서버에서 렌더링
export const dynamic = 'force-dynamic'
export default async function BlogPage() {
const posts = await getAllPostsAsync()
return <PostList posts={posts} />
}이렇게 하면 매 요청마다 서버에서 Supabase를 조회하고 렌더링한다. DB 수정사항이 즉시 반영되었다.
문제 1 해결! ...인 줄 알았다.
두 번째 문제 발생: 느려진 페이지 로딩
force-dynamic 적용 후 페이지가 눈에 띄게 느려졌다. curl로 측정해봤다.
curl -w "TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
-o /dev/null -s "https://my-blog.vercel.app/"결과:
TTFB: 0.831s
Total: 1.038s
TTFB(Time To First Byte)가 0.8초. 정적 페이지라면 0.1초 미만이어야 정상이다.
느린 이유
force-dynamic은 매 요청마다:
- Vercel Serverless 함수 실행 (cold start 포함)
- Supabase DB 쿼리 실행
- React 서버 사이드 렌더링
- 응답 전송
이 모든 과정을 거친다. 캐싱이 전혀 없으니 당연히 느릴 수밖에 없다.
최종 해결: ISR (Incremental Static Regeneration)
실시간 반영이 정말 필요할까? 블로그 특성상 수정 후 몇 분 정도 딜레이는 괜찮다.
ISR을 사용하면:
- 캐시된 페이지를 빠르게 제공 (정적처럼)
- 지정 시간마다 백그라운드에서 자동 재생성
- DB 변경사항도 반영됨 (약간의 딜레이)
// ISR: 10분마다 백그라운드 재생성
export const revalidate = 600
export default async function BlogPage() {
const posts = await getAllPostsAsync()
return <PostList posts={posts} />
}ISR 작동 방식
- 첫 요청: 페이지 생성 후 캐시
- 600초(10분) 동안: 캐시된 페이지 즉시 제공 (빠름)
- 600초 후 첫 요청: 백그라운드에서 재생성 시작
- 재생성 완료 후: 새 페이지로 캐시 교체
- 다음 요청부터: 새 페이지 제공
사용자는 항상 빠른 응답을 받고, 데이터도 최대 10분 딜레이로 반영된다.
적용 결과
모든 데이터 페칭 페이지에 revalidate = 600 적용:
// src/app/page.tsx (홈)
// src/app/blog/page.tsx (블로그 목록)
// src/app/blog/[slug]/page.tsx (포스트 상세)
// src/app/category/page.tsx, [slug]/page.tsx
// src/app/series/page.tsx, [slug]/page.tsx
// src/app/tags/page.tsx, [tag]/page.tsx
// src/app/notes/page.tsx예상 결과:
- TTFB: 0.8초 → 0.1초 이하
- 데이터 반영: 즉시 → 최대 10분 딜레이
정리: 언제 무엇을 쓸까
| 방식 | 언제 사용 | 장점 | 단점 |
|---|---|---|---|
| 정적 생성 (기본) | 데이터가 변하지 않는 페이지 | 가장 빠름 | 빌드 후 데이터 변경 불가 |
force-dynamic | 실시간 데이터가 필수인 경우 | 항상 최신 데이터 | 느림, 서버 부하 |
ISR (revalidate) | 대부분의 경우 | 빠름 + 데이터 갱신 | 약간의 딜레이 |
블로그, 마케팅 사이트, 문서 사이트 등 대부분의 경우 ISR이 정답이다. 실시간성이 중요한 대시보드나 채팅 같은 경우에만 force-dynamic을 사용하자.
배운 점
- Next.js 기본 동작을 이해하자: App Router는 기본적으로 정적 생성한다
- 성능 측정부터: "느린 것 같다"가 아니라 TTFB를 측정하자
- 트레이드오프를 이해하자: 실시간성 vs 성능, 적절한 균형점 찾기
- ISR은 좋은 기본값: 대부분의 상황에서 ISR이 최적의 선택
이 글이 어떠셨나요?
관련 포스트
Next.js 블로그에 SEO와 자체 분석 시스템 붙이기
JSON-LD, 동적 Sitemap, 조회수 추적, 유입 경로 분석까지 - 직접 만든 블로그에 필요한 것들을 하나씩 붙여본 기록
2026. 01. 28. 오후 01:00Development바이브 코딩 프로젝트의 6개월 후
바이브 코딩으로 만든 프로젝트의 지속 가능성. 기술 부채 청산, 유지보수, 그리고 진짜 "완성"이란 무엇인가.
2026. 02. 15. 오후 10:00Development1인 개발자의 바이브 코딩 시스템
혼자서 팀처럼 개발하는 법. CLAUDE.md, CI, 셀프 코드리뷰를 활용한 규칙 기반 솔로 개발 시스템을 구축한다.
2026. 02. 11. 오후 10:00뉴스레터 구독
새 글이 올라오면 이메일로 알려드려요.