React Server Components(RSC)는 React를 순수 렌더링 라이브러리에서 서버-클라이언트 통신과 데이터 패칭을 프레임워크 수준에서 통합하는 방향으로 발전시킨다. SSR과 Suspense가 해결하지 못한 번들 사이즈 증가, 클라이언트 과부하, waterfall 데이터 패칭 문제를 서버에서 렌더링을 완결하는 방식으로 해결한다. Next.js 13.4 App Router에서 기본값으로 채택된 RSC의 동작 원리, 한계, 올바른 활용 패턴을 다룬다.
핵심 포인트- RSC는 서버에서 데이터 패칭 + 렌더링 완결 -> 클라이언트로는 HTML 스트림만 전송, JS 번들 크기를 앱 규모에 관계없이 일정하게 유지
- SSR의 'all-or-nothing waterfall' 한계 보완: Suspense와 결합해 일부 컴포넌트만 서버에서 우선 스트리밍, 나머지는 병렬 처리
- 클라이언트 상호작용(useEffect, state) 필요 시 'use client' 한 줄로 Client Component로 전환 -- RSC는 Client Component를 대체하지 않음
- Server Actions: 클라이언트에서 서버 함수 직접 호출 -> 별도 API 라우트 없이 DB 업데이트/폼 처리, JS 로드 전에도 폼 동작(Progressive Enhancement)
- Next.js App Router 데이터 패칭: 기본값은 빌드 타임 정적 렌더링, {next: {revalidate: N}}으로 ISR, {cache: 'no-store'}로 SSR 전환
- RSC 한계: 직렬화 가능 데이터만 전송 가능, WebSocket 등 지속 연결 불가, lifecycle hooks 사용 불가
상세 정리- PHP 시절 서버-클라이언트 통합 -> React의 클라이언트 분리 -> 다시 서버 통합으로 진화한 역사적 맥락
- SSR은 초기 HTML 전송하지만 모든 JS 다운로드 + hydration 완료 전까지 인터랙션 불가(all-or-nothing)
- React Suspense: 컴포넌트를 래핑해 서버 HTML 스트리밍 + 선택적 hydration -> 우선순위 높은 컴포넌트 먼저 인터랙티브
- Suspense로도 해결 안 되는 문제: useEffect 기반 클라이언트 데이터 패칭 = 렌더링 후 추가 왕복, JS 번들 계속 증가
- RSC: 각 컴포넌트가 서버에서 독립적으로 데이터 패칭 + 렌더링, 결과는 클라이언트 React 트리에 스트리밍으로 인터리빙
- 클라이언트 번들 기본 런타임은 캐시 가능하고 크기 예측 가능 -- 앱이 성장해도 자동으로 증가하지 않음
- RSC re-render: 상태 변경 시 서버에서 재렌더링 -> 기존 DOM에 병합(hard refresh 없음, 클라이언트 상태 유지)
- RSC + Suspense: 데이터 패칭과 렌더링 모두 서버에서 -> 대기 시간을 서버에서 관리, 총 왕복 횟수 최소화
- RSC 한계 상세: 직렬화 불가 값(함수, 클래스 인스턴스) 전달 불가, useEffect/useState 사용 불가, WebSocket 등 실시간 연결 불가
- Server Actions: RSC 내 서버 함수를 클라이언트 이벤트에서 직접 호출 -> RPC-like 인터페이스, DB/폼 처리
- Progressive Enhancement: Server Actions는 JS 로드 전에도 폼 제출 동작 가능
- Next.js App Router: getServerSideProps/getStaticProps 대신 fetch API + 캐시 옵션으로 데이터 전략 결정
- 자동 request 중복 제거(deduping): 동일 fetch를 여러 컴포넌트에서 호출해도 한 번만 실행
- 올바른 사용 패턴: 동적 데이터 패칭 -> RSC, 로컬 인터랙션(클릭, 애니메이션, 폼 상태) -> Client Component
왜 읽나React의 렌더링 모델이 왜 RSC로 진화했는지 역사적 맥락부터 Server Actions까지 한 편으로 이해할 수 있는 가장 체계적인 입문 글이다.