상호작용 경계를 선언해
상태, 이벤트, ref, 브라우저 API가 필요하거나 Framer Motion, Headless UI, React Hook Form처럼 hook에 기대는 라이브러리를 쓸 때는 파일 첫 줄에 'use client'를 적어. 이 선언이 서버 트리 안에서 브라우저가 맡을 경계를 만들어.
선언은 import 그래프를 따라 퍼져
'use client'는 그 파일 하나에만 붙는 딱지가 아니야. 그 파일이 import한 모듈들도 클라이언트 번들에 들어가. 그래서 페이지나 레이아웃 꼭대기에 붙이면 필요 없는 하위 코드까지 모두 브라우저로 보내게 돼. 선언은 가능한 한 상호작용이 있는 잎에 가까이 둬.
| 필요한 것 | 실행 위치 |
|---|---|
useState, useEffect, useRef | 클라이언트 |
onClick, onChange, onSubmit | 클라이언트 |
window, document, localStorage | 클라이언트 |
| hook 기반 외부 UI 라이브러리 | 클라이언트 |
| 평범한 데이터 표시 | 서버 |
| 데이터 fetch와 DB 조회 | 서버 |
| 비밀값 읽기 | 서버 |
| 무거운 데이터 가공 | 서버 |
서버 컴포넌트가 클라이언트 컴포넌트를 그리는 건 괜찮아. 경계를 넘겨야 할 값만 직렬화할 수 있으면 돼.
use client 파일이 import하는 목록을 코드 리뷰에서 함께 봐. 작은 토글이 거대한 chart와 데이터 도구를 끌고 오면 상호작용 잎을 더 작게 분리해. 서버 부모가 초기 값과 서버에서 렌더링한 children을 내려 주면 기능을 유지하면서 클라이언트 경계를 낮출 수 있어. 클라이언트 경계를 옮길 때는 기능만 보지 말고 번들에 새로 딸려오는 import도 비교해. 작은 상호작용 하나가 무거운 라이브러리 전체를 끌고 오면 경계가 너무 높은 거야. 클라이언트 컴포넌트도 첫 화면에서는 서버에서 미리 렌더링될 수 있어. 이름이 “클라이언트”라고 HTML이 브라우저에서만 생기는 건 아니야. 차이는 상호작용을 위해 코드가 브라우저로 전송되고 hydration된다는 점이니, 표시 위치와 실행 번들을 구분해. use client를 페이지 꼭대기와 작은 잎에 각각 두고 bundle analyzer 결과를 저장해. 외부 hook 라이브러리와 서버 children이 어느 chunk에 포함되는지 보고, 선언을 지웠을 때 이벤트와 브라우저 API가 빌드 단계에서 제대로 실패하는지도 확인해.