# 리액트 19 서버 컴포넌트 (RSC)

- Author: @baealex
- Published: 2024-03-11
- Updated: 2025-12-20
- Source: http://blex.me/@baealex/react-server-component
- Tags: 리액트, 프론트엔드

---

리액트 18에서 Server Component 라는 개념이 추가되었다. 서버 컴포넌트란? 서버에서 렌더링되는 컴포넌트이다. (이 이상의 표현이 아직은 떠오르지 않는다.) 하지만 사실 리액트에서는 이미 컴포넌트를 서버에서 렌더링 하는 방법을 제공하고 있었다.

## 기존 SSR의 문제점

---

기존 컴포넌트를 서버 사이드 렌더링 (이하 SSR) 할 때 어떤 문제들이 있었을까?

#### 1. 하이드레이션

사용자는 서버에서 렌더링 된 HTML과 다운로드된 자바스크립트를 활용해서 React Virtual DOM을 구성하고 이벤트 바인딩을 처리해야 웹 사이트를 정상적으로 사용할 수 있게 된다. 이 과정을 하이드레이션이라고 한다. 하이드레이션의 문제는 컴포넌트에 대한 모든 코드가 자바크립트 코드(이하 Bundle)에 포함되어야 한다는 점이다. 페이지의 규모가 커질수록 하이드레이션과 Bundle 사이즈에 큰 비용이 발생한다.

#### 2. 클라이언트 기반 컴포넌트

리액트의 모든 컴포넌트는 클라이언트에서 동작하는 것을 기본으로 하지만 SSR을 처리하려면 서버 환경에서 구동되는 것도 고려하지 않을 수 없다. 모든 컴포넌트에 대하여 브라우저 API를 사용하거나 브라우저 API에 의존적인 라이브러리를 사용한다면 서버에서 처리하지 않도록 별도로 처리를 해줘야 한다.

#### 3. Props Drilling

페이지 단위로 SSR을 구성했거나, (페이지 라우터 기반의) Next 프레임워크를 사용할 경우 페이지에 props으로 렌더링 될 데이터를 전달하는 방식을 주로 취하는데, 이때 컴포넌트의 깊이가 깊어지고 하위의 컴포넌트에 데이터 전달이 필요하다면 불필요한 Props Drilling이 발생할 수 있다.

## 서버 컴포넌트

---

서버 컴포넌트의 도입으로 위 3가지 문제를 고칠 수 있게 되었다. 서버 컴포넌트의 역할은 매우 단순한다. 데이터를 가져오고 HTML을 렌더링한다. 별도의 라이프 사이클을 가지지 않으며 사용자와 상호 작용할 수 없는 불변의 컴포넌트이므로 **하이드레이션을 해야 할 필요가 없다.** 따라서 서버 컴포넌트는 사용자에게 전달되지 않으므로 키 노출과 같은 이슈로 부터 안전하며 번들 사이즈가 줄어들 것을 기대할 수 있다.

지금까지 리액트는 클라이언트 기반의 클라이언트 컴포넌트 (이하 RCC)를 기본으로 하였지만 이제부터는 서버 기반의 서버 컴포넌트(이하 RSC)를 기본으로 한다. 어떻게 서버와 클라이언트의 구분을 명확하게 할 수 있었을까? RSC와 RCC에는 룰이 있다. RSC는 자식으로 RSC와 RCC를 가질 수 있지만 RCC는 RCC를 자식으로만 가질 수 있다. 즉, 기본적으로 모두 서버에서 동작하는 것을 전제로 하지만 클라이언트 컴포넌트 아래로는 클라이언트에서 실행되는 환경이라고 생각할 수 있다.

<figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"><img alt="" src="https://blex.me/resources/media/images/content/2024/3/11/202431120_kVtGCFNofch2V1YhZjEl.png" style="object-fit: cover;"/></figure>

RCC는 코드 상단에 `'use client'` 라는 디렉티브를 사용하여 클라이언트 컴포넌트라고 구분할 수 있다. RCC 아래로는 모두 클라이언트 컴포넌트인 것이 보장되므로 디렉티브를 선언한 RCC 아래의 RCC에서는 디렉티브를 생략할 수 있다.

- ['use client' directive - React](https://react.dev/reference/react/use-client)

또한 각각의 서버 컴포넌트에서 필요한 데이터는 해당 서버 컴포넌트에서 직접 가져와서 사용하거나 내려줄 수 있기 때문에 최소한의 Props Drilling만 발생하게 된다.

#### RSC vs RCC

<table style="min-width: 75px;"><colgroup><col style="min-width: 25px;"/><col style="min-width: 25px;"/><col style="min-width: 25px;"/></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>구분</p></th><th colspan="1" rowspan="1"><p>서버 컴포넌트</p></th><th colspan="1" rowspan="1"><p>클라이언트 컴포넌트</p></th></tr><tr><td colspan="1" rowspan="1"><p>서버 컴포넌트 포함 가능</p></td><td colspan="1" rowspan="1"><p>✅</p></td><td colspan="1" rowspan="1"><p>❌</p></td></tr><tr><td colspan="1" rowspan="1"><p>클라이언트 컴포넌트 포함 가능</p></td><td colspan="1" rowspan="1"><p>✅</p></td><td colspan="1" rowspan="1"><p>✅</p></td></tr><tr><td colspan="1" rowspan="1"><p>렌더링 후 불변</p></td><td colspan="1" rowspan="1"><p>✅</p></td><td colspan="1" rowspan="1"><p>❌</p></td></tr><tr><td colspan="1" rowspan="1"><p>서버 접근</p></td><td colspan="1" rowspan="1"><p>✅</p></td><td colspan="1" rowspan="1"><p>❌</p></td></tr><tr><td colspan="1" rowspan="1"><p>상호 작용</p></td><td colspan="1" rowspan="1"><p>❌</p></td><td colspan="1" rowspan="1"><p>✅</p></td></tr><tr><td colspan="1" rowspan="1"><p>번들 포함 (정보 노출 여부)</p></td><td colspan="1" rowspan="1"><p>❌</p></td><td colspan="1" rowspan="1"><p>✅</p></td></tr><tr><td colspan="1" rowspan="1"><p>Browser API 사용 가능</p></td><td colspan="1" rowspan="1"><p>❌</p></td><td colspan="1" rowspan="1"><p>✅</p></td></tr><tr><td colspan="1" rowspan="1"><p>React Hook 사용 가능</p></td><td colspan="1" rowspan="1"><p>❌</p></td><td colspan="1" rowspan="1"><p>✅</p></td></tr></tbody></table>

<figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"><img alt="" src="https://blex.me/resources/media/images/content/2024/3/11/202431121_noHLZo9oIJDRp3BqKPIy.png" style="object-fit: cover;"/></figure>
