서버 컴포넌트(RSC) vs 클라이언트 컴포넌트
Next.js 13부터 도입된 App Router 환경에서는 기본적으로 모든 컴포넌트가 서버 컴포넌트(React Server Components)입니다. 이 혁신적인 패러다임은 서버와 클라이언트가 각자 가장 잘하는 일에 집중하게 하여, 페이지 로딩 속도를 극대화하고 보안을 강화합니다.
1. 서버 컴포넌트 (Server Components) 란?
Next.js의 모든 컴포넌트는 아무런 선언을 하지 않으면 기본적으로 서버 컴포넌트로 동작합니다. 말 그대로 브라우저가 아닌 서버에서만 실행되는 컴포넌트입니다.
- 뛰어난 성능 (Zero Bundle Size): 렌더링된 HTML 껍데기만 클라이언트로 보내므로 클라이언트가 다운로드해야 할 JavaScript 코드가 획기적으로 줄어듭니다.
- 안전한 백엔드 접근: 컴포넌트 내에서 데이터베이스 비밀번호나 API Key를 직접 다루더라도 브라우저에 노출되지 않으므로 매우 안전합니다.
- 검색 엔진 최적화 (SEO): 완성된 HTML을 즉시 제공하므로 구글과 같은 크롤러 봇이 내용을 완벽하게 수집할 수 있습니다.
2. 클라이언트 컴포넌트 (Client Components) 와 "use client"
사용자와의 동적인 상호작용(Interaction)이 필요할 때는 클라이언트 컴포넌트를 사용해야 합니다. 파일의 가장 첫 번째 줄에 "use client"; 라고 적어주면 클라이언트 컴포넌트로 전환됩니다.
- 버튼 클릭(
onClick), 입력창 변경(onChange) 등 이벤트 리스너가 필요할 때 - React의 상태 관리(
useState,useReducer)나 생명주기 훅(useEffect)이 필요할 때 window,document등 브라우저 전용 API를 써야 할 때
주의점: "use client"를 남발하면 Next.js App Router의 성능적 이점을 잃게 됩니다. 전체 페이지를 "use client"로 만들기보다는, 꼭 필요한 버튼이나 폼(Form) 요소만 잘게 쪼개서 클라이언트 컴포넌트로 분리하는 것이 핵심 최적화 기법입니다.
3. 어떻게 조합해야 할까? (Composition Pattern)
현대적인 Next.js 개발의 핵심은 서버 컴포넌트를 베이스로 깔고, 그 안의 인터랙티브한 작은 조각들을 클라이언트 컴포넌트로 끼워 넣는 것(Colocation)입니다.
✅ 올바른 패턴: <페이지(서버)> 안에서 <좋아요 버튼(클라이언트)> 컴포넌트를 import 해서 사용하기.
❌ 피해야 할 패턴: 클라이언트 컴포넌트 내부에서 서버 컴포넌트를 import 하기. (클라이언트 컴포넌트 하위에 있는 모든 컴포넌트는 자동으로 클라이언트 환경에서 실행되어 버립니다.)
// ==========================================
// 📂 app/page.tsx (서버 컴포넌트)
// ==========================================
import LikeButton from './LikeButton';
export default async function BlogPostPage() {
// DB에서 데이터 직접 조회 (서버단에서 안전하고 빠르게 실행됨)
// 서버 컴포넌트이므로 "use client"가 필요 없습니다.
const post = await db.post.findUnique({ id: 1 });
return (
<article className="max-w-2xl mx-auto p-8">
{/* 정적인 콘텐츠는 서버에서 렌더링되어 완성된 HTML로 전송됨 (SEO 완벽) */}
<h1 className="text-3xl font-bold">{post.title}</h1>
<div className="mt-4 text-gray-700">{post.content}</div>
<div className="mt-8 border-t pt-4">
{/* 사용자와 상호작용하는 부분만 클라이언트 컴포넌트로 분리하여 삽입 */}
<LikeButton initialCount={post.likes} />
</div>
</article>
);
}
// ==========================================
// 📂 app/LikeButton.tsx (클라이언트 컴포넌트)
// ==========================================
"use client"; // 파일의 최상단에 선언하여 클라이언트 컴포넌트임을 명시!
import { useState } from 'react';
export default function LikeButton({ initialCount }: { initialCount: number }) {
// useState와 onClick 같은 이벤트는 클라이언트 컴포넌트에서만 사용 가능합니다.
const [likes, setLikes] = useState(initialCount);
return (
<button
onClick={() => setLikes(likes + 1)}
className="px-4 py-2 bg-pink-500 text-white rounded-lg hover:bg-pink-600 transition"
>
❤️ 좋아요 {likes}
</button>
);
}