Jiseong's Web Dev blog
React Server Component 깊게 이해하기 본문
Intro
React는 왜 RSC(React Server Component) 방식을 사용할까? RSC가 해결하려고하는 문제는 무엇일까? Server Side Rendering과 RSC는 다른 것일까? Next.js에서 RSC를 렌더링하는 과정, RCC(React Client Component)를 렌더링하는 과정에 대해 자세히 살펴보자.
SSR? RSC?
먼저 SSR과 RSC의 개념에 대해 정확히 짚어보자. SSR은 요청 시점에 서버에서 HTML을 생성해 클라이언트로 전달하는 렌더링 방식이다. 서버가 데이터를 fetch하고 HTML을 완성해서 클라이언트로 전달한다. 이후 클라이언트는 JS 번들을 다운로드 받아 실행하고 Hydration 과정을 거쳐 인터렉션을 더한다.
RSC는 서버에서 실행되는 컴포넌트이다. 서버 컴포넌트는 JS 번들에 포함되지 않는다. 서버에서 모든 렌더링 로직을 실행하기 때문이다. JS 번들 최적화에서 이점이 있다. RSC는 SSR과 함께 동작하는 컴포넌트이다. RSC는 서버에서 실행하기 때문에 Hydration 과정이 필요없기도하다.
SSR은 "HTML을 언제 생성하느냐"에 관한 기술이고, RSC는 "컴포넌트를 어디서 실행하느냐"에 관한 기술이다.
렌더링 과정을 단계별로 살펴보자
// app/page.tsx
console.log("[Home]");
// SSG 비활성을 위해
export const dynamic = "force-dynamic";
export default async function Home() {
console.log("[Home] Page Render");
return (
<div className="size-full h-100 justify-center flex items-center flex-col gap-8">
<h1>Home</h1>
<LikeButton label="Click" />
</div>
);
}
// app/components/like-button.tsx
"use client";
import { useEffect, useState } from "react";
export default function LikeButton({ label }: { label: string }) {
console.log("[LikeButton] render");
const [like, setLike] = useState(true);
useEffect(() => {
console.log("[LikeButton] mounted");
}, []);
return (
<button onClick={() => setLike((cur) => !cur)}>
{label}
{like ? "like" : "dislike"}
</button>
);
}
/ 으로 접속하면 어떤 일이 일어날까?
1. Server Side Rendering
먼저 브라우저는 서버로 요청을 보낸다. SSR이 시작된다.
// server
[Home]
[Home] Page Render
[LikeButton]
[LikeButton] render
// browser
[LikeButton]
[LikeButton] render
[LikeButton] mounted
로그를 확인해보면, RSC든 RCC든 상관없이 모두 서버에서 실행된다. 이후 브라우저에서 RCC가 실행되고, 마운트된다. 실행되는 과정에서 RSC와 RCC는 RSC Payload로 변환된다. 서버에서 실행되는 과정에서 일어나는 일을 더 자세히 살펴보자.
RSC Payload는 Next.js에서 설명하는 용어이고, React 내부적으로는 React Flight protocol이라는 serialization 포맷을 사용한다.
Next.js 공식 문서에 따르면 아래와 같다.
- Server Components are rendered into a special data format called the React Server Component Payload (RSC Payload).
서버 컴포넌트는 RSC Payload 라는 특별한 데이터로 렌더링된다.
- Client Components and the RSC Payload are used to pre-render HTML.
클라이언트 컴포넌트와 RSC Payload는 HTML pre render를 위해 사용된다.
정리하면 RSC Payload를 생성하기 위해 실행하는 것이다. RSC Payload는 RSC Tree를 표현하기 위한 데이터 포맷이다. client 사이드에서 React 내부적으로 사용하는 데이터이고, 이는 browser의 DOM 업데이트를 위한 것이다. 아래와 같은 정보들을 포함한다.
- 서버 컴포넌트의 렌더링 결과물
- 클라이언트 컴포넌트가 어디에 렌더링될 지 참조하는 placeholder, 이 컴포넌트들이 참조하는 JS 파일들
- 서버 컴포넌트에서 클라이언트 컴포넌트로 전달하는 props
SSR의 결과물은 HTML 문서이다. 정확히 어떤 데이터가 나오는지 개발자 도구에서 네트워크 탭을 살펴보자.

