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 Component | Client 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 배포 패턴.