최근 들어 React 생태계에서 가장 많이 언급되는 개념 중 하나는 바로 React Server Components(RSC)입니다.
이 글에서는 React, Next.js 공식 문서와 Dan Abramov가 직접 쓴 RSC From Scratch 글을 바탕으로 RSC의 등장 배경부터 실제 구현 원리까지 깊이 있게 탐구해 보겠습니다.
React Server Component, 왜 필요할까?
기존의 React 애플리케이션은 대부분 클라이언트에서 모든 렌더링 작업을 수행합니다.
이 방식은 유연성과 인터랙티브한 사용자 경험을 제공하지만, 몇 가지 근본적인 한계도 함께 가지고 있었습니다.
첫째로, 데이터 페칭의 비효율성입니다. 클라이언트가 데이터를 로딩하기 위해 별도의 API 호출을 수행하고, 응답을 기다린 후에야 화면에 표시할 수 있습니다. 이는 초기 로딩 속도 저하와 사용자 경험 악화로 이어집니다.
둘째, 보안적인 측면에서 민감한 데이터나 API 키를 클라이언트에 노출시키지 않으려면 별도의 보안 계층을 구현해야 하는 복잡함이 존재합니다.
셋째, 클라이언트 자바스크립트의 비대화 문제입니다. 마크다운 파서를 포함한 큰 라이브러리들을 클라이언트로 전송하여 번들 크기를 키우고, 클라이언트가 이를 모두 다운로드하고 파싱하여 실행해야 하므로 성능에 부정적인 영향을 끼칩니다.
이러한 문제들을 해결하고자 등장한 것이 바로 React Server Components(RSC)입니다.
Dan Abramov가 말하는 RSC
Dan Abramov는 자신의 글 "RSC From Scratch"에서 RSC의 개념과 동작 원리를 처음부터 구현해보며 설명합니다.
그가 제시한 접근법은 기존의 React 컴포넌트를 서버에서 미리 렌더링 가능한 형태로 점진적으로 확장하는 것입니다.
Abramov의 글에서 첫 번째로 등장하는 중요한 개념은 JSX입니다. 기존 PHP나 Node.js 방식으로 HTML 문자열을 직접 조합하던 방식에서 JSX를 이용해 마크업을 직접 JavaScript 코드 안에 넣을 수 있게 하여 문자열 조작 실수를 방지합니다. React는 JSX를 통해 내부적으로 React Element라는 트리 구조를 생성합니다.
하지만 JSX 트리를 그대로 클라이언트에 보낼 수는 없습니다. 최종적으로 클라이언트가 받는 것은 HTML이어야 하며, React는 서버에서 JSX 트리를 렌더링하여 HTML 문자열로 변환하는 과정을 수행합니다. 이것이 바로 서버 사이드 렌더링(SSR)의 기본 개념입니다.
React Server Components는 이 SSR 개념을 더 확장하여 JSX 트리 자체를 특수한 형태의 데이터(RSC Payload)로 변환하고, 이를 클라이언트로 전달하여 클라이언트에서 최종적으로 DOM을 업데이트하게 합니다. 이 과정에서 클라이언트는 번거롭게 큰 번들을 받을 필요 없이 미리 렌더링된 HTML과 최소한의 자바스크립트만을 다운로드합니다.
React Server Components의 핵심 개념
React 공식 문서에 따르면 Server Components는 빌드 타임 또는 요청 타임에 서버에서 미리 렌더링되는 새로운 형태의 컴포넌트입니다.
이는 클라이언트 애플리케이션 또는 서버 사이드 렌더링(SSR)과 별도의 환경에서 렌더링됩니다.
React Server Components의 주요 특징은 다음과 같습니다.
-
데이터 페칭: 서버에서 데이터를 바로 가져와 미리 렌더링할 수 있어 추가적인 API 호출 없이 초기 페이지 로딩 시 데이터를 제공합니다.
-
보안: 민감한 데이터를 클라이언트로 보내지 않고 서버에서만 처리하므로 보안적인 이점을 제공합니다.
-
캐싱 및 성능: 서버에서 렌더링한 결과를 캐싱하여 여러 사용자 간 공유가 가능하며, 클라이언트로 전송되는 자바스크립트 번들을 최소화하여 초기 로딩 성능을 대폭 개선합니다.
Next.js에서의 Server Components
Next.js 공식 문서에서는 RSC를 활용한 서버 렌더링 방식을 더 구체적으로 소개합니다.
Next.js는 기본적으로 모든 컴포넌트를 Server Components로 간주하고, 라우트 세그먼트 단위로 렌더링 작업을 나누어 수행합니다.
Next.js의 렌더링 전략은 크게 다음 세 가지로 나눌 수 있습니다.
-
정적 렌더링 (Static Rendering): 빌드 타임에 미리 렌더링하여 CDN에 캐싱하고 사용자 간 공유합니다. 이는 블로그 글, 제품 페이지 등 정적인 컨텐츠에 적합합니다.
-
동적 렌더링 (Dynamic Rendering): 사용자별로 맞춤화된 데이터를 요청 시점에 렌더링하여 제공합니다. 개인화된 데이터가 중요한 페이지에서 사용됩니다.
-
스트리밍 (Streaming): 페이지 전체가 렌더링되기 전에 미리 렌더링된 부분부터 점진적으로 클라이언트로 전송하여 빠르게 컨텐츠를 보여줍니다.
Next.js는 데이터 캐싱 여부와 특정 Dynamic API(cookies, headers 등)의 사용 여부에 따라 자동으로 최적의 렌더링 전략을 선택합니다.
마무리
React Server Components의 실제 활용은 현재 Next.js와 같은 프레임워크를 통해 점차 늘어나고 있습니다.
서버와 클라이언트의 렌더링 작업을 명확히 구분하고, 효율적으로 데이터를 처리하여 성능을 극대화하는 방식이 React 생태계의 새로운 표준이 되어가고 있습니다.
이 글이 React Server Components의 등장 배경과 개념, 실제 구현 방식에 대해 이해하는 데 조금이나마 도움이 되길 바랍니다.