| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- python Django
- 강제 타입변환
- 파이썬 제어문
- Python
- Kotlin If
- 도전
- Kotlin 클래스 속성정의
- 파이썬 장고
- 넥스트js
- NextJs
- Kotlin 조건문
- 클래스 속성
- 자바 기본타입
- github
- 파이썬
- git
- Variable declaration
- 희망
- activate 오류
- Kotlin Class
- Kotlin 클래스
- 성공
- 다중조건문
- Kotlin else if
- 좋은글
- Python Class
- django virtualenv
- 장고 가상환경
- 파이썬 클래스
- 파이썬 반복문
- Today
- Total
키모스토리
#33. Redering section Summary 본문
Next.js(App Router) 렌더링 완전 정리 (RSC 중심) — Static/Dynamic/Streaming/컴포지션 패턴까지 (v16 기준)
이 글은 Next.js App Router(최신 v16 계열) 기준으로, “왜 이 페이지는 즉시 뜨지?”, “어떤 순간에 동적 렌더링이 되지?”, “RSC에서 Client/Server 컴포넌트는 어떻게 섞어야 하지?” 같은 렌더링 관련 핵심을 한 번에 정리한 포스팅입니다.
1) RSC(React Server Components) 관점에서 “렌더링”을 다시 보기
App Router에서 page / layout은 기본이 Server Component입니다. 즉, “일단 서버에서 렌더링 가능한 건 서버에서” 처리하고, 필요한 부분만 클라이언트로 내려보내는 구조가 기본값입니다. Next.js
요청 1회에 내부적으로 벌어지는 일(개념)
서버에서 Next.js는 라우트 세그먼트(레이아웃/페이지) 단위로 렌더링을 “조각”내서 처리합니다.
- Server Component 트리 → RSC Payload(특수 바이너리 포맷) 생성
- Client Component + RSC Payload → HTML 프리렌더(초기 화면용) 생성 Next.js
클라이언트에서는:
- HTML로 빠르게(비인터랙티브) 화면을 먼저 보여주고
- RSC Payload로 Server/Client 트리를 합쳐(리컨실)
- Client Component만 하이드레이션해서 인터랙션을 붙입니다. Next.js
핵심: “모든 걸 CSR로 보내서 브라우저에서 렌더”가 아니라, 서버가 주도하고 클라는 필요한 상호작용만 책임지는 것이 기본 철학입니다.
2) Static Rendering vs Dynamic Rendering (라우트가 ‘언제’ 만들어지나)
Next.js의 “렌더링 전략”은 결국 HTML이 언제 생성되느냐의 문제입니다.
Static Rendering (정적 렌더링)
- 빌드 시점(또는 revalidate 후 백그라운드) 에 라우트가 렌더되고 결과가 캐시됩니다.
- 캐시된 결과는 여러 요청에서 재사용됩니다. Next.js+1
Dynamic Rendering (동적 렌더링)
- **요청 시점(request time)**에 라우트가 렌더됩니다.
- 보통 “요청자마다 달라질 수 있는 정보”를 쓰는 순간 동적이 됩니다. Next.js+1
라우트가 Dynamic이 되는 대표 트리거(공식 문서 기준):
- cookies(), headers(), connection(), draftMode()
- searchParams(prop)
- unstable_noStore()
- fetch({ cache: 'no-store' }) Next.js
3) “Static/Dynamic을 내가 통제하고 싶다” — Route Segment Config 핵심
App Router에서는 “페이지 전체를 SSG/SSR로 딱 잘라” 제어하기보다는, fetch 단위의 캐싱 제어를 더 선호합니다. 다만, 마이그레이션/편의 목적으로 페이지 레벨 옵션도 제공합니다. Next.js
export const dynamic
export const dynamic = 'auto'
// 'auto' | 'force-dynamic' | 'error' | 'force-static'
- 'auto'(기본): 가능한 캐시를 많이 하되, 필요하면 동적으로도 동작
- 'force-dynamic': 항상 요청 시점 렌더링(실질적으로 모든 fetch를 no-store처럼) Next.js
- 'error': 강제 정적. 동적 API나 uncached를 쓰면 에러로 막음 Next.js
- 'force-static': 강제 정적(쿠키/헤더/서치파람을 “빈 값”처럼 처리) Next.js
개발 모드에서 “캐시가 안 먹는 것처럼 보이는” 이유
개발 모드에서는 페이지가 항상 on-demand로 렌더되고 캐시되지 않습니다. 그래서 수정 사항이 바로 보이고, revalidate 기다릴 일도 없습니다. Next.js
4) Streaming(스트리밍 렌더링)과 loading.tsx, Suspense
스트리밍의 목표는 간단합니다:
“느린 데이터 때문에 전체 화면이 막히지 않게, 준비된 UI부터 먼저 보여주자”
App Router는 스트리밍을 지원하고, 라우트에 loading.tsx를 두면 로딩 UI를 쉽게 구성할 수 있습니다.
그리고 중요한 현실 팁 1개:
- 일부 브라우저는 스트리밍 응답을 버퍼링해서 응답이 1024 bytes를 넘기 전까지 화면에 표시가 안 될 수 있습니다.
(Hello world 급으로 단순한 페이지에서 특히 체감됩니다) Next.js
5) Server/Client Components “섞어 쓰기(Interleaving)” 규칙과 베스트 패턴
(1) Client Component 안에서 다른 Client Component 사용
가능합니다. 'use client'가 선언된 파일 안에서 하위 컴포넌트는 자연스럽게 클라이언트 번들 트리에 포함됩니다. Next.js
(2) Client Component 안에서 Server Component를 “직접 import”하고 싶을 때?
불가능(Unsupported Pattern) 입니다.
Client Component 모듈에서 Server Component를 import하는 건 지원되지 않습니다. Next.js
✅ 대신, **children(슬롯)로 Server Component를 “전달받아 렌더”**하는 패턴이 공식 권장입니다.
Client 컴포넌트(슬롯 제공)
'use client'
import { useState } from 'react'
export function Modal({ children }: { children: React.ReactNode }) {
const [open, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen((v) => !v)}>Toggle</button>
{open ? <div className="modal">{children}</div> : null}
</>
)
}
import { Modal } from './Modal'
import ServerCart from './ServerCart' // 서버 컴포넌트
export default function Page() {
return (
<Modal>
<ServerCart />
</Modal>
)
}
이 패턴이 가능한 이유/동작 방식(요약):
- 새 요청이 생기면 항상 Server Components가 먼저 렌더
- 그 결과(RSC Payload)에 Client Component 자리(참조)가 포함됨
- 이후 클라이언트가 이를 조합하여 최종 트리를 구성 Next.js+1
(3) Server Component 내에서 다른 Server Component 사용
당연히 가능합니다(서버 트리 내부 조합). Next.js
(4) Server Component 내에서 Client Component 사용
가능합니다. 다만 그 Client 컴포넌트 아래 서브트리는 클라이언트 번들 대상이므로, 번들 크기를 생각해 “클라이언트를 아래로 내리는” 패턴이 권장됩니다. Next.js+1
6) Client Component Placement(“클라 컴포넌트는 가능한 아래로”)
큰 레이아웃을 통째로 'use client'로 만들면, 정적 UI까지 전부 클라이언트 번들에 들어가서 손해가 큽니다.
공식 권장:
- 상태/이벤트가 필요한 작은 부분만 Client Component로 분리
- 레이아웃/페이지는 가능한 Server Component 유지 Next.js+1
7) Props 직렬화(Serialization) 제약
Server → Client로 props를 넘길 때는 React가 직렬화 가능한 값이어야 합니다.
직렬화가 안 되는 값(예: 함수, 클래스 인스턴스 등)이 필요하면:
- 클라이언트에서 다시 fetch(SWR 등)하거나
- Route Handler를 통해 받아오는 방식 등을 고려합니다. Next.js
8) import "server-only" / import "client-only" (경계 실수 방지)
서버 전용 코드(토큰/DB 접근/비밀키)가 실수로 클라이언트 번들로 섞이는 것을 막기 위해:
- 서버 전용 모듈에서 import 'server-only'
- 클라이언트 전용 모듈에서 import 'client-only'
같은 가드 패턴을 사용합니다. Next.js
9) generateStaticParams + dynamicParams (동적 라우트의 정적 생성 제어)
generateStaticParams
동적 세그먼트(예: /products/[id])에서 빌드 시점에 정적으로 생성할 파라미터 목록을 정의합니다.
또한 generateStaticParams는 'use client' 파일 안에서는 사용할 수 없습니다. Next.js
dynamicParams
generateStaticParams에 포함되지 않은 파라미터로 접근했을 때의 동작을 제어합니다.
export const dynamicParams = true // true | false
- true(기본): 목록 밖의 params는 온디맨드로 생성
- false: 목록 밖의 params는 404 반환 Next.js
그리고 이 옵션은 pages 라우터의 getStaticPaths에서 쓰던 fallback 개념을 대체합니다. Next.js
10) (질문에서 가장 많이 나오는 포인트) “build → start 후엔 왜 지연이 안 보이나요?”
사용자 예시:
- Product 컴포넌트 2초 지연
- Reviews 컴포넌트 4초 지연
- 그런데 next build → next start 후 /product-reviews 접속하면 즉시 표시
이 현상은 정상일 가능성이 큽니다.
이유: Production에서 해당 라우트가 “정적(Static)”으로 프리렌더 되었기 때문
정적 렌더링이면,
- 지연(setTimeout)이 “요청 시점”이 아니라 “빌드 시점”에 이미 실행되고
- 결과 HTML이 캐시/저장되어
- 런타임 요청에서는 이미 만들어진 결과물을 바로 서빙합니다. Next.js
✅ 확인/재현 팁
- next dev에서는 on-demand 렌더링이라 지연이 보이는 편입니다(개발 모드 캐시 없음). Next.js
- next start에서 “요청마다 지연을 보고 싶다”면:
또한 스트리밍을 테스트 중인데 “로딩이 바로 안 보인다”면, 브라우저 1024 bytes 버퍼링 이슈도 함께 체크하셔야 합니다. Next.js
11) 운영 관점: build → start로 올린 뒤 수정사항이 즉시 반영되나요?
- next start는 프로덕션 모드 서버이고, 먼저 next build로 빌드가 되어 있어야 합니다. Next.js
- 소스 수정이 있으면, 일반적으로 다시 build 후 재시작해야 결과가 반영됩니다.
- 반대로 개발 중 즉시 반영(HMR)은 next dev의 역할입니다. Next.js
12) npm run build 출력에서 Route 정보가 간단하게만 보이는 경우(상세 옵션)
Next.js CLI 문서 기준으로:
- next build는 라우트별 정보를 출력합니다. Next.js
- 더 “verbose”한 빌드 출력이 필요하면 --debug 옵션을 사용합니다(추가 빌드 출력 표시). Next.js
- 번들 구성을 더 깊게 보고 싶으면 next experimental-analyze(Turbopack analyzer)도 선택지입니다. Next.js
마무리 체크리스트 (현업에서 자주 쓰는 판단 기준)
- 이 UI는 상호작용/브라우저 API/useEffect가 필요한가? → Client Component Next.js
- 나머지는 가능한 Server Component로 유지해서 JS 번들을 줄였는가? Next.js+1
- 라우트가 요청마다 달라야 하나?
- cookies/headers/searchParams/no-store fetch 등을 쓰면 Dynamic이 됩니다. Next.js
- generateStaticParams + dynamicParams로 “정적 생성 범위/404 정책”을 의도대로 잡았는가? Next.js
- Client에서 Server 컴포넌트를 쓰려 했는가?
- import는 금지, children/props 슬롯 패턴을 사용했는가? Next.js+1
'Web Devlopment > NextJs' 카테고리의 다른 글
| #32. Interleaving Server and Client Components (0) | 2025.12.30 |
|---|---|
| #31. 서드파티 UI 패키지, Context Providers (0) | 2025.12.30 |
| #30. Streaming, Server/Client Composition Patterns (0) | 2025.12.30 |
| #29. generateStaticParams / dynamicParams (0) | 2025.12.30 |
| #28. RSC 렌더링과 Static/Dynamic Rendering (0) | 2025.12.30 |
