Next.js에서 React 서버 및 클라이언트 컴포넌트: 차이점 이해

발행: (2025년 12월 31일 오전 08:00 GMT+9)
8 min read
원문: Dev.to

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 개발에 진정한 이점이 됩니다.

Back to Blog

관련 글

더 보기 »