왜 Next.js와 Server Components 인가
클라이언트 사이드 리액트의 한계와 Server Components가 풀어내는 문제, CSR · SSR · RSC의 차이를 정리합니다. 이 책 전체의 회전점이 되는 챕터입니다.
21장까지로 3부 (TypeScript와 함께)가 마무리됐습니다. 1부에서 컴포넌트 / props / state / 이벤트 / 폼을, 2부에서 effect / Context / 훅 / 성능 / 라우팅을, 3부에서 모든 코드의 타입 안전망을 입혔습니다. 본 챕터부터 4부가 시작됩니다. 이 책의 전환점이 되는 부입니다.
지금까지 본 모든 리액트 코드는 클라이언트 사이드였습니다. 21장 마지막 절에서 미리 본 RSC 모델, 즉 서버에서 직접 페칭하고 클라이언트의 useEffect + fetch가 거의 사라지는 모델의 배경을 본 챕터에서 한 번 정리하겠습니다. 코드는 거의 없습니다. 왜 새 모델이 필요한지, 그게 무엇을 푸는지를 먼저 명확히 해 두는 게 다음 23~27장 이해에 결정적이기 때문입니다.
CSR — 우리가 지금까지 해 온 방식 #
이 책의 1~3부에서 만든 모든 리액트 앱은 CSR (Client-Side Rendering) 방식이었습니다.
1. 브라우저가 빈 HTML 받음 (<div id="root"></div>만 있는 껍데기)
2. 자바스크립트 번들 다운로드
3. 자바스크립트 실행 → 리액트가 화면을 그림
4. 필요하면 fetch로 데이터 가져옴 → 다시 그림이 방식의 장점은 명확합니다.
- 한 번 로드된 후 페이지 전환이 빠르다 (15장에서 다룬 SPA 이점)
- 서버는 정적 파일만 호스팅하면 된다 (배포 단순)
- 인터랙션이 풍부한 UI 구현에 강하다
그런데 단점도 분명합니다.
단점 1. 첫 화면이 늦게 보임 #
브라우저가 빈 화면을 받고, 자바스크립트를 다운받고, 실행하고, 그제서야 화면이 그려집니다. 인터넷이 느리거나 디바이스가 약하면 흰 화면이 오래 보입니다. 31장(성능·번들·Web Vitals)에서 본격적으로 다룰 LCP(Largest Contentful Paint) 지표가 직격탄을 맞는 지점입니다.
단점 2. SEO가 어렵다 #
검색 엔진의 크롤러는 자바스크립트를 안 돌리거나 늦게 돌립니다. 빈 HTML만 보면 본문을 인덱싱하지 못해 검색 노출이 약해집니다.
단점 3. 자바스크립트 번들이 커진다 #
화면을 그리기 위한 모든 코드 (컴포넌트, 라이브러리, 데이터 처리 로직)가 클라이언트로 다운로드돼야 합니다. 라이브러리 하나 추가할 때마다 사용자가 받는 용량도 커집니다.
단점 4. 데이터 페칭이 워터폴 #
서버 응답 → JS 다운로드 → JS 실행 → fetch → 또 fetch. 단계마다 기다림이 누적됩니다. 21장 (fetch와 API 응답 타이핑)에서 만든 useFetch 패턴이 사실 이 워터폴의 한 단면을 보여 줍니다.
SSR — 서버에서 HTML을 미리 만들기 #
이 문제들을 풀기 위해 고전적으로 쓰인 방식이 **SSR (Server-Side Rendering)**입니다. PHP나 Ruby on Rails 같은 전통적인 웹 서버 모델로 돌아가는 방식입니다.
1. 브라우저가 페이지 요청
2. 서버에서 React를 실행해 HTML을 만듦
3. 완성된 HTML을 보냄 (콘텐츠가 이미 들어있음)
4. 브라우저가 그 HTML을 표시
5. 동시에 JS 번들도 받아 인터랙션을 활성화 (hydration)hydration이라는 단어가 등장했는데, “이미 그려진 정적 HTML에 JS로 이벤트 핸들러를 붙여 살아 있는 컴포넌트로 만든다"는 뜻입니다. 마른 HTML에 JS의 물을 부어 살린다는 비유에서 왔습니다.
SSR은 첫 화면 속도와 SEO 문제를 풀어 줍니다. 다만 여전히 모든 컴포넌트 코드가 클라이언트로 전송되어 hydration되어야 하므로, 번들 크기 문제는 그대로 남습니다.
RSC — React Server Components의 등장 #
여기서 React 팀이 한 발 더 나갑니다. “애초에 클라이언트에 갈 필요 없는 컴포넌트도 있지 않을까?”
블로그 글의 본문을 생각해 보세요. 한 번 그려진 후 사용자가 클릭하거나 입력할 게 별로 없습니다. 그저 데이터를 가져와 화면에 출력할 뿐입니다. 이런 컴포넌트라면 서버에서만 실행하고, HTML(또는 그에 가까운 표현)만 클라이언트로 보내면 됩니다. 컴포넌트의 자바스크립트 코드 자체를 클라이언트에 보낼 이유가 없습니다.
이게 **React Server Components (RSC)**의 핵심 아이디어입니다.
1. 브라우저가 페이지 요청
2. 서버에서 Server Components를 실행
3. Server Components는 데이터베이스 / API 직접 호출 가능
4. 결과를 직렬화된 형태로 전송 (HTML + 클라이언트 컴포넌트 위치만 표시)
5. 클라이언트는 받은 HTML을 표시 + Client Components만 hydrateRSC가 풀어내는 것들:
- 번들 크기 절감 — 정적인 컴포넌트들의 코드는 아예 클라이언트로 안 감
- 데이터 페칭이 단순해짐 — Server Component에서 그냥
await db.query(...)하면 됨 (네트워크 왕복 없음) - 민감 정보 보호 — API 키나 DB 자격증명이 클라이언트에 절대 노출 안 됨
- SEO와 첫 화면 속도 — SSR의 장점은 그대로
이 책의 21장 마지막 절에서 본 RSC 모델 미리보기의 배경이 위 흐름입니다.
그래서 무엇이 어디서 실행되는가 #
이 4부에서 계속 등장할 핵심 개념입니다. 머릿속에 미리 그려 두세요.
| 컴포넌트 종류 | 실행 위치 | 사용 가능한 것 | 사용 불가 |
|---|---|---|---|
| Server Components | 서버에서만 (한 번) | DB 직접 접근, 환경변수, fs 모듈, async / await | useState, useEffect, 이벤트 핸들러, 브라우저 API |
| Client Components | 서버 (SSR) + 클라이언트 (hydration) | useState, useEffect, 이벤트, 브라우저 API | DB 직접 접근 (보안), fs |
같은 페이지 안에서 두 종류가 공존합니다. 페이지의 정적인 콘텐츠 부분은 Server Component, 인터랙션이 필요한 부분(폼, 토글, 드롭다운 등)은 Client Component로 나눕니다. 이 경계를 잘 그리는 게 모던 리액트 앱 설계의 핵심입니다. 다음 24장(Server vs Client Components)에서 본격적으로 다루겠습니다.
React 19가 이 모델에 추가하는 것 #
React 19 정식 출시로 RSC 위에서 동작하는 새 도구들이 안정화됐습니다. 이 4부에서 차례로 만나게 됩니다.
use()훅 (26장) — Promise / Context 모두를 풀 수 있는 훅. RSC에서 만든 Promise를 Client에서 풀 때 핵심- Server Actions (27장) —
<form action={serverFn}>안에서 서버 함수를 직접 호출. 폼 / mutation의 새 표준 useActionState,useFormStatus,useOptimistic(27장 + 28장) — Server Actions와 짝을 이루는 훅들- Asset Loading (preload, preconnect) — RSC streaming과 자연스럽게 어울림
이 모든 신기능을 한곳에 모아 정리한 곳이 28장(React 19 신규 기능 정리)입니다. 4부 본문에서 신기능이 등장할 때마다 28장으로의 cross-link가 따라옵니다.
Next.js와의 관계 #
React Server Components는 React 자체의 기능이지만, 이걸 실제로 쓰려면 빌드 도구와 서버 인프라가 필요합니다. RSC를 직렬화하고, 라우팅을 처리하고, 캐싱을 관리하고, 서버를 띄워 주는 일을 누군가 해 줘야 하기 때문입니다.
Next.js는 React 팀과 가장 긴밀히 협업하는 메타프레임워크로, RSC를 정식으로 지원하는 가장 성숙한 선택지입니다(Remix, Waku, TanStack Start 같은 다른 옵션도 있습니다). 이 4부에서는 Next.js 15를 씁니다. 이 책의 6부 capstone(풀스택 Todo 앱)도 Next.js 15 위에서 만듭니다.
전환의 핵심 #
이 4부를 시작하기 전에 가장 중요한 마인드셋 두 가지가 있습니다.
1. “이 코드는 어디서 실행되나?“를 항상 의식하라 #
지금까지 우리는 “코드는 다 브라우저에서 돈다"는 단일 환경에서 살았습니다. 모던 리액트에서는 같은 파일 안에서도 서버에서 도는 코드와 클라이언트에서 도는 코드가 섞여 있을 수 있습니다. 이 경계를 의식하지 않으면 빠르게 혼란에 빠집니다.
매번 자문하세요. “이 함수는 서버에서 실행되나, 클라이언트에서 실행되나?” 그러면 데이터 페칭이 어떻게 동작해야 하는지, 환경변수에 접근 가능한지, 이벤트 핸들러를 달아도 되는지가 명확해집니다.
2. 기본은 Server Component, 필요한 것만 Client Component #
새 컴포넌트를 만들 때 기본은 Server Component입니다(Next.js App Router에서). Client Component가 필요한 명확한 이유(상태, 이벤트, 브라우저 API)가 있을 때만 'use client' 디렉티브를 붙여 변환합니다. 이 디폴트가 번들 크기를 작게 유지하는 핵심입니다.
부록 A와의 관계 — 옛 리액트에서 넘어오는 분 #
이 4부의 모델은 옛 리액트(Class component / Pages Router / Redux-only / useEffect + fetch)와 동작 방식이 다릅니다. 옛 코드베이스에서 본 모델로 넘어오는 절차는 부록 A(옛 리액트 마이그레이션)에 따로 정리해 두었습니다. 이 4부를 읽으며 옛 패턴이 떠오를 때마다 부록 A를 곁눈으로 두면 자연스럽습니다.
이 4부에서 다룰 내용 #
다음 23~28장에서 차근차근 다룰 내용입니다.
- 23장 Next.js 프로젝트 시작, App Router 파일 구조, layout 시스템
- 24장
'use client'디렉티브와 서버 / 클라이언트 컴포넌트 경계 - 25장 Server Component에서
await fetch(...)패턴, 캐싱 - 26장 Suspense와
loading.tsx,use()훅으로 로딩 처리 - 27장 Server Actions로 폼 제출과 mutation 다루기
- 28장 React 19 신규 기능 정리 — Actions / useFormStatus / useOptimistic / use() / React Compiler / ref as prop
그리고 6부 capstone (34장)에서 이 4부의 모든 도구를 풀스택 Todo 앱 한 개로 묶겠습니다.
연습문제 #
본 챕터는 코드가 거의 없는 개념 챕터라 연습문제도 메타 형태입니다.
- 본인이 만들어 본 (또는 알고 있는) 웹 앱 한 개를 고르고, 그 앱의 페이지 / 컴포넌트를 “이건 Server Component가 적합” / “이건 Client Component가 적합” 두 그룹으로 나눠 보세요. 분기 기준을 “이 컴포넌트에 인터랙션 (state, 이벤트, 브라우저 API)이 있는가?“로 잡으면 됩니다.
- CSR / SSR / RSC의 차이를 다음 세 지표로 비교한 작은 표를 그려 보세요. (a) 첫 화면이 보이기까지 걸리는 시간, (b) SEO 친화성, (c) 클라이언트 번들 크기. 세 모델의 트레이드오프가 머리에 남으면 4부가 가볍게 읽힙니다.
- 이 책의 21장 마지막 절 (RSC 미리보기) 코드와 21장의
useFetch코드를 다시 한 번 비교해 보세요. 두 모델에서useState/useEffect/cancelled플래그 / 로딩 분기가 어디로 사라졌는지 / 어디로 옮겨갔는지 직접 짚어 보면 4부 진입이 훨씬 자연스럽습니다.
한 줄 요약: CSR은 빠른 인터랙션을 주지만 첫 화면이 느리고 SEO가 약하고 번들이 크다. SSR은 첫 화면과 SEO를 풀어 주지만 여전히 모든 코드가 클라이언트로 간다. RSC는 정적 콘텐츠를 서버에서만 실행하고 코드 자체가 클라이언트로 가지 않는다. 이 4부의 두 마인드셋 — (1) “이 코드는 어디서 실행되나?“를 항상 의식, (2) 기본은 Server Component, 필요한 것만 Client Component.
다음 챕터 #
다음 23장 Next.js 시작과 App Router에서는 실제로 Next.js 15 프로젝트를 만들고, App Router의 파일 기반 라우팅과 layout 시스템을 직접 만져 보겠습니다. 15장(React Router)의 동작 방식과 비교하면서 같은 문제(URL → 화면)를 다른 방식으로 푸는 모습을 짚겠습니다.