Client Component는 브라우저에서 실행됩니다(초기 HTML을 위해 서버에서도 미리 렌더링됨). 파일 맨 위에 "use client" 지시문으로 옵트인합니다. 상호작용, 상태, 이펙트, 브라우저 API가 필요할 때마다 이것이 필요합니다.
선언하기
tsx
;
{ useState } ;
() {
[count, setCount] = ();
;
}
✓ useState / useReducer / useEffect / useRef / useContext
✓ 이벤트 핸들러 (onClick, onChange, onSubmit)
✓ 브라우저 API (window, localStorage, navigator, IntersectionObserver)
✓ 위 항목을 사용하는 서드파티 라이브러리 (대부분의 UI/애니메이션 라이브러리)
파일을 "use client"로 표시하면 그 파일과 그것이 import하는 모든 것이 클라이언트 번들의 일부가 됩니다. 그래서 클라이언트 경계는 작고 트리의 낮은 곳에 — "클라이언트 섬" — 두어야 합니다. 최상위 레이아웃을 클라이언트로 표시하면(앱 전체가 브라우저 번들로 끌려 들어감) 안 됩니다.
// ✅ 좋음: 서버 페이지, 필요한 곳에만 작은 클라이언트 섬
export default async function Page() { // Server Component
const data = await getData();
return <><StaticContent data={data} /><InteractiveWidget /></>; // 섬은 "use client"
}
// Server Component가 페칭하여 직렬화 가능한 props를 클라이언트 섬으로 전달
<LikeButton initialLikes={post.likes} postId={post.id} />
Server에서 Client Component로 전달되는 props는 서버→클라이언트 경계를 넘기 때문에 직렬화 가능해야 합니다(함수 없음, 클래스 인스턴스 없음).
Client Component는 서버 우선인 앱에서 상호작용을 추가하는 방법입니다.
핵심 기술은 이를 최소화하고 잎(leaf)에 두는 것입니다. 필요한 곳에만 상호작용을 두고 나머지는 Server Component로 — 그래야 JavaScript를 최소한으로 보냅니다.
"use client"를 트리의 높은 곳에 잘못 두는 것은 번들을 부풀리고 Server Components의 이점을 포기하는 흔한 실수입니다.