안녕하세요! 프론트엔드 강사입니다. Next.js 공식 문서를 통해 깊이 있게 공부하시려다니 정말 훌륭한 접근이네요! 영어 문서라 조금 막막하셨을 텐데, 제가 꼼꼼하게 다 번역해 드리고 실무 팁까지 팍팍 담아서 이해하기 쉽게 설명해 드릴게요. 자, 그럼 시작해 볼까요? 🚀
[문서 메타 정보]
use clientuse client 디렉티브를 어떻게 사용하는지 배워봅시다.'use client' 디렉티브(지시어)는 컴포넌트가 클라이언트 사이드(브라우저)에서 렌더링되도록 진입점(entry point)을 선언하는 역할을 해요. 상태 관리(useState 등), 이벤트 핸들링(onClick 등), 그리고 브라우저 API 접근처럼 클라이언트 측의 자바스크립트 기능이 필요한 상호작용적인 사용자 인터페이스(UI)를 만들 때 꼭 사용해야 하죠. 참고로 이건 Next.js만의 기능이 아니라 React 자체의 기능이랍니다!
👨🏫 강사의 부연 설명: Next.js의 App Router 환경에서는 기본적으로 모든 컴포넌트가 '서버 컴포넌트'로 동작해요. 그래서 브라우저에서 동작해야 하는 동적인 기능들(예: 버튼 클릭, 입력창 입력 등)을 넣으려면 명시적으로 "이건 클라이언트에서 실행해줘!"라고 알려줘야 하는데, 그 마법의 주문이 바로
'use client'랍니다.
알아두면 좋은 꿀팁! 💡
클라이언트 컴포넌트가 포함된 모든 파일에
'use client'디렉티브를 추가할 필요는 없어요! 서버 컴포넌트 내에서 직접 렌더링하고자 하는, 즉 '진입점'이 되는 컴포넌트 파일에만 추가하시면 됩니다.
'use client'디렉티브는 클라이언트와 서버 사이의 네트워크 경계(boundary)를 정의해 주거든요. 그래서 이렇게 정의된 파일에서 내보내진(exported) 컴포넌트들은 클라이언트로 들어가는 진입점 역할을 하게 된답니다.
👨🏫 강사의 팁: 실무에서 초보자분들이 제일 많이 하는 실수 중 하나가, 헷갈린다고 모든 파일 맨 위에
'use client'를 다 적어놓는 거예요! 하지만 방금 읽으신 것처럼,'use client'가 선언된 파일에서 자식 컴포넌트들을 import 해서 쓰면 그 자식들도 자동으로 클라이언트 환경에서 동작하게 됩니다. 그러니 최상단 진입점에 딱 한 번만 달아주시면 충분해요!
클라이언트 컴포넌트의 진입점을 선언하려면, 그 어떤 import 구문보다도 먼저 파일의 맨 꼭대기(최상단)에 'use client' 디렉티브를 추가해 주시면 됩니다.
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
)
}
'use client' 디렉티브를 사용할 때, 클라이언트 컴포넌트가 (부모로부터) 받는 props(속성값)는 반드시 직렬화(serializable) 가능한 형태여야만 합니다. 이게 무슨 뜻이냐면, 서버에서 클라이언트로 데이터를 보낼 때 React가 네트워크를 통해 전송할 수 있는 데이터 형식이어야 한다는 거예요.
👨🏫 강사의 부연 설명: 쉽게 말해 문자열, 숫자, 배열, 일반 객체(
{}) 등은 직렬화가 가능해서 네트워크를 타고 넘어갈 수 있어요. 하지만 함수(function)나 클래스 인스턴스, Date 객체 등은 네트워크를 통해 전송할 수 없습니다! 아래 예시를 한 번 볼까요?
'use client'
export default function Counter({
onClick /* ❌ Function is not serializable */,
}) {
return (
<div>
<button onClick={onClick}>Increment</button>
</div>
)
}
'use client'
export default function Counter({
onClick /* ❌ Function is not serializable */,
}) {
return (
<div>
<button onClick={onClick}>Increment</button>
</div>
)
}
👨🏫 강사의 팁: 위 코드에서 주석에 표시된 것처럼, 서버 컴포넌트에서 클라이언트 컴포넌트로 함수를 props로 넘겨주는 것은 불가능해요. 만약 상호작용이 필요하다면, 상태와 관련된 로직 자체를 클라이언트 컴포넌트 내부로 옮기거나, Next.js의 서버 액션(Server Actions)을 활용해 서버 측 로직을 안전하게 호출하는 방식을 사용해야 합니다!
서버 컴포넌트와 클라이언트 컴포넌트를 적절히 결합하면, 성능도 챙기면서 사용자와의 상호작용도 뛰어난 애플리케이션을 만들 수 있습니다.
다음 예시를 함께 볼까요?
Header는 정적 콘텐츠를 처리하는 서버 컴포넌트입니다.Counter는 페이지 내에서 사용자와의 상호작용을 가능하게 해주는 클라이언트 컴포넌트입니다.import Header from './header'
import Counter from './counter' // This is a Client Component
export default function Page() {
return (
<div>
<Header />
<Counter />
</div>
)
}
import Header from './header'
import Counter from './counter' // This is a Client Component
export default function Page() {
return (
<div>
<Header />
<Counter />
</div>
)
}
👨🏫 강사의 팁: 바로 이게 Next.js App Router의 핵심 패턴이에요! 화면의 뼈대나 무거운 데이터 작업은 서버에서 미리 렌더링해 가져오고(
Header), 사용자가 버튼을 누르는 등의 동적인 부분(Counter)만 클라이언트에서 처리하도록 구성하면 화면 로딩 속도가 비약적으로 빨라진답니다. 적재적소에 두 컴포넌트를 섞어 쓰는 연습을 해보시면 큰 도움이 될 거예요!
'use client'에 대해 더 깊이 알고 싶으시다면 React 공식 문서를 확인해 보세요!
모든 문서의 의미론적 개요(semantic overview)를 보시려면 https://nextjs.org/docs/sitemap.md를 참고해 주세요.
사용 가능한 모든 문서의 색인(index)을 보시려면 https://nextjs.org/docs/llms.txt를 참고해 주세요.