Next.js 헷갈렸던 개념정리

JT·2026년 1월 14일
post-thumbnail

Next.js를 학습하면서 헷갈렸던 개념에 대해 정리하고자한다.

먼저, SSR/SSG/ISR 페이지라는 구분이다.
특히 SSR 페이지 라는 표현은 실제 동작 방식과 다른 인상을 주기 때문에 오해가 자주 생긴다.

다음으로, Page Router와 App Router 모두 SSR과 Hydration 차이점이다.
Page Router와 App Router 모두 SSR과 Hydration이 발생할 수 있지만, 그 범위와 처리 방식은 다르다.

먼저 SSR/SSG/ISR이 왜 혼동을 유발하는지 정리하고,
이어서 Page Router와 App Router의 SSR/Hydration 범위와 처리 방식 차이를 비교해 보겠다.

1. Next.js에서 헷갈리는 페이지 구분 개념

과거 Next.js에서는 페이지를 다음과 같이 구분했다.

  • SSR 페이지
  • SSG 페이지
  • ISR 페이지

하지만 이 구분은 렌더링 방식의 차이라기보다 캐싱 전략의 차이에 가까웠다.

SSG/ISR/SSR 모두 서버에서 HTML을 만들 수 있다.
SSG/ISR도 서버에서 HTML을 생성하지만, SSR처럼 매 요청마다 생성하는 게 아니라 빌드 시(또는 재검증 시) 생성한 결과를 캐시/재사용한다.

구분언제 HTML을 만드느냐얼마나 오래 캐시하느냐
SSG빌드 시 1회영구
ISR빌드 시 + 일정 주기재검증
SSR요청 시 매번캐시 없음

그런데 이름이 “SSR 페이지”이다 보니
“SSR만 서버에서 렌더링하고, SSG/ISR은 다른 방식이다”라고 오해하기 쉽다.
하지만 실제론 SSG/ISR도 서버에서 HTML을 만든다.
다만 SSG/ISR은 ‘한 번 만들어 둔 결과를 재사용’하는 쪽이고, SSR은 ‘요청마다 다시 만든다’는 쪽이다.

그래서 App Router에서는 이 혼동을 줄이기 위해 용어를 이렇게 정리했다.

App Router 용어의미
Dynamic Page요청마다 새로 렌더링되는 페이지
Static Page캐시(full route cache)된 HTML을 사용하는 페이지 (SSG/ISR)

App Router에서는 “SSR/SSG/ISR” 대신
“Dynamic / Static”이라는 캐싱 관점의 용어로 정리했다.


2. Page Router와 App Router의 SSR 및 Hydration 차이점

Page Router와 App Router 모두 서버에서 HTML을 생성(SSR)한다는 점은 동일하다.
하지만 두 방식은 SSR 책임 분리 구조와 hydration 처리 방식에서 근본적인 차이가 있다.

Page Router의 SSR + Hydration 구조

Page Router는 Server / Client 컴포넌트 구분이 없다.
즉, 모든 컴포넌트는 서버에서도 실행될 수 있고, 클라이언트에서도 다시 실행될 수 있다.

서버에서 하는 일

  • React 컴포넌트 실행
  • getServerSideProps, getStaticProps 실행
  • 데이터 패칭
  • HTML 생성
  • 패칭 결과를 __NEXT_DATA__에 직렬화

클라이언트에서 하는 일

  • 전체 JS 번들 로드
  • __NEXT_DATA__를 통해 서버 데이터 복원
  • React 트리를 처음부터 다시 구성
  • 기존 HTML과 비교하여 페이지 전체를 한 번에 hydration

브라우저에 전달되는 구조

  • HTML
  • 전체 클라이언트 번들 (전체 컴포넌트 트리 코드)
  • NEXT_DATA

특징

  • React 컴포넌트 트리 전체가 클라이언트로 전달됨
  • hydration 단위는 “페이지 전체”
  • 서버 전용 코드가 실수로 번들에 포함될 위험 존재

Hydration 특징

  • hydration 단위: 페이지 전체
  • 모든 컴포넌트가 hydration 대상
  • JS 로딩이 끝나야 상호작용 가능
  • 초기 JS 비용이 큼

Page Router는
서버가 만든 HTML 위에, 클라이언트가 같은 페이지 트리를 다시 실행해 이벤트를 붙이며 ‘페이지 전체’를 한 번에 hydration 한다.

Page Router 전체 처리 과정

1) 요청/빌드 트리거

  • SSR: 매 요청마다 실행
  • SSG: 빌드 시 실행
  • ISR: 빌드 시 실행 + 재검증 시점에 다시 실행

2) 서버에서 실행

  • 해당 페이지 컴포넌트 렌더링 실행
  • getServerSideProps 또는 getStaticProps 실행
  • 데이터 패칭(외부 API/DB 등)
  • HTML 생성
  • props 결과를 NEXT_DATA에 JSON으로 직렬화해서 HTML에 포함

