핵심 요약
App Router에서 모든 컴포넌트는 기본 서버 컴포넌트다. "use client"는 "여기부터 아래 전부 클라이언트 번들"이라는 경계 선언이므로, 상태·이벤트·브라우저 API가 필요한 잎(leaf) 컴포넌트에만 붙여야 한다. 위쪽에 붙일수록 JS 번들이 커진다.
1. 원칙
- 데이터 패칭·정적 마크업 → 서버 컴포넌트(기본)
- onClick·useState·useEffect·window → 클라이언트 컴포넌트
- 서버 컴포넌트는 클라이언트 컴포넌트를 자식으로 렌더할 수 있다(반대로 import는 불가)
2. 경계를 아래로 미는 패턴
// page.tsx (서버) — 데이터는 서버에서
export default async function Page() {
const posts = await getPosts()
return <PostList posts={posts}><LikeButton /></PostList>
}
// LikeButton.tsx 만 "use client"
3. 함정
- 레이아웃·페이지 최상단에 use client 박으면 SSR 이점 상실
- 서버 컴포넌트에서
useState쓰면 에러 — 클라이언트로 분리 - 클라이언트 컴포넌트에 서버 전용 모듈(예: DB) import 금지 — children으로 주입
자주 묻는 질문
use client를 한 번 쓰면 그 아래 전부 클라이언트인가요?
네. import로 연결된 하위 모듈이 모두 클라이언트 번들에 포함됩니다. 그래서 경계를 최대한 잎 쪽으로 내려야 번들이 작아집니다.
서버 컴포넌트에서 클라이언트 컴포넌트를 어떻게 쓰나요?
그냥 import해서 렌더하면 됩니다. 반대로 클라이언트에서 서버 컴포넌트를 import는 못 하고, children prop으로 주입받아야 합니다.

댓글 0