
Next.js의 App Router에서는 컴포넌트가 서버 컴포넌트(Server Component, RSC) 또는 클라이언트 컴포넌트(Client Component)로 나뉜다.
console.log("server")를 찍으면 터미널에서만 출력됨.✔ 대표적인 사용 예시
✔ 데이터베이스 요청 (DB 조회, API 호출)
✔ SEO가 중요한 정적인 페이지
✔ 초기 로딩이 빠른 UI를 제공할 때
✔ 인터렉션이 없는 모든 페이지
// 서버 컴포넌트 (기본값, "use client"가 없음)
export default async function ServerComponent() {
console.log("서버에서만 실행됨!");
const data = await fetch("https://api.example.com/data").then((res) => res.json());
return <div>서버에서 가져온 데이터: {data.name}</div>;
}
onClick, useState, useEffect 등 사용 가능).useEffect 필요)."use client" 선언이 필요함.✔ 대표적인 사용 예시
✔ 버튼 클릭, 입력 필드, 모달, 드롭다운 등
✔ 브라우저에서 실행해야 하는 기능 (localStorage, window 객체 등)
// 클라이언트 컴포넌트 (상단에 "use client" 선언 필수)
"use client";
import { useState } from "react";
export default function ClientComponent() {
const [count, setCount] = useState(0);
return (
<div>
<p>클라이언트에서 실행되는 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
💡 정리하면?
- 서버 컴포넌트는 HTML에 포함되어 우선 렌더링 됨 → 빠른 FCP (SEO, 초기 로딩 성능 우수)
- 클라이언트 컴포넌트만 Hydration 할 때 재실행 → 인터랙션 가능해짐
📌 서버 컴포넌트는 서버에서만 실행되는데, 클라이언트 컴포넌트는 두 번 실행되므로 불가능!
import하면, 클라이언트에서 강제로 서버 컴포넌트를 실행해야 하므로 오류 발생 ⚠️Next.js는 오류 방지를 위해 서버 컴포넌트를 강제로 클라이언트 컴포넌트로 변환시킴. 이 과정에서 불필요한 클라이언트 컴포넌트가 발생하므로 지양할 것.❌ 잘못된 예시
"use client";
import ServerComponent from "./ServerComponent"; // ❌ 오류 발생
export default function ClientComponent() {
return <ServerComponent />;
}
✅ 올바른 해결 방법
서버 컴포넌트를 직접 import하지 않고, children으로 넘겨서 렌더링해야 한다.
"use client";
export default function ClientComponent({ children }: { children: React.ReactNode }) {
return <div>{children}</div>;
}
// ServerComponent.tsx
export default function ServerComponent() {
return <p>이것은 서버 컴포넌트입니다!</p>;
}
// 사용 예시
import ServerComponent from "./ServerComponent";
import ClientComponent from "./ClientComponent";
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
);
}
📌 직렬화(Serialization)란?
서버에서 데이터를 전송할 때, { name: "Alice", age: 18 } 같은 객체는 직렬화가 가능하지만,
함수(() => {}), Date, Map, Set, RegExp 같은 데이터는 직렬화가 불가능하다.
❌ 잘못된 예시 (서버 컴포넌트 → 클라이언트 컴포넌트로 함수 전달)
export default function ServerComponent() {
const handleClick = () => {
console.log("클릭!");
};
return <ClientComponent onClick={handleClick} />; // ❌ 오류 발생
}
⚠️ 왜?
handleClick 함수는 문자열로 변환할 수 없으므로, 클라이언트 컴포넌트로 전달되지 않음.✅ 올바른 해결 방법 (클라이언트에서 함수 정의)
"use client";
export default function ClientComponent() {
const handleClick = () => {
console.log("클릭!");
};
return <button onClick={handleClick}>클릭</button>;
}
| 구분 | 서버 컴포넌트 (Server Component) | 클라이언트 컴포넌트 (Client Component) |
|---|---|---|
| 실행 위치 | 서버에서만 실행 | 서버 & 클라이언트 모두 실행 |
useState, useEffect 사용 | ❌ 불가능 | ✅ 가능 |
이벤트 핸들러 (onClick) | ❌ 불가능 | ✅ 가능 |
| SEO 최적화 | ✅ 가능 | ❌ 어려움 |
| JS 번들 크기 | 📉 줄어듦 | 📈 커질 수 있음 |
| 직접 import 가능? | ✅ 가능 | ❌ 불가능 (children으로 전달해야 함) |
| 직렬화 불가능한 데이터 전달 | ❌ 불가능 | ✅ 가능 (내부에서 정의) |
import하지 않도록 주의!