React 교재 · 고급 23편

React Server Components 정리

"useEffect 안에서 fetch" 시대를 끝낸 패러다임. RSC 가 바꾸는 것과 안 바꾸는 것.

서버와 브라우저 사이 데이터 라인 일러스트 — RSC 컨셉

React Server Components (RSC) 는 React 18 (2022) 에서 도입돼 Next.js 13 App Router (2023) 부터 본격화. 2026 년 현재 Next.js 의 기본 모델 + 다른 메타 프레임워크 (Remix·Waku) 도 채택 중. 1~22편의 모든 패턴은 여전히 유효하지만, RSC 가 추가되면서 "어디서 무엇을 할지" 가 다시 정리됐다.

이번 23편은 RSC 의 정확한 의미 + 'use client' / 'use server' 경계 + 데이터 fetch 가 컴포넌트 안으로 들어오는 모델 + 실전 도입 가이드.

1. 두 종류의 컴포넌트 — 서버 vs 클라이언트

RSC 도입 후 컴포넌트가 2 종류로 명확히 나뉜다.

구분Server ComponentClient Component
실행 위치서버 (Node·Edge)브라우저 (기존과 동일)
표시 방식기본값. 'use client' 없으면 server파일 첫줄 'use client'
useState·useEffect❌ 사용 불가✅ 가능
DB·파일 직접 접근✅ async 함수로 자유롭게❌ API 통해서만
번들에 포함❌ HTML 만 전송✅ JS 번들에 포함

2. 데이터 fetch 가 컴포넌트 안으로

가장 큰 변화. 17편 TanStack Query 가 클라이언트 fetch 의 표준이었다면, RSC 는 서버에서 직접 DB 호출.

// app/users/page.tsx (Server Component — 'use client' 없음) import { db } from '@/lib/db'; export default async function UsersPage() { // ← 컴포넌트가 async 함수! const users = await db.users.findMany(); // DB 직접 접근 return ( <ul> {users.map(u => <li key={u.id}>{u.name}</li>)} </ul> ); }

이 한 컴포넌트가 → 서버에서 DB 쿼리 → HTML 생성 → 브라우저로 전송. useEffect 도 없고 loading state 도 없고 useQuery 도 없다. 클라이언트 JS 번들에 이 코드는 1바이트도 안 포함됨.

장점 — ① 번들 크기 감소 (이 컴포넌트 + db 라이브러리 전부 브라우저 미전송). ② DB 비밀번호·API 키 같은 secret 안전 (서버에만). ③ waterfall fetch 제거 (서버에서 병렬). ④ SEO 친화 (HTML 이 서버에서 완성).

3. 경계 — 'use client' 의 정확한 의미

인터랙티브 (useState·이벤트 핸들러·useEffect) 가 필요하면 클라이언트로. 파일 첫 줄에 'use client'.

// app/components/LikeButton.tsx 'use client'; import { useState } from 'react'; export function LikeButton({ initialCount }: { initialCount: number }) { const [count, setCount] = useState(initialCount); return <button onClick={() => setCount(c => c + 1)}>❤ {count}</button>; } // app/posts/[id]/page.tsx (서버 컴포넌트) import { LikeButton } from '@/components/LikeButton'; export default async function PostPage({ params }) { const post = await db.posts.findUnique({ where: { id: params.id } }); return ( <article> <h1>{post.title}</h1> <p>{post.body}</p> <LikeButton initialCount={post.likes} /> </article> ); }

서버 컴포넌트가 클라이언트 컴포넌트를 import 해서 박는 게 표준 패턴. 데이터 fetch·전체 레이아웃은 서버, 인터랙티브 부품만 클라이언트로 격리.

역방향 import 금지 — 'use client' 컴포넌트가 서버 컴포넌트를 import 할 수 없다 (children prop 으로 받을 수만 있음). 이게 RSC 의 가장 큰 학습 장벽. 한 컴포넌트 트리에서 server → client 는 자연스럽지만 client → server 는 의도적으로 차단.

4. 'use server' — Server Actions

RSC 의 반대 방향. 클라이언트가 호출하는데 서버에서 실행되는 함수. 폼 제출·DB 변경 같은 mutation 에 사용.

// app/actions.ts 'use server'; import { db } from '@/lib/db'; import { revalidatePath } from 'next/cache'; export async function createPost(formData: FormData) { await db.posts.create({ data: { title: formData.get('title'), body: formData.get('body') } }); revalidatePath('/posts'); // 캐시 무효화 → 목록 자동 갱신 } // 폼에서 호출 import { createPost } from '@/app/actions'; function NewPostForm() { return ( <form action={createPost}> <input name="title" /> <textarea name="body" /> <button>발행</button> </form> ); }

이게 마법 — 클라이언트 코드처럼 보이지만 함수 자체는 서버에서 실행. fetch API 호출이 자동으로 wired up. 17편 useMutation 의 일상 케이스가 server action 한 줄로.

5. 도입 가이드 — 언제 / 어디서

RSC 는 메타 프레임워크 (Next.js 14+·Remix·Waku) 안에서만 동작. Vite 기본 React 에선 아직 못 씀. 박준성님 회사처럼 Next.js 쓰는 경우 — 새 페이지부터 RSC 디폴트로, 기존 SPA 코드는 'use client' 유지하며 점진 마이그레이션.

지금 행동 — Next.js 프로젝트면 RSC 가 디폴트라 자연스러움. Vite SPA 면 22편까지 패턴이 여전히 표준. 둘 다 알아두면 React 진화 어디로 가든 따라간다.

23편으로 React 의 현재와 미래 모두 정리. 24편은 production — 빌드·배포 파이프라인 (Vite build·Vercel·Cloudflare Pages).

다음 글

React 교재 24편 — 빌드와 배포. Vite production build, code splitting, Vercel·Cloudflare 배포 패턴.

© 2026 주나이테크(주) @JUNAITECH