body를 보면 <h1/>, <button/> 들이 렌더링 된 것을 볼 수 있다. 또, 아래쪽의 script 태그 부분에서 RSC Payload 관련 스크립트를 찾을 수 있다.
<script>
self.__next_f.push([
1,
`
d:I[
51810,
["/_next/static/chunks/33f2145b8edcf2b7.js"],
"default"
]
4:[
"$",
"div",
null,
{
"className": "size-full h-100 justify-center flex items-center flex-col gap-8",
"children": [
[
"$",
"h1",
null,
{
"children": "Home"
}
],
[
"$",
"$Ld",
null,
{
"label": "Click"
}
]
]
}
]
`,
]);
</script>
RSC Payload를 담고있는 script의 내용을 살펴보자. h1, Home, label, Click 등의 키워드를 보면 내가 작성한 서버 컴포넌트와 클라이언트 컴포넌트 내용들인 것을 알 수 있다. GPT의 도움을 받아 해석을 부탁했다. 몇가지 정리해보면,
- 구조는 기본적으로 ["$", type, key, props] 입니다.
- "$" 는 React element marker입니다.
- "$Ld" 는 Client Component reference입니다.
실제로 Client Component는 참조만 한다는 것을 확인할 수 있다.
2. Client Side
이제 서버에서 생성한 HTML 문서와 JS 번들을 다운로드 받아 Hydration 해야한다. 네트워크 탭에서 JS 번들을 한 번 찾아보자.

클라이언트 컴포넌트인 LikeButton 컴포넌트의 코드를 확인할 수 있다. Next.js 문서에서는 세가지를 설명한다.
- HTML is used to immediately show a fast non-interactive preview of the route to the user.
이미 클라이언트 컴포넌트도 SSR 과정에서 HTML에 포함된다. 따라서 non-interactive preview를 즉시 보여준다. - RSC Payload is used to reconcile the Client and Server Component trees.
RSC Payload는 Client and Server Component tree를 reconcilation(재조정, diffing 알고리즘을 통한 렌더링 과정)을 하기 위해 사용된다. - JavaScript is used to hydrate Client Components and make the application interactive.
네트워크 탭에서 확인한 다운로드된 JS 번들은 클라이언트 컴포넌트에 hydration 된다.
요약하자면
- 브라우저 요청
- 서버에서 Server Component 실행
- RSC Payload 생성
- RSC Payload + Client Component로 HTML 생성
- HTML 브라우저 전송
- JS bundle 다운로드
- Hydration
- RSC Payload 기반 React tree 구성
- Client Component 실행 (마운트)
- Interactive UI 완성
느낀 점
SSR, CSR, RSC, RCC에 대해 어느 정도 이해하고 있다고 생각했다. 하지만 RSC가 실제로 언제 어떻게 동작하는지 디버깅하며 파고들다 보니, 내가 알고 있던 것은 깊이가 부족하다고 느껴졌다. 공식 문서를 읽고, 실제 네트워크 응답과 실행 로그를 확인하면서 렌더링 과정이 어떻게 이어지는지 조금 더 명확하게 이해할 수 있었다. 기술을 이해할 때 "왜 이렇게 동작할까?"라는 질문을 계속 던지는 것이 얼마나 중요한지 다시 한 번 느낀 계기였다.
'React' 카테고리의 다른 글
| RadixUI의 asChild, BaseUI의 render (0) | 2026.02.07 |
|---|---|
| useEffect 남용을 줄이자. (0) | 2025.03.02 |
| 선언적으로 에러처리 아주 깔끔하게 하기 (Next.js 15) (0) | 2024.10.29 |
| 웹뷰에서 소셜 로그인시 문제 해결하기 (2) | 2024.09.15 |
| 찜 하기, 백엔드 서버 부하 줄이기 (feat. react-query) (0) | 2024.06.09 |