3) 브라우저로 전달되는 구성

  • HTML
  • 전체 JS 번들(페이지 트리 코드)
  • NEXT_DATA(서버 props/라우팅 정보 등)

4) 클라이언트에서 Hydration

  • 브라우저가 HTML 먼저 표시(초기 화면)
  • JS 번들 다운로드/실행
  • NEXT_DATA를 읽어서 서버 props 복원
  • React가 페이지 트리를 처음부터 다시 구성
  • 기존 HTML에 이벤트 핸들러를 붙이며 페이지 전체 hydration 완료
  • 이후부터 상호작용 가능

App Router의 SSR + Hydration 구조

App Router는 Server Component와 Client Component가 구조적으로 분리됩니다.

서버에서 하는 일

  • Server Component 실행
  • DB 접근, fetch, 비즈니스 로직 처리
  • HTML 생성 (Server Component 결과 + Client Component의 초기 UI 포함)
  • RSC Payload 생성 (서버 컴포넌트 실행 결과)

클라이언트에서 하는 일

  • HTML 즉시 표시
  • RSC Payload로 React 트리 복원
  • Client Component 위치에서만 JS 번들 로드
  • 필요한 부분만 hydration

브라우저에 전달되는 구조

  • HTML
  • RSC Payload (서버 컴포넌트 실행 결과)
  • Client Component 번들

특징

  • 서버 컴포넌트 코드는 클라이언트로 내려오지 않음
  • Client Component만 번들에 포함
  • hydration은 Client Component 단위로 부분적으로 발생
  • 스트리밍, Suspense로 단계적 렌더링 가능
  • DB, fs 같은 서버 전용 코드 사용이 구조적으로 안전

Hydration 특징

  • hydration 단위: Client Component
  • 서버 컴포넌트 영역은 hydration 대상이 아님
  • JS 로딩과 hydration이 스트리밍으로 진행
  • Suspense로 렌더링 경계를 분리 가능

App Router는
서버 컴포넌트는 서버에서만 실행되고 결과(RSC/HTML)만 전달되며, 상호작용이 필요한 ‘클라이언트 컴포넌트’만 부분적으로 hydration 된다.

App Router 전체 처리 과정 (RSC 기반)

1) 요청/캐시 판단
요청이 들어오면 Next.js가 이 라우트가 Static으로 재사용 가능한지 / Dynamic으로 매번 새로 그려야 하는지를 판단한다.

2) 서버에서 실행 (Server Component)

  • 라우트의 Server Component 트리 실행
  • fetch/DB 접근/비즈니스 로직 처리 (서버 전용 코드 안전하게 사용 가능)
  • HTML 생성(초기 렌더 결과)
  • RSC Payload 생성(서버 컴포넌트 실행 결과를 클라이언트가 이어받기 위한 데이터)

3) 브라우저로 전달되는 구성

  • HTML
  • RSC Payload(서버 컴포넌트 결과)
  • Client Component 번들(상호작용 영역만)

4) 클라이언트에서 복원 + 부분 Hydration

  • 브라우저가 HTML을 즉시 표시
  • RSC Payload를 받아 React 트리를 이어서 복원
  • Client Component가 있는 지점에서만 해당 JS 번들을 로드
  • 필요한 부분만 hydration 진행(부분적/점진적 가능)
  • 인터랙션은 Client Component 단위로 “붙는 즉시” 가능해짐

5) 스트리밍 & Suspense (가능해지는 이유)

  • 서버가 HTML/RSC Payload를 조각(스트림)으로 보내면
  • 클라이언트는 도착한 것부터 화면에 반영하고
  • Suspense 경계로 로딩 UI를 구획화할 수 있다.

마무리

용어의 재정의 (SSR/SSG/ISR → Dynamic/Static)

  • 과거의 SSR/SSG/ISR 구분은 "렌더링 방식"이라기보다 "캐싱 시점과 전략"의 차이였습니다.
  • App Router는 이를 직관적으로 Static(캐시됨)과 Dynamic(요청 시 생성)으로 용어를 정리하여 혼동을 줄였습니다.

Page Router App Router SSR 차이

구분Page RouterApp Router
렌더링 단위페이지 (Page)컴포넌트 (Server / Client)
Hydration전체 (Full) 페이지 로드 후 전체 JS 실행부분 (Partial) Client Component만 실행
JS 번들 크기큼 (모든 컴포넌트 코드 포함)작음 (Client Component 코드만 포함)
데이터 전송NEXT_DATA (JSON)RSC Payload (직렬화된 트리)
주요 특징설정이 단순하나 최적화 한계 존재"Streaming, Suspense 등 정교한 제어 가능"
profile
함께 개선하는 프론트엔드 개발자

0개의 댓글