Contents
see List개요
React Server Components(RSC)는 React 18에서 도입된 혁신적인 렌더링 패러다임입니다. 서버에서만 실행되는 컴포넌트를 통해 번들 크기를 줄이고 초기 로딩 성능을 크게 향상시킬 수 있습니다.
핵심 개념
RSC는 기존 SSR과는 근본적으로 다른 접근 방식을 취합니다:
- 제로 번들 임팩트: 서버 컴포넌트 코드는 클라이언트 번들에 포함되지 않습니다.
- 직접 데이터 접근: 데이터베이스나 파일 시스템에 직접 접근할 수 있습니다.
- 자동 코드 스플리팅: 클라이언트 컴포넌트를 자동으로 분리합니다.
- 스트리밍 지원: Suspense와 결합하여 점진적 렌더링이 가능합니다.
서버 컴포넌트 vs 클라이언트 컴포넌트
올바른 컴포넌트 타입 선택이 중요합니다:
// ✅ 서버 컴포넌트 (기본)
// - 데이터 페칭
// - 민감한 정보 접근
// - 큰 의존성 사용
async function ProductList() {
const products = await db.product.findMany();
return <div>{products.map(p => <ProductCard {...p} />)}</div>;
}
// ✅ 클라이언트 컴포넌트
// - 인터랙티브 UI
// - 브라우저 API 사용
// - 상태 관리
'use client';
function AddToCartButton({ productId }) {
const [isLoading, setIsLoading] = useState(false);
const handleClick = async () => {
setIsLoading(true);
await addToCart(productId);
setIsLoading(false);
};
return <button onClick={handleClick}>{isLoading ? '추가 중...' : '장바구니 담기'}</button>;
}
컴포넌트 구성 패턴
서버와 클라이언트 컴포넌트를 효과적으로 조합하는 패턴들입니다:
1. 서버 컴포넌트에서 클라이언트 컴포넌트로 데이터 전달
// app/products/page.tsx (서버 컴포넌트)
async function ProductsPage() {
const products = await fetchProducts();
return (
<div>
<ProductFilter products={products} />
</div>
);
}
// components/ProductFilter.tsx (클라이언트 컴포넌트)
'use client';
function ProductFilter({ products }) {
const [filter, setFilter] = useState('all');
const filtered = products.filter(p => filter === 'all' || p.category === filter);
return (
<div>
<select onChange={(e) => setFilter(e.target.value)}>
<option value="all">전체</option>
<option value="electronics">전자제품</option>
</select>
{filtered.map(p => <ProductCard key={p.id} {...p} />)}
</div>
);
}
2. 클라이언트 컴포넌트에 서버 컴포넌트 삽입 (children 패턴)
// ClientTabs.tsx (클라이언트 컴포넌트)
'use client';
export function ClientTabs({ children }) {
const [activeTab, setActiveTab] = useState(0);
return (
<div>
<div className="tabs">
<button onClick={() => setActiveTab(0)}>탭 1</button>
<button onClick={() => setActiveTab(1)}>탭 2</button>
</div>
<div>{children[activeTab]}</div>
</div>
);
}
// page.tsx (서버 컴포넌트)
async function Page() {
const data1 = await fetchData1();
const data2 = await fetchData2();
return (
<ClientTabs>
<ServerContent data={data1} />
<ServerContent data={data2} />
</ClientTabs>
);
}
데이터 페칭 최적화
서버 컴포넌트에서 효율적인 데이터 페칭 전략:
import { cache } from 'react';
// 요청 메모이제이션
const getUser = cache(async (id: string) => {
return await db.user.findUnique({ where: { id } });
});
// 여러 컴포넌트에서 호출해도 한 번만 실행됨
async function UserProfile({ userId }) {
const user = await getUser(userId);
return <div>{user.name}</div>;
}
async function UserPosts({ userId }) {
const user = await getUser(userId); // 캐시된 결과 재사용
const posts = await db.post.findMany({ where: { authorId: userId } });
return <div>{posts.length} posts by {user.name}</div>;
}
스트리밍과 병렬 데이터 로딩
Suspense를 활용하여 사용자 경험을 개선합니다:
import { Suspense } from 'react';
async function SlowComponent() {
const data = await slowQuery();
return <div>{data}</div>;
}
async function FastComponent() {
const data = await fastQuery();
return <div>{data}</div>;
}
export default function Page() {
return (
<div>
{/* 빠른 컴포넌트는 먼저 표시 */}
<Suspense fallback={<Skeleton />}>
<FastComponent />
</Suspense>
{/* 느린 컴포넌트는 준비되면 표시 */}
<Suspense fallback={<Skeleton />}>
<SlowComponent />
</Suspense>
</div>
);
}
서버 액션 활용
서버 액션을 통해 API 라우트 없이 서버 로직을 실행할 수 있습니다:
// actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
const post = await db.post.create({
data: { title, content }
});
revalidatePath('/blog');
redirect(`/blog/${post.id}`);
}
// CreatePostForm.tsx
'use client';
import { createPost } from './actions';
export function CreatePostForm() {
return (
<form action={createPost}>
<input name="title" required />
<textarea name="content" required />
<button type="submit">작성</button>
</form>
);
}
활용 팁
- 가능한 한 서버 컴포넌트를 사용하여 클라이언트 번들을 최소화하세요.
- 클라이언트 컴포넌트는 트리의 가능한 한 아래쪽(리프)에 배치하세요.
- 서버 컴포넌트에서는 시리얼라이즈 가능한 props만 전달하세요 (함수, Date 객체 등은 불가).
- React.cache()를 활용하여 중복 요청을 방지하세요.
- Suspense 경계를 전략적으로 배치하여 사용자 경험을 최적화하세요.
마무리
React Server Components는 웹 개발의 패러다임을 바꾸는 혁신적인 기술입니다. 서버와 클라이언트의 경계를 명확히 이해하고 적절히 활용하면 성능과 개발 경험을 모두 향상시킬 수 있습니다.