
Network Boundary (Server, Client)바운더리 분리 방법RSC 생성 원리빌드 과정: 모듈 그래프 생성RSC 생성: 클라이언트 요청 시 동적 처리예시코드Server module graphclient module graph서버 처리 (RSC 생성)서버처리 (SSR)클라이언트 처리 (초기 렌더링)클라이언트 처리 (Hydration)클라이언트 처리 (CSR)요약
Network Boundary (Server, Client)
Network Boundary란 client, server 환경을 구분하는 개념적인 선이다.
React에서 네트워크 바운더리를 어디에 둘 것인지 선택할 수 있다.
- data fetch와 유저의 포스트들을 서버에서 렌더링 시킬 수도 있고(Server Components),
- 좋아요 버튼 등 Interactive한 요소들은 Client에서 렌더링 시킨다. (Client Components)

위 그림의 예시에선 다음과 같이 구분했다.
- 전 페이지에서 공유되는 Nav 컴포넌트를 서버에서 렌더링 하되,
- active한 상호작용 가능한 링크들을 보여주는 Links는 client에서 렌더링.
바운더리 분리 방법
Next.js는 기본적으로 SSR로 컴포넌트를 렌더링 한다.
CSR로 렌더링 시키고 싶은 컴포넌트에는 최상단에
‘use client’
지시자를 표시한다.'use client'; import { useState } from 'react'; import { formatDate } from './formatters'; import Button from './button'; export default function RichTextEditor({ timestamp, text }) { const date = formatDate(timestamp); // ... const editButton = <Button />; // ... }
RSC 생성 원리


How React server components work: an in-depth guide
A deep dive exploration of React server components under the hood.
빌드 과정: 모듈 그래프 생성
React는 컴포넌트가 어떻게 실행될지를 기준으로 두 개의 모듈 그래프를 생성한다.
- Server Module Graph (서버 모듈 그래프)
server
에서 실행될 모든 서버 컴포넌트(Server Component) 를 포함하는 그래프.- 여기에는 API 요청, DB 조회, 파일 읽기 등의 서버 전용 코드가 들어갈 수 있음.
use client
지시자가 없는 컴포넌트는 자동으로 여기에 포함됨.- 서버에서 실행할 수 있도록 준비된 코드의 모음 (요청이 들어오면 서버에서 실행해서 html을 만듦)
- 실행될 코드들의 레퍼런스들이 Tree 구조로 관리되는 것임.
- Client Module Graph (클라이언트 모듈 그래프)
use client
가 선언된 클라이언트 컴포넌트(Client Component) 들을 포함하는 그래프.- 여기에는 이벤트 핸들러, 브라우저 전용 API 사용 코드 등이 들어감.
- 브라우저에서 실행될 JavaScript 코드로 변환 및 번들링됨. (클라이언트 컴포넌트를 생성할 수 있는 Javscript 코드가 번들링되어 클라이언트 모듈 그래프에 포함됨)
- 클라이언트에서 실행될 React 컴포넌트의 JavaScript 코드가 미리 번들링되어 포함됨.
- 이를 위해 Webpack, Vite 같은 번들러가 사용됨.
RSC 생성: 클라이언트 요청 시 동적 처리
.png%3FspaceId%3Da8902909-946a-4e32-b90c-551e6f727162?table=block&id=19a2e09f-9db6-80f1-b234-c266d4c2caa2&cache=v2)
빌드 후 서버는 이 두 개의 모듈 그래프를 유지하고 있다가, 클라이언트가 요청하면 그때 RSC를 생성한다.
RSC로 만들어두지 않는 이유는, 요청받은 시점에서 SSR을 할 때 필요한 api 데이터 등이 달라질 수 있기 때문이다.
- 서버에서 서버 컴포넌트를 렌더링하고, 그 결과를 React Server Component Payload (RSC Payload)라는 특별한 데이터 형식으로 변환.
- RSC Payload는 클라이언트로 전송되며, 여기에는
- 서버 컴포넌트의 렌더링 결과 (HTML)
- 클라이언트 컴포넌트가 들어가야 할 자리(Placeholder)
- 클라이언트 컴포넌트가 실행할 번들된 JavaScript 파일의 경로(참조정보)가 포함됨.
- 클라이언트의 React가 RSC Payload를 받아서
- 서버에서 받은 HTML을 바탕으로 초기 렌더링 수행.
- 클라이언트 컴포넌트가 필요한 위치에 JavaScript 번들을 로드하고 실행하여 DOM을 업데이트.
예시
코드
// 예제: 페이지 컴포넌트 export default function Page() { return ( <Layout> <ServerComponent /> <ClientComponent /> {/* use client */} </Layout> ); } // 서버 컴포넌트 function ServerComponent() { return <div>서버에서 렌더링됨</div>; }
Server module graph
Page ├── Layout (Server) // 실행할 코드 참조 └── ServerComponent (Server) // 실행할 코드 참조
client module graph
// 🚨 빌드 시점에서 컴포넌트를 그릴 javascript 코드로 번들링 되어 있음. Page └── ClientComponent (Client) // 번들링된 클라이언트 컴포넌트의 참조
서버 처리 (RSC 생성)
사용자로부터 요청을 받으면, 빌드 시점에 생성한 server module graph와 client module graph를 참조해서 RSC를 생성함.
// server component { $$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: { title: "oh my", children: "서버에서 렌더링됨" }, _owner: null, _store: {} } // 이후 RSC Payload로 직렬화 되어 아래와 같이 변환됨. // json [ { "type": "div", "props": { "title": "oh my", "children": "서버에서 렌더링됨" } } ]
// client component { $$typeof: Symbol(react.element), type: { $$typeof: Symbol(react.module.reference), // CSR 필요한 건 레퍼런스를 남김 // ClientComponent is the default export... name: "default", filename: "./src/ClientComponent.client.js" // 파일 위치 정보 }, props: { children: "oh my" }, } // 이후 RSC Payload로 직렬화 되어 아래와 같이 변환됨. // json [ { "$$typeof": "react.element", "type": { "$$typeof": "react.module.reference", "name": "default", "filename": "./src/ClientComponent.client.js" }, "props": { "children": "oh my" } } ]
서버처리 (SSR)
- 서버에서 RSC Payload를 사용하여 초기 HTML을 렌더링.
- Server Component는 미리 HTML로 변환해서 응답에 포함됨.
- 반면, Client Component는 실행되지 않고, placeholder 또는 boundary 마커만 남김.
<html> <body> <div title="oh my">서버에서 렌더링됨</div> <!-- React Server Component ClientBoundary: ClientComponent --> </body> </html>
클라이언트 처리 (초기 렌더링)
- 클라이언트는 서버로부터 HTML + RSC Payload를 받음.
- 서버로부터 SSR 된 html을 받아서 초기 렌더링.
클라이언트 처리 (Hydration)
- SSR 결과를 interactive 하게 만드는 과정.
- 클라이언트가 받은 HTML에 React를 연결하여 Interactive하게 만듦
- React는 기존 DOM을 파싱하여 React 내부 구조로 변환하고 이벤트 핸들러를 다시 연결함.
Hydration의 목적은 SSR로 생성된 정적인 HTML을 React가 다시 컨트롤할 수 있도록 만드는 것
- 정적 html로 사용자에게 바로 보여주고 (정적인 페이지),
- 이후 hydration 과정을 통해 react가 제어할 수 있도록 하는 것 (이벤트 연결 등)
- i.g.
button
요소가 초기 html 렌더링 시에는 UI만 보여짐. 하이드레이션 과정에서 이벤트 핸들러를 연결하며 interactive 하게 됨.
클라이언트 처리 (CSR)
- RSC Payload를 파싱하여 React.createElement()를 통해 다시 React 요소로 변환.
- 클라이언트가 번들링된 JS 파일을 다운로드해서 실행.
- type 필드에 있는
react.module.reference
를 보고, 해당 파일의 JS를 로드한 후 컴포넌트를 실행
// 이 과정은 React 내부에서 자동으로 이루어짐 import("./src/ClientComponent.client.js").then((mod) => { const ClientComponent = mod.default; React.createElement(ClientComponent, { children: "oh my" }); });
CSR은 클라이언트에서 추가적인 JavaScript 로직을 실행하는 과정이며,
Hydration과 CSR은 병렬적으로 일어날 수 있다
요약
1️⃣ 빌드 과정:
- React는
server module graph
와client module graph
를 나누어 관리함. - ✅ Server Module Graph → 서버에서 실행할 수 있도록 준비된 코드의 모음
- 요청 시점에 서버에서 실행하여 html을 만듦.
- ✅ Client Module Graph → 브라우저에서 실행할 JavaScript 코드로 번들링됨
- client에서 컴포넌트를 만들 떄 필요한 javascript를 미리 번들링 해놓음.
2️⃣ 클라이언트가 요청하면:
- 서버에서
server module graph
를 실행해 RSC Payload를 생성함.
3️⃣ 클라이언트는:
- 서버에서 받은 HTML을 사용해 초기 렌더링.
- 필요한 JavaScript를 로드하여 클라이언트 컴포넌트를 실행하고 DOM을 업데이트.