SSR vs SSG 비교: Claude Code로 Next.js/Astro 렌더링 전략 고르기
Claude Code로 Next.js/Astro의 SSR, SSG, ISR, 정적 export를 비교하고 실행 코드와 검증 명령까지 정리합니다.
용어부터 맞추기
SSR과 SSG의 선택은 단순히 빠른 쪽을 고르는 문제가 아닙니다. SSR은 server-side rendering, 즉 요청이 들어올 때 서버가 HTML을 만드는 방식입니다. SSG는 static site generation, 즉 빌드 시점에 HTML을 만들어 CDN이나 정적 호스팅에서 배포하는 방식입니다. ISR은 Incremental Static Regeneration으로, 정적 응답을 유지하면서 시간이나 이벤트 기준으로 다시 생성하는 절충안입니다.
Next.js App Router를 처음 쓰면 Server Component가 항상 요청마다 SSR 된다고 오해하기 쉽습니다. 현재 공식 Dynamic Route Segments 문서의 TypeScript 예시는 params를 Promise로 다룹니다. 그리고 요청 시점 렌더링을 명시해야 하지만 일반적인 요청 API를 쓰지 않는 경우에는 connection을 사용할 수 있습니다.
Claude Code에 “이 페이지를 빠르게 만들어줘”라고만 말하면, 재고나 검색 결과, 회원별 UI까지 정적으로 굳혀 버릴 수 있습니다. 반대로 모든 페이지를 SSR로 바꾸면 TTFB와 서버 비용이 올라갑니다. 먼저 페이지별로 데이터 신선도, 개인화 여부, 업데이트 빈도, 수익 페이지 영향, 검증 명령을 표로 만들게 하는 편이 안전합니다.
비교표와 실제 사용 사례
| 질문 | SSG | ISR | SSR |
|---|---|---|---|
| HTML 생성 시점 | 빌드 시점 | 빌드와 재검증 시점 | 요청 시점 |
| 적합한 페이지 | 글, 문서, 랜딩 페이지 | 상품, 카테고리, 뉴스 목록 | 대시보드, 검색, 장바구니 |
| 데이터 신선도 | 빌드 시점 | 시간 또는 이벤트 기준 | 거의 최신 |
| 배포 비용 | 낮음 | 중간 | 높아지기 쉬움 |
| 흔한 실패 | 긴 빌드, 오래된 내용 | 짧은 시간 동안 오래된 응답 | 캐시 없으면 느림 |
첫 번째 사례는 블로그나 문서입니다. 본문, OGP, 내부 링크, CTA가 게시 시점에 고정된다면 SSG가 가장 단순합니다. 글을 수정할 때 다시 빌드하면 됩니다. JavaScript 크기도 줄이고 싶다면 Claude Code 코드 분할과 함께 보면 좋습니다.
두 번째 사례는 쇼핑몰 상품 페이지입니다. 가격, 재고, 카테고리 순서는 몇 분마다 바뀔 수 있지만 대부분의 방문자는 빠른 정적 응답을 받아야 합니다. 이런 경우 ISR이 잘 맞습니다. 공식 Next.js ISR 가이드는 ISR이 Node.js runtime에서 지원되고 정적 export에서는 지원되지 않는다고 설명합니다.
세 번째 사례는 회원 대시보드입니다. Cookie, 권한, 결제 상태, 읽지 않은 알림, 개인 추천은 요청마다 달라집니다. SSR로 보고 캐시 헤더를 명확히 해야 합니다. 동적 페이지를 edge에 둘지 검토한다면, 전략을 정한 뒤 Claude Code edge computing을 함께 확인하세요.
Next.js SSR 예제
아래 코드는 Next.js App Router 프로젝트에 그대로 붙여 넣어 실행할 수 있는 형태입니다. dummyjson.com을 사용하므로 API key가 필요 없습니다. connection()과 cache: "no-store"를 함께 써서 요청 시점 동작을 리뷰에서 분명히 보여 줍니다.
// app/products/[id]/page.tsx
import { notFound } from "next/navigation";
import { connection } from "next/server";
type Product = {
id: number;
title: string;
price: number;
stock: number;
updatedAt: string;
};
async function getProduct(id: string): Promise<Product | null> {
await connection();
const res = await fetch(`https://dummyjson.com/products/${id}`, {
cache: "no-store",
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to load product: ${res.status}`);
const data = (await res.json()) as {
id: number;
title: string;
price: number;
stock: number;
meta?: { updatedAt?: string };
};
return {
id: data.id,
title: data.title,
price: data.price,
stock: data.stock,
updatedAt: data.meta?.updatedAt ?? new Date().toISOString(),
};
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<main>
<h1>{product.title}</h1>
<p>Price: ${product.price.toLocaleString("en-US")}</p>
<p>Stock: {product.stock}</p>
<p>Updated: {new Date(product.updatedAt).toLocaleString("ko-KR")}</p>
</main>
);
}
가장 흔한 함정은 cookies()나 headers()를 공유 layout에 넣는 것입니다. 테마나 지역을 읽는 작은 코드처럼 보여도 요청 시점 API가 들어가면 여러 route가 동적 렌더링으로 기울 수 있습니다. 글 페이지까지 같은 layout을 쓰면 SSG로 충분한 페이지가 영향을 받습니다. Claude Code에는 변경 전에 영향받는 route 목록을 먼저 요구하세요.
SSG, ISR, 정적 Export 구분하기
SSG와 ISR은 방문자에게 둘 다 정적으로 느껴질 수 있지만 운영 방식은 다릅니다. SSG는 빌드할 때 다시 만듭니다. ISR은 시간 또는 이벤트 후 다시 만듭니다. 완전한 정적 배포는 output: "export"로 out 폴더를 만들고 Node.js 서버 없이 배포합니다. 공식 Static Exports는 next build가 route별 HTML을 만들 수 있다고 설명합니다.
// app/catalog/[id]/page.tsx
import { notFound } from "next/navigation";
export const revalidate = 3600;
type Product = {
id: number;
title: string;
description: string;
};
export async function generateStaticParams() {
return ["1", "2", "3"].map((id) => ({ id }));
}
async function getProduct(id: string): Promise<Product | null> {
const res = await fetch(`https://dummyjson.com/products/${id}`, {
next: { revalidate: 3600, tags: [`product:${id}`] },
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to load product: ${res.status}`);
return (await res.json()) as Product;
}
export default async function CatalogPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<article>
<h1>{product.title}</h1>
<p>{product.description}</p>
</article>
);
}
// next.config.mjs
const nextConfig = {
output: "export",
images: {
unoptimized: true,
},
};
export default nextConfig;
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export:check": "next build && npx serve out"
}
}
정적 export와 ISR을 같은 것으로 생각하면 안 됩니다. 정적 export는 정적 호스팅을 위한 선택입니다. Cookie, on-demand revalidation, Server Function, 진짜 SSR이 필요하면 서버 runtime을 계획해야 합니다. Claude Code가 코드를 쓰기 전에 Cloudflare Pages 정적 호스팅인지, Node 컨테이너인지, Vercel인지, Netlify인지 먼저 알려 주세요.
Astro의 정적 생성과 On-Demand Rendering
Astro는 기본적으로 정적 생성에 가깝습니다. 공식 Astro on-demand rendering 문서는 페이지, route, API endpoint가 기본적으로 빌드 시점에 prerender 되며, adapter를 추가한 뒤 특정 route에서 export const prerender = false로 요청 시점 렌더링을 선택할 수 있다고 설명합니다.
---
// src/pages/docs/[slug].astro
export async function getStaticPaths() {
const docs = [
{ slug: "ssr", title: "SSR guide" },
{ slug: "ssg", title: "SSG guide" },
];
return docs.map((doc) => ({
params: { slug: doc.slug },
props: { doc },
}));
}
const { doc } = Astro.props;
---
<html lang="ko">
<body>
<article>
<h1>{doc.title}</h1>
<p>This page was generated at build time.</p>
</article>
</body>
</html>
---
// src/pages/account.astro
export const prerender = false;
const session = Astro.cookies.get("session")?.value;
const name = session ? "Masa" : "Guest";
Astro.response.headers.set("Cache-Control", "private, no-store");
---
<html lang="ko">
<body>
<h1>Account</h1>
<p>Hello, {name}.</p>
</body>
</html>
Astro에서 자주 나는 실수는 adapter를 잊는 것입니다. 로컬에서는 그럴듯해 보여도 production에서 on-demand rendering을 하려면 서버 runtime이 필요합니다. Claude Code에는 astro.config.mjs, 배포 플랫폼, 모든 prerender 설정을 함께 점검하게 하세요.
검증 명령과 Claude Code 프롬프트
렌더링 전략은 코드만 보고 확정할 수 없습니다. 빌드 결과, 응답 헤더, cache 로그, 성능 지표를 확인해야 합니다. Next.js ISR 공식 가이드도 next build와 next start로 production behavior를 확인하라고 안내합니다.
# Next.js: production behavior
npm run build
npm run start
# In another terminal
curl -I http://localhost:3000/catalog/1
curl -I http://localhost:3000/products/1
# ISR cache debugging
NEXT_PRIVATE_DEBUG_CACHE=1 npm run start
# Astro
npm run build
npm run preview
Claude Code에는 코드만 요청하지 말고 판단 기록을 함께 요청하세요.
Goal: classify every route in a Next.js/Astro site as SSR, SSG, ISR, or static export.
Inspect:
- app/ or src/pages/ routes
- cookies(), headers(), searchParams, connection(), cache: "no-store"
- generateStaticParams(), revalidate, output: "export", Astro prerender
- monetization pages, member pages, articles, product pages, and search pages
Constraints:
- Do not break SEO metadata or internal links
- Keep npm run build passing before and after
- Return a table with the chosen strategy, reason, touched files, and verification command
구체적인 함정
첫째, 사용자별 콘텐츠를 정적 글 본문에 넣는 것입니다. 회원 이름, 개인 추천, 비공개 가격은 SSG HTML에 들어가면 안 됩니다. 본문은 정적으로 두고 개인화는 클라이언트 컴포넌트나 명확한 동적 영역으로 분리합니다.
둘째, ISR 시간을 너무 짧게 잡는 것입니다. revalidate = 1은 비용을 다시 서버로 되돌리는 경우가 많습니다. 정확한 갱신이 필요하면 on-demand revalidation을 쓰고, 실시간 데이터가 필요하면 SSR을 선택합니다.
셋째, 정적 export를 선택한 뒤 서버 기능을 추가하는 것입니다. 인증, Cookie 기반 UI, Server Route Handler, ISR은 runtime이 필요합니다. 정적 호스팅은 훌륭하지만 서버의 대체품은 아닙니다.
넷째, Claude Code의 “빨라 보이는” 제안을 바로 받아들이는 것입니다. 수익형 글 사이트에서는 성능만으로 품질이 끝나지 않습니다. OGP, 구조화 데이터, 내부 링크, 광고 위치, CTA 측정, LCP, CLS까지 봐야 합니다. 전략을 고른 뒤에는 Claude Code 성능 최적화를 기준으로 점검하세요.
수익 흐름까지 포함한 결론
ClaudeCodeLab 같은 콘텐츠 비즈니스에서는 글, 튜토리얼, 비교 글, 상단 퍼널 페이지는 기본적으로 SSG가 맞습니다. 상품 목록, 카테고리, 뉴스성 페이지는 ISR을 사용합니다. 회원 페이지, 검색, 장바구니, 결제 주변 UI, 개인 요청 데이터가 들어가는 곳은 SSR로 둡니다.
일상적인 작업 흐름부터 정리하려면 무료 Claude Code 치트시트로 시작하세요. 구현 템플릿과 운영 자료가 필요하다면 제품 페이지를 확인할 수 있습니다. 팀 단위로 route 전략, 권한, 코드 리뷰, 분석, 배포 교육을 맞추려면 Claude Code 교육 및 상담이 다음 단계입니다.
실제로 확인한 결과
이번 업데이트에서는 Next.js Dynamic Route Segments, connection, ISR, Static Exports, Astro on-demand rendering, Claude Code overview를 확인했습니다. Masa의 콘텐츠 운영에서는 글은 SSG, 상품과 카테고리 목록은 ISR, 계정과 검색은 SSR로 나누는 방식이 가장 안정적이었습니다. 가장 효과가 컸던 Claude Code 사용법은 코드 변경 전 route 분류표를 만들게 한 것입니다. 이 표 덕분에 정적 페이지에 Cookie 처리가 섞이는 문제를 빨리 발견했고, 각 route의 이유와 검증 명령도 리뷰에서 바로 확인할 수 있었습니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.