리액트 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를 자식으로만 가질 수 있다. 즉, 기본적으로 모두 서버에서 동작하는 것을 전제로 하지만 클라이언트 컴포넌트 아래로는 클라이언트에서 실행되는 환경이라고 생각할 수 있다.
RCC는 코드 상단에 'use client'
라는 디렉티브를 사용하여 클라이언트 컴포넌트라고 구분할 수 있다. RCC 아래로는 모두 클라이언트 컴포넌트인 것이 보장되므로 디렉티브를 선언한 RCC 아래의 RCC에서는 디렉티브를 생략할 수 있다.
또한 각각의 서버 컴포넌트에서 필요한 데이터는 해당 서버 컴포넌트에서 직접 가져와서 사용하거나 내려줄 수 있기 때문에 최소한의 Props Drilling만 발생하게 된다.
RSC vs RCC
구분 | 서버 컴포넌트 | 클라이언트 컴포넌트 |
---|---|---|
서버 컴포넌트 포함 가능 | ✅ | ❌ |
클라이언트 컴포넌트 포함 가능 | ✅ | ✅ |
렌더링 후 불변 | ✅ | ❌ |
서버 접근 | ✅ | ❌ |
상호 작용 | ❌ | ✅ |
번들 포함 (정보 노출 여부) | ❌ | ✅ |
Browser API 사용 가능 | ❌ | ✅ |
React Hook 사용 가능 | ❌ | ✅ |
Ghost