개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Next.js Client Components — 브라우저에서 실행되는 컴포넌트

2025년 5월 14일
Next.jsClient Components'use client'useState상호작용

Next.js App Router에서 컴포넌트는 기본적으로 Server Component로 동작한다. 그러나 버튼 클릭, 폼 입력, 애니메이션처럼 브라우저와 상호작용이 필요한 순간에는 Client Component가 필요하다. 'use client' 선언 하나로 컴포넌트의 실행 환경이 서버에서 브라우저로 전환된다.

'use client' 선언

파일 최상단에 'use client'를 추가하면 해당 파일과 그 하위 import 트리 전체가 클라이언트 번들에 포함된다.

'use client'

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(count + 1)}>
      클릭 횟수: {count}
    </button>
  )
}

'use client' — React가 이 모듈을 클라이언트 번들 진입점으로 표시하는 디렉티브. Next.js는 이 경계를 기준으로 서버/클라이언트 번들을 분리한다.

언제 Client Component를 사용하는가

필요 상황이유
useState, useReducer브라우저 메모리에 상태 유지 필요
useEffectDOM 마운트 이후 사이드 이펙트 실행
이벤트 핸들러 (onClick 등)브라우저 이벤트 루프에서만 동작
window, document 접근서버 환경에는 해당 전역 객체 없음
외부 클라이언트 라이브러리브라우저 API에 의존하는 패키지

Server Component와 조합 패턴

Client Component는 가능한 한 트리의 말단(leaf)에 배치하는 것이 핵심 원칙이다. 데이터 페칭과 레이아웃은 Server Component가 담당하고, 상호작용이 필요한 작은 부분만 Client Component로 분리한다.

Page (Server Component)
├── Header (Server Component)
│   └── NavMenu (Server Component)
├── ArticleList (Server Component)  ← DB 쿼리
│   └── LikeButton (Client Component) ← 클릭 이벤트
└── Footer (Server Component)

Server Component가 Client Component를 children prop으로 전달하면, Server Component의 렌더링 결과(직렬화된 JSX)가 클라이언트로 스트리밍된다.

// layout.tsx (Server Component)
import Modal from './Modal'        // Client Component
import ProductInfo from './ProductInfo' // Server Component

export default async function Page() {
  const product = await fetchProduct()

  return (
    <Modal>
      <ProductInfo product={product} />
    </Modal>
  )
}

children prop 패턴 — Server Component를 Client Component의 자식으로 넘기면 서버에서 미리 렌더링된 결과가 클라이언트 번들을 늘리지 않고 전달된다.

Client Component의 렌더링 흐름

빌드 타임
  └── 번들러가 'use client' 경계 감지
      └── 해당 모듈 → 클라이언트 JS 번들에 포함

요청 타임
  Server Component → HTML 생성 (스트리밍)
       │
       └── Client Component placeholder 삽입
               │
  브라우저 Hydration
       └── React가 placeholder에 이벤트 핸들러 연결

Hydration — 서버에서 생성된 정적 HTML에 React가 이벤트 리스너와 상태를 연결하는 과정. Client Component만 이 과정을 거친다.

useEffect 사용 시 주의점

useEffect는 서버에서 실행되지 않는다. 브라우저 전용 API(localStorage, navigator 등)는 반드시 useEffect 내부에서 접근해야 한다.

'use client'

import { useState, useEffect } from 'react'

export default function ThemeToggle() {
  const [theme, setTheme] = useState('light')

  useEffect(() => {
    const saved = localStorage.getItem('theme')
    if (saved) setTheme(saved)
  }, [])

  return <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>{theme}</button>
}

Client Component를 최소한으로 유지하고 Server Component와 올바르게 조합하면, 번들 크기를 줄이면서도 풍부한 상호작용을 구현할 수 있다.

← 이전 글Next.js Server Components — 서버에서만 실행되는 컴포넌트
다음 글 →Next.js Data Fetching — 서버에서 데이터를 가져오는 방법