Next.js 15에서 SSR·SSG·ISR은 dynamic으로 결정되고, fetch가 보완한다

김현준·2025년 7월 19일
0

넥스트JS 이모저모

목록 보기
20/23

Next.js 15부터는 getStaticProps, getServerSideProps가 사라지고,
정적/동적 렌더링 전략은 dynamicfetch의 조합으로 결정된다.

  • 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로 렌더링

상황별 Q&A

Q1. fetch 없는 컴포넌트는 어떻게 렌더링될까?

별도 dynamic 설정이 없고, fetch, cookies(), headers() 같은 동적 동작도 없다면
기본값 'auto'에 의해 정적(SGG) 으로 처리된다.

즉, 빌드 시점에 컴포넌트를 렌더링하여 HTML과 JSON으로 변환하고,
이 결과물을 CDN에 정적으로 배포한다.

→ 사용자가 접속할 때마다 서버가 다시 실행되지는 않으며,
→ 이미 만들어진 정적 HTML이 그대로 반환된다.

참고: fetch는 단순한 데이터 호출이 아니다.
어떤 캐시 옵션을 쓰느냐에 따라 SSR이나 ISR을 유도할 수 있다.


Q2. SSG면, 서버가 필요한 컴포넌트만 조립해서 보내주는 건가?

아니다. Next.js의 SSG는 "사용자가 요청할 때마다 조립하는 방식"이 아니라,
"빌드할 때 전체 페이지를 미리 만들어 두는 방식"
이다.

더 쉽게 설명하면:

  • SSG는 웹사이트를 미리 만들어 정적으로 보관해 두는 개념이다.
  • 즉, 페이지는 요청이 올 때마다 서버에서 컴포넌트를 실행해서 만들지 않는다.
  • 오히려, 빌드할 때 전체 컴포넌트를 실행해서 완성된 HTML과 JSON을 만들어 놓고,
    이를 CDN에 저장해 둔다.

실제 동작 흐름:

  1. dynamic = 'auto'이고 fetch도 없으면 SSG로 판단됨
  2. Next.js는 페이지 전체를 빌드 시점에 실행해서 → 정적 HTML과 JSON으로 미리 생성
  3. 사용자가 페이지를 요청하면 → 서버는 실행되지 않고 그 정적 결과물만 전달
  4. 브라우저는 그걸 받아서 React가 hydration을 통해 인터랙션만 붙임

요약: SSG는 서버가 요청마다 조립해주는 게 아니라
아예 미리 만들어서 그대로 꺼내주는 것이다.


Q3. dynamic = 'force-static'인데 자식이 fetch(..., { revalidate: 60 })라면?

무시된다.

  • 부모가 force-static이면 하위 fetch도 무조건 정적으로 고정됨
  • revalidate 옵션이 있어도 한 번만 fetch되고, 이후 재검증되지 않음
  • revalidateTag, revalidatePath 같은 수동 캐시 무효화도 동작하지 않음

즉, force-static전체를 완전히 SSG로 고정하는 명령이므로
그 아래 어떤 동적 요청이 있더라도 렌더링 전략이 변경되지 않는다

해결법

  1. 부모 컴포넌트를 dynamic = 'auto'로 변경
  2. 또는 자식 컴포넌트를 다른 라우트 세그먼트로 분리하여 revalidate가 적용되도록 한다

Q4. 그럼 각 컴포넌트마다 dynamic을 다 설정해야 하나?

아니다. 대부분의 경우 dynamic = 'auto' (기본값)로 충분하다.
Next.js가 내부적으로 fetch, cookies, headers 등의 사용 여부를 판단해
정적으로 만들 수 있으면 SSG로 처리하고, 그렇지 않으면 SSR로 전환한다.

하지만 다음과 같은 경우에는 명시적으로 설정해주는 게 좋다:

상황추천 설정
로그인 유저 정보 필요 (SSR)dynamic = 'force-dynamic'
마케팅/문서 페이지 (완전 정적)dynamic = 'force-static'
동적 요소 방지 및 실수 방지용dynamic = 'error'

Q5. force-static이면, 다른 페이지 갔다가 돌아왔을 때 서버에 다시 요청하나?

아니다. Next.js는 CSR(Client-Side Rendering) 전환 이후,
해당 정적 페이지를 메모리에 캐싱하고 다시 서버에 요청하지 않는다.

즉:

  • 사용자가 한번 방문한 정적 페이지는
  • 다시 돌아올 때 Next.js가 메모리에서 즉시 복원한다
  • 이는 성능 개선과 사용자 경험에 매우 효과적이다

dynamicParams 정리

export const dynamicParams = false // 기본값: true
의미
truegenerateStaticParams에 없는 경로는 SSR로 처리됨
false정의되지 않은 경로는 404 에러 반환

→ 기존 getStaticPathsfallback: false 기능과 거의 동일


fetch 캐싱 전략과 dynamic의 관계

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 설정이 하위보다 우선됨에 유의
  • 대부분은 dynamic = 'auto' + 적절한 fetch 옵션 조합으로 충분하다
profile
기록하자

0개의 댓글