
SOFTMOA TECHNOLOGY
React Server Components 심화 패턴과 활용
소프트모아가 정리한 기술 기록입니다.
Contents
작성일 2026. 02. 11.
개요
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는 웹 개발의 패러다임을 바꾸는 혁신적인 기술입니다. 서버와 클라이언트의 경계를 명확히 이해하고 적절히 활용하면 성능과 개발 경험을 모두 향상시킬 수 있습니다.
Share this
FrontEnd
Showing 1 to 10 of 94 entries
