Next.js에서 React 서버 및 클라이언트 컴포넌트: 차이점 이해
Source: Dev.to
위에 제공된 텍스트가 없습니다. 번역을 원하는 본문을 알려주시면 그대로 한국어로 번역해 드리겠습니다.
소개
React 팀은 React 18에서 실험적 기능으로 Server Components를 도입했습니다. 이를 통해 React 애플리케이션의 일부를 서버에서 렌더링할 수 있어 클라이언트에 전달되는 JavaScript 양을 줄이고 보다 효율적인 데이터 페칭을 가능하게 합니다.
Next.js는 이 개념을 App Router(버전 13에서 도입)에서 채택했습니다. 이 프레임워크는 React의 서버‑렌더링 기능을 자체 라우팅, 데이터 페칭, 성능 최적화와 결합합니다.
서버 컴포넌트
- 서버에서만 실행됩니다 – 브라우저에서는 절대 실행되지 않습니다.
useState,useEffect,window와 같은 클라이언트 전용 기능을 사용할 수 없습니다.- 데이터베이스나 API에서 안전하게 데이터를 가져와 결과 HTML만 반환할 수 있습니다.
- 클라이언트 번들에서 JavaScript를 제외하므로 번들 크기가 감소하고 성능이 향상됩니다.
- 클라이언트 컴포넌트를 import 할 수 있지만, 클라이언트 컴포넌트는 서버 컴포넌트를 import 할 수 없습니다.
// This is a Server Component (default)
export default async function ServerComponent() {
const posts = await fetchPosts();
return (
<ul>
{posts.map(post => (
<li key={post.id}>- {post.title}</li>
))}
</ul>
);
}
기본적으로 Next.js App Router(
app/디렉터리) 내의 모든 컴포넌트는 별도로 표시하지 않는 한 서버 컴포넌트입니다.
클라이언트 컴포넌트
클라이언트 컴포넌트는 브라우저에서 실행되는 기존 React 컴포넌트입니다. 인터랙티브 기능, 상태 관리, 혹은 부수 효과가 필요할 때 사용합니다.
파일을 클라이언트 컴포넌트로 지정하려면 파일 상단에 "use client" 지시자를 추가합니다:
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
핵심 포인트
- 초기 HTML은 서버에서 렌더링(프리렌더)되고, 이후 브라우저에서 하이드레이션됩니다.
- React 상태 훅(
useState,useEffect등)과 브라우저 API를 사용할 수 있습니다. - 서버 컴포넌트를 직접 import 할 수는 없지만, 프롭스로 전달받을 수 있습니다.
Source:
서버와 클라이언트 컴포넌트 구성
서버 컴포넌트를 클라이언트 컴포넌트에 전달하기
서버 컴포넌트는 다른 서버 컴포넌트를 렌더링하고 이를 클라이언트 컴포넌트에 prop으로 전달할 수 있습니다. 서버는 노드를 렌더링하고 직렬화한 뒤, 클라이언트는 이를 React 요소로 받습니다.
// Server Component
export function ServerInfo() {
return <div>Server time: {new Date().toISOString()}</div>;
}
// Client Component (Wrapper)
"use client";
export function Wrapper({ info }) {
return (
<section>
## Wrapped content:
{info}
</section>
);
}
// Server Page
import { ServerInfo } from "./ServerInfo";
import { Wrapper } from "./Wrapper";
export default function Page() {
return <Wrapper info={<ServerInfo />} />;
}
서버 컴포넌트 안에서 클라이언트 컴포넌트 사용하기
// Server Component (page)
import Counter from "./Counter"; // ✅ 허용 (클라이언트 컴포넌트)
export default async function Page() {
const user = await fetchUser();
return (
<section>
<h1>Hello, {user.name}</h1>
<Counter />
</section>
);
}
하이드레이션
Hydration은 React가 서버에서 생성된 정적 HTML에 인터랙티브 기능을 부착하는 과정입니다. 일반적인 함정은 다음과 같습니다:
- 비결정적 렌더링 (예:
Math.random(),Date.now()) 은 서버와 클라이언트에서 서로 다른 마크업을 생성합니다. - 서버와 클라이언트 간의 조건부 렌더링 차이.
- 서버와 클라이언트 데이터가 달라질 때 발생하는 props 불일치.
모범 사례
- 양쪽에서 렌더링되는 컴포넌트에서는 결정론적 데이터를 사용하세요.
- 인터랙티브 기능은 작고 명확히 정의된 클라이언트 컴포넌트에 격리하세요.
- 동적 로직은
useEffect혹은 기타 클라이언트 전용 영역에 배치하세요.
비교
| 기능 | 서버 컴포넌트 | 클라이언트 컴포넌트 |
|---|---|---|
| 실행 | 서버만 | 서버 사전 렌더링 + 브라우저 하이드레이션 |
React 상태 훅(useState, useEffect) 사용 가능 | ❌ | ✅ |
| 보안 데이터 가져오기 | ✅ (직접) | ⚠️ (API 라우트를 통해) |
| 임포트 방향 | ✅ 클라이언트 컴포넌트 임포트 가능 | ❌ 서버 컴포넌트 임포트 불가 |
| 서버 컴포넌트를 props 로 받을 수 있음 | ✅ | ✅ |
| 번들 크기 영향 | 최소 | 번들 크기 증가 |
| SEO | 우수 | 또한 좋음 (사전 렌더링) |
| 일반적인 사용 | 정적 콘텐츠, 데이터‑중심 렌더링 | 인터랙티브 UI, 폼, 애니메이션 |
요약
- Server Components는 데이터가 많거나 정적 콘텐츠에 이상적이며, 서버에서만 실행되어 클라이언트 번들을 작게 유지하고 SEO를 향상시킵니다.
- Client Components는 인터랙티브성을 제공하며, 서버에서 사전 렌더링된 후 브라우저에서 하이드레이션됩니다.
- import 흐름은 Server → Client(허용)이며 Client → Server는 금지됩니다.
- Server Components를 Client Components에 props로 전달하면 두 렌더링 모델의 장점을 유지하면서 강력한 컴포지션이 가능합니다.
올바르게 사용하면 이 아키텍처는 Next.js 앱을 더 빠르고, 더 안전하며, 유지보수가 쉬워지게 합니다—현대 React 개발에 진정한 이점이 됩니다.