최근에 Next.js를 계속 공부하고 있는데 그 중 가장 헷갈렸던 부분 중 하나가 'use client' 지시어와 React Hooks의 관계였다.
Next.js 13 버전부터 도입된 App Router와 함께 서버 컴포넌트가 기본값이 되면서, React의 useState, useEffect와 같은 훅을 사용하기 위해서는 'use client' 지시어가 필요하게 되었다.
이것이 정확히 무엇을 의미하는지, 그리고 이로 인해 SSR(Server-Side Rendering)과 어떤 관계가 있는지 깊이 이해하고 싶어 공부를 시작했고, 이 글에서는 Next.js에서 React Hooks를 사용할 때 'use client' 지시어가 필요한 이유와 이것이 렌더링 방식에 미치는 영향에 대해 자세히 살펴보려 한다.
먼저 Next.js의 렌더링 방식을 이해하고자 하는 분들은 아래 글을 먼저 읽고 오는게 좋을 것 같다.
[Next.js] SSR이란 무엇인가? - Next.js의 서버 사이드 렌더링 이해하기
Next.js와 SSR최근 Next.js를 공부하면서 SSR(Server Side Rendering)에 대한 개념이 혼란스러웠다.특히 "Next.js는 기본적으로 SSR이 적용되어 있다"는 말과 "서버를 구현해야 SSR을 사용할 수 있다"는 말이 서로
bbin-guuuu.tistory.com
React Hooks와 'use client' 지시어
React Hooks는 함수형 컴포넌트에서 상태 관리와 생명주기 기능을 사용할 수 있게 해주는 React의 기능이다.
useState, useEffect, useContext 등의 Hooks는 클라이언트 측 브라우저 환경에서 실행되도록 설계되었다.
이전 글에서도 몇번 말했듯이
Next.js의 App Router에서는 모든 컴포넌트가 기본적으로 서버 컴포넌트이기 때문에, React Hooks를 사용하려면 해당 컴포넌트가 클라이언트에서 실행되어야 한다는 것을 명시적으로 선언해야 한다.
이를 위해 사용하는 것이 바로 'use client' 지시어다!!!
'use client';
import React, { useState, useEffect } from 'react';
export default function ClientComponent() {
const [clientTime, setClientTime] = useState<string>('');
useEffect(() => {
// 브라우저 환경에서만 실행되는 코드
setClientTime(new Date().toLocaleTimeString());
const timer = setInterval(() => {
setClientTime(new Date().toLocaleTimeString());
}, 1000);
return () => clearInterval(timer);
}, []);
return (
<div>
<h1>클라이언트 컴포넌트 예시</h1>
<p>클라이언트 시간: {clientTime}</p>
</div>
);
}
위 코드에서는 파일 최상단에 'use client' 지시어를 추가하여 이 컴포넌트가 클라이언트 컴포넌트임을 명시하고 있다.
이렇게 하면 useState와 useEffect 같은 React Hooks를 사용할 수 있게 된다.
이 예시에서는 클라이언트의 현재 시간을 가져와 1초마다 업데이트하는 기능을 구현했다.
이러한 동적 상태 관리와 브라우저 API 사용은 클라이언트 컴포넌트에서만 가능하다.
서버 컴포넌트 vs 클라이언트 컴포넌트
서버 컴포넌트와 클라이언트 컴포넌트의 차이점을 명확히 이해하는 것이 중요하다.
서버 컴포넌트 특징:
- 서버에서만 실행된다.
- 클라이언트로 HTML만 전송되므로 JavaScript 번들 크기를 줄일 수 있다.
- 데이터베이스나 파일 시스템과 같은 서버 자원에 직접 접근할 수 있다.
- React Hooks (useState, useEffect 등)를 사용할 수 없다.
- 브라우저 API (localStorage, window 객체 등)에 접근할 수 없다.
클라이언트 컴포넌트 특징:
- 서버에서 초기 HTML을 생성한 후 클라이언트에서 hydration(자바스크립트 연결 과정)이 일어난다.
- React Hooks와 이벤트 핸들러를 사용할 수 있다.
- 브라우저 API에 접근할 수 있다.
- 사용자 상호작용이 필요한 UI 요소를 구현할 수 있다.
- 'use client' 지시어로 선언해야 한다.
다음은 서버 컴포넌트와 클라이언트 컴포넌트를 함께 사용하는 예시다.
// page.tsx (서버 컴포넌트)
import ClientCounter from './ClientCounter';
import { getServerData } from './data-utils';
export default async function Page() {
// 서버에서 데이터를 가져오는 비동기 함수
const data = await getServerData();
return (
<div>
<h1>서버에서 가져온 데이터</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
{/* 클라이언트 컴포넌트 사용 */}
<ClientCounter initialCount={data.count} />
</div>
);
}
// ClientCounter.tsx (클라이언트 컴포넌트)
'use client';
import { useState } from 'react';
interface CounterProps {
initialCount: number;
}
export default function ClientCounter({ initialCount }: CounterProps) {
const [count, setCount] = useState<number>(initialCount);
return (
<div>
<h2>클라이언트 카운터</h2>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
<button onClick={() => setCount(count - 1)}>감소</button>
</div>
);
}
이 예시에서 Page 컴포넌트는 서버 컴포넌트로, 서버에서 데이터를 가져와 렌더링한다.
그리고 ClientCounter라는 클라이언트 컴포넌트를 자식으로 포함하여, 사용자 상호작용이 필요한 카운터 기능을 구현했다.
서버에서 가져온 데이터(initialCount)를 클라이언트 컴포넌트에 전달하여 초기값으로 사용하는 패턴이다.
✔︎결론✔︎
React Hooks와 브라우저 API는 클라이언트 환경에서만 동작하므로, 'use client'지시어를 파일 최상단에 추가하여 해당 컴포넌트를 클라이언트 컴포넌트로 만들고,
클라이언트 컴포넌트 + 서버 컴포넌트로 적절히 프로젝트를 구성하면 된다!!!
'Next.js' 카테고리의 다른 글
[Next.js] Next.js + TypeScript에서의 라우팅 시 데이터 전달 방법 총정리 (0) | 2025.03.18 |
---|---|
[Next.js] Next.js + TypeScript에서의 페이지 이동 방법 총정리 (0) | 2025.03.17 |
[Next.js] ❗️입문자 주목❗️ TypeScript로 구현하는 로그인 시스템 튜토리얼 (0) | 2025.03.14 |
[Next.js] App Router vs Pages Router (4) | 2025.03.13 |
[Next.js] SSR이란 무엇인가? - Next.js의 서버 사이드 렌더링 이해하기 (1) | 2025.03.12 |