Next.js App Router를 학습할 때 가장 먼저 이해해야 하는 개념 중 하나가 Server Component와 Client Component입니다. 두 컴포넌트는 모두 React 컴포넌트처럼 보이지만, 실행되는 위치와 사용할 수 있는 기능이 다릅니다. 이 차이를 제대로 이해하지 못하면 데이터 조회, 이벤트 처리, 상태 관리, 성능 최적화에서 자주 혼란을 겪게 됩니다.
Server Component란?
Server Component는 이름 그대로 서버에서 실행되는 컴포넌트입니다. Next.js App Router에서는 기본적으로 모든 컴포넌트가 Server Component로 동작합니다. 즉, 파일 상단에 별도의 설정을 하지 않으면 해당 컴포넌트는 서버에서 렌더링됩니다.
Server Component의 가장 큰 장점은 서버에서 직접 데이터를 가져올 수 있다는 점입니다. 예를 들어 데이터베이스 조회, 외부 API 호출, 파일 시스템 접근 같은 작업을 컴포넌트 내부에서 바로 처리할 수 있습니다.
export default async function ProductList() {
const products = await fetch("https://example.com/api/products").then(res =>
res.json()
);
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
위 코드는 서버에서 실행되기 때문에 브라우저에 불필요한 JavaScript를 보내지 않습니다. 사용자는 이미 완성된 HTML에 가까운 결과를 받게 되고, 초기 로딩 성능에도 유리합니다.
또한 Server Component에서는 민감한 정보를 안전하게 다룰 수 있습니다. 데이터베이스 접속 정보, API Secret Key, 서버 전용 로직 등을 클라이언트에 노출하지 않고 사용할 수 있습니다. 그래서 게시글 목록, 상품 상세, 사용자 정보 조회처럼 “화면에 보여줄 데이터를 서버에서 가져오는 역할”에 적합합니다.
다만 Server Component에는 제한도 있습니다. 브라우저에서만 동작하는 기능은 사용할 수 없습니다. 예를 들어 useState, useEffect, onClick, window, localStorage 같은 기능은 Server Component에서 사용할 수 없습니다. 이런 기능은 브라우저 환경이 필요하기 때문에 Client Component에서 처리해야 합니다.
Client Component란?
Client Component는 브라우저에서 실행되는 컴포넌트입니다. 사용자의 클릭, 입력, 상태 변경처럼 상호작용이 필요한 UI를 만들 때 사용합니다. Client Component로 만들려면 파일 상단에 "use client"를 선언해야 합니다.
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
클릭 수: {count}
</button>
);
}
위 컴포넌트는 버튼 클릭에 따라 상태가 변경됩니다. 이런 동작은 서버에서 처리할 수 없기 때문에 Client Component가 필요합니다.
Client Component에서는 React의 상태 관리 Hook을 사용할 수 있습니다. 대표적으로 useState, useEffect, useRef, useReducer 등을 사용할 수 있고, 브라우저 API인 window, document, localStorage에도 접근할 수 있습니다. 모달, 드롭다운, 탭 메뉴, 검색 입력창, 좋아요 버튼, 폼 입력 처리 등은 Client Component로 만드는 경우가 많습니다.
하지만 Client Component를 많이 사용할수록 브라우저로 전달되는 JavaScript 양이 증가합니다. 이는 초기 로딩 속도와 성능에 영향을 줄 수 있습니다. 따라서 모든 컴포넌트를 Client Component로 만드는 것은 좋은 방식이 아닙니다. 꼭 상호작용이 필요한 부분만 Client Component로 분리하는 것이 좋습니다.