'use client'는 CSR 스위치가 아니에요
그로스앤디 · 2026-10-07
오늘 배운 것 한 줄
Next.js App Router에서는 컴포넌트가 기본으로 서버에서 돌아요. 그리고 ’use client’는 브라우저에서 그리라는 스위치가 아니라, 여기서부터 브라우저로 보낼 코드라는 경계 표시다.
왜 중요한가
처음엔 이름만 보고 CSR로 바꾸는 지시어인 줄 알았어요. 그런데 ’use client’를 붙인 컴포넌트도 첫 HTML은 서버가 만들어요. 브라우저는 그 HTML 위에 클릭 같은 기능을 붙이고요(hydration). 달라지는 건 그 파일의 JavaScript가 브라우저로 내려간다는 점이에요.
그래서 붙이는 위치가 중요해요. 이 경계는 아래로 번지거든요. ‘use client’ 파일이 불러오는 컴포넌트도 전부 브라우저 몫이 돼요. 페이지 맨 위에 붙이면 보여 주기만 하는 부분까지 다 내려가요.
어제 배운 이벤트 위임은 이벤트를 어디서 들을지의 문제였어요. 오늘은 이벤트가 어디서 돌 수 있는지의 문제였고요. 서버에는 클릭이 없으니까요.
내 예시
TIL 카드에 북마크 버튼을 단다고 해 볼게요.
// BookmarkButton.tsx — 클릭이 있는 가장 작은 단위에만
'use client'; // import보다 위, 파일 첫 줄
import { useState } from 'react';
export default function BookmarkButton() {
const [saved, setSaved] = useState(false);
return (
<button aria-pressed={saved} onClick={() => setSaved(!saved)}>
{saved ? '저장됨' : '나중에 읽기'}
</button>
);
}
// TilCard.tsx — 지시어 없음. 서버 컴포넌트로 남는다
import BookmarkButton from './BookmarkButton';
export default function TilCard({ title }: { title: string }) {
return (
<article>
<h3>{title}</h3>
<BookmarkButton />
</article>
);
}
카드는 서버에 두고 버튼만 브라우저로 보내요. 제목처럼 보여 주기만 하는 부분은 컴포넌트 코드 없이 HTML로만 도착해요.
다음에 해 볼 것
’use client’를 일부러 빼고 onClick을 써서 실제 에러 메시지를 직접 확인해 볼 거예요. 그리고 클라이언트 컴포넌트 안에 서버 컴포넌트를 children으로 넣는 패턴도 손으로 해 볼게요.
'use client' 경계 — 기본은 서버 컴포넌트다 · Prerender 와 Hydration — 뼈대를 먼저 보내고 기능을 붓는다