2 min read
RSC Boundary 설계: Server/Client 분리 기준
React Server Components 경계를 기능 단위로 나눠 번들 크기와 복잡도를 동시에 관리하는 방법

도입
RSC를 도입하면 데이터 로딩을 서버로 밀어낼 수 있어 첫 렌더 성능이 좋아진다. 하지만 무작정 Server Component로 바꾸면 상호작용이 많은 영역에서 오히려 코드 복잡도가 증가한다. 특히 팀 단위로 개발할 때는 "어디까지 서버인가"에 대한 합의가 없으면 컴포넌트 경계가 흔들린다.
이 글은 RSC 경계를 기능 기준으로 나누는 방식, 그리고 경계가 잘못됐을 때 나타나는 징후를 실무 관점에서 정리한다.

문제 정의
다음 상황이 반복되면 경계 설계가 잘못된 가능성이 높다.
- 페이지마다
"use client"가 과도하게 붙어 초기 JS 번들이 급격히 커진다. - 서버에서 가져온 데이터를 다시 클라이언트 훅으로 중복 fetch한다.
- UI 변경과 데이터 로딩 변경이 동시에 발생해 리뷰 범위가 커진다.
- 캐시 정책이 페이지 단위로 흩어져 재검증 전략이 일관되지 않다.
핵심은 "UI 인터랙션"과 "데이터 오케스트레이션"을 같은 레이어에 두지 않는 것이다.
핵심 개념
| 구분 | Server Component에 두기 좋은 것 | Client Component에 두기 좋은 것 |
|---|---|---|
| 데이터 처리 | DB/API aggregate, 권한 기반 필터링 | 실시간 로컬 상태, 폼 편집 상태 |
| 렌더링 | SEO 필요 영역, 초기 콘텐츠 | 클릭/드래그/애니메이션 중심 영역 |
| 캐시 | revalidateTag, fetch cache | 브라우저 메모리 캐시 |
| 의존성 | Node 전용 SDK, 비공개 토큰 | DOM API, Web API |
경계를 나눈 뒤에는 "클라이언트 섬(island) 최소화"를 목표로 둔다. 하나의 페이지에 상호작용 영역 2~3개 수준이면 대부분 충분하다.
코드 예시 1: 서버 경계에서 데이터 조합
import { cache } from "react";
import { getPostList, getPopularTags } from "@/lib/posts/repository";
const loadPostsPage = cache(async () => {
const [posts, tags] = await Promise.all([
getPostList({ limit: 30 }),
getPopularTags({ limit: 20 }),
]);
return { posts, tags };
});
export default async function PostsPageServer() {
const { posts, tags } = await loadPostsPage();
return (
<>
<PostsHero total={posts.length} />
<PostsList posts={posts} />
<PopularTags tags={tags} />
</>
);
}
코드 예시 2: 상호작용만 클라이언트 섬으로 분리
"use client";
import { useMemo, useState } from "react";
export function PostFilterIsland({ tags }: { tags: string[] }) {
const [query, setQuery] = useState("");
const [selectedTag, setSelectedTag] = useState<string | null>(null);
const normalized = useMemo(() => query.trim().toLowerCase(), [query]);
return (
<section className="space-y-2">
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="검색어"
className="w-full rounded-md border px-3 py-2"
/>
<TagPills tags={tags} selected={selectedTag} onSelect={setSelectedTag} />
<p className="text-sm text-muted-foreground">query={normalized || "(empty)"}</p>
</section>
);
}
아키텍처 흐름
Mermaid diagram rendering...
트레이드오프
- 서버 중심으로 옮기면 번들은 줄지만, 컴포넌트 분할 규칙을 팀이 이해해야 한다.
- 클라이언트 섬을 최소화하면 성능은 좋아지지만, UI 상태 전달 설계가 중요해진다.
- 경계가 안정되면 페이지 성능 개선뿐 아니라 리뷰 범위도 작아진다.
정리
RSC 경계는 "서버냐 클라이언트냐"를 기술 취향으로 고르는 문제가 아니다. 데이터 책임과 상호작용 책임을 분리하는 아키텍처 결정이다. 경계 기준을 문서화하고 PR 체크리스트에 반영하면, 팀 전체 품질이 안정적으로 올라간다.
이미지 출처
- Cover: source link
- License: CC BY-SA 4.0 / Author: Lora Gilyard
- Note: Wikimedia Commons 무료 라이선스 이미지를 다운로드 후 1600px 기준 JPG로 최적화했습니다.