Next.js 15부터는 getStaticProps, getServerSideProps가 사라지고,
정적/동적 렌더링 전략은 dynamic과 fetch의 조합으로 결정된다.
dynamic은 페이지 단위에서 SSR/SSG 여부를 지정하고fetch는 데이터의 캐싱 방식과 갱신 주기를 설정해 SSR/ISR 여부에 영향을 준다하지만 이 두 옵션이 서로 얽히는 상황에서는 동작 우선순위나 예외 케이스가 헷갈릴 수밖에 없다.
export const dynamic = 'auto'
// 'auto' | 'force-static' | 'force-dynamic' | 'error'
| 옵션 | 의미 | 
|---|---|
'auto' (기본값) | 자동으로 SSG/SSR 판단 (fetch, cookies 등 사용 여부에 따라) | 
'force-static' | 정적 빌드 강제 (SSG). 쿠키/헤더/동적 fetch 사용 불가 | 
'force-dynamic' | SSR 강제. 매 요청마다 새로 렌더링 | 
'error' | 동적 요소가 있으면 빌드 실패 (SSG만 허용) | 
force-dynamic(SSR)이면, 하위에서 force-static(SSG)을 설정해도 무시되고 전체가 SSR로 렌더링fetch 없는 컴포넌트는 어떻게 렌더링될까?별도
dynamic설정이 없고,fetch,cookies(),headers()같은 동적 동작도 없다면
기본값'auto'에 의해 정적(SGG) 으로 처리된다.
즉, 빌드 시점에 컴포넌트를 렌더링하여 HTML과 JSON으로 변환하고,
이 결과물을 CDN에 정적으로 배포한다.
→ 사용자가 접속할 때마다 서버가 다시 실행되지는 않으며,
→ 이미 만들어진 정적 HTML이 그대로 반환된다.
참고:
fetch는 단순한 데이터 호출이 아니다.
어떤 캐시 옵션을 쓰느냐에 따라 SSR이나 ISR을 유도할 수 있다.
아니다. Next.js의 SSG는 "사용자가 요청할 때마다 조립하는 방식"이 아니라,
"빌드할 때 전체 페이지를 미리 만들어 두는 방식"이다.
dynamic = 'auto'이고 fetch도 없으면 SSG로 판단됨요약: SSG는 서버가 요청마다 조립해주는 게 아니라
아예 미리 만들어서 그대로 꺼내주는 것이다.
dynamic = 'force-static'인데 자식이 fetch(..., { revalidate: 60 })라면?무시된다.
force-static이면 하위 fetch도 무조건 정적으로 고정됨revalidate 옵션이 있어도 한 번만 fetch되고, 이후 재검증되지 않음revalidateTag, revalidatePath 같은 수동 캐시 무효화도 동작하지 않음즉, force-static은 전체를 완전히 SSG로 고정하는 명령이므로
그 아래 어떤 동적 요청이 있더라도 렌더링 전략이 변경되지 않는다
dynamic = 'auto'로 변경revalidate가 적용되도록 한다dynamic을 다 설정해야 하나?아니다. 대부분의 경우
dynamic = 'auto'(기본값)로 충분하다.
Next.js가 내부적으로fetch,cookies,headers등의 사용 여부를 판단해
정적으로 만들 수 있으면 SSG로 처리하고, 그렇지 않으면 SSR로 전환한다.
하지만 다음과 같은 경우에는 명시적으로 설정해주는 게 좋다:
| 상황 | 추천 설정 | 
|---|---|
| 로그인 유저 정보 필요 (SSR) | dynamic = 'force-dynamic' | 
| 마케팅/문서 페이지 (완전 정적) | dynamic = 'force-static' | 
| 동적 요소 방지 및 실수 방지용 | dynamic = 'error' | 
force-static이면, 다른 페이지 갔다가 돌아왔을 때 서버에 다시 요청하나?아니다. Next.js는 CSR(Client-Side Rendering) 전환 이후,
해당 정적 페이지를 메모리에 캐싱하고 다시 서버에 요청하지 않는다.
즉:
export const dynamicParams = false // 기본값: true
| 값 | 의미 | 
|---|---|
true | generateStaticParams에 없는 경로는 SSR로 처리됨 | 
false | 정의되지 않은 경로는 404 에러 반환 | 
→ 기존 getStaticPaths의 fallback: false 기능과 거의 동일
| fetch 옵션 | 결과적 렌더링 | 
|---|---|
{ cache: 'no-store' } | SSR (매 요청마다 새로 fetch) | 
{ next: { revalidate: 60 } } | ISR (60초마다 재생성) | 
| 기본 fetch (force-cache 상태) | SSG (정적 캐시) | 
단, 부모 컴포넌트가
dynamic = 'force-static'이면
이 모든revalidate설정도 무시됨
| 상황 | 렌더링 결과 | 
|---|---|
fetch 없음, 설정 없음 | SSG | 
fetch(..., no-store) | SSR | 
dynamic = 'force-dynamic' | 무조건 SSR | 
dynamic = 'force-static' + revalidate | ❌ 무시됨, 고정 SSG | 
자식 fetch revalidate: 60, 부모 force-static 설정 | ❌ 자식도 정적 처리됨 | 
| CSR 이동 후 복귀 | 메모리 캐시에서 복원 | 
dynamic은 페이지/레이아웃 단위에서만 설정 가능 (컴포넌트 내부에선 불가)fetch는 SSR/ISR/SSG를 간접적으로 유도하는 핵심 트리거dynamic = 'auto' + 적절한 fetch 옵션 조합으로 충분하다