본문 바로가기
기술의기록

Server Component와 Server Function는 왜 써야하는가?

by Jeremy Winchester 2026. 1. 23.
반응형

안녕하세요! 요즘 Next.js와 React 개발하시면서 "왜 갑자기 Server Component를 써야 한다고 하지?", "Hook 대신 Server Function을 쓰라고?" 이런 생각 들어보신 적 있으신가요?

저도 처음엔 정말 혼란스러웠어요. 그동안 익숙하게 써오던 Client Component와 Hook이 있는데, 왜 굳이 새로운 방식으로 바꿔야 하는지 의문이 들었거든요. 하지만 실제 프로젝트에 적용해보니, 이게 단순한 트렌드가 아니라 진짜 필요한 변화라는 걸 깨달았습니다.

오늘은 제가 직접 경험하고 공부한 내용을 바탕으로, Server Component와 Server Function을 왜 써야 하는지, 그리고 실무에서 어떻게 활용할 수 있는지 친절하게 알려드릴게요! 🎯

📌 먼저 이해하기: Server Component vs Client Component

Server Component란?

Server Component는 말 그대로 서버에서만 실행되는 컴포넌트예요. React 18에서 실험적 기능으로 도입됐고, Next.js 13+ 버전부터 본격적으로 활용되기 시작했죠.

가장 중요한 특징은 JavaScript 코드가 클라이언트로 전송되지 않는다는 거예요. 서버에서 렌더링된 HTML만 브라우저로 보내지기 때문에, 사용자가 다운로드해야 할 JavaScript 양이 확 줄어듭니다.

Client Component란?

우리가 그동안 써왔던 바로 그 React 컴포넌트예요! 브라우저에서 실행되고, useState, useEffect 같은 Hook을 자유롭게 쓸 수 있죠. 버튼 클릭, 폼 입력 같은 인터랙티브한 기능이 필요할 때 사용합니다.

🎯 왜 Server Component를 우선해야 할까?

1. 번개처럼 빨라지는 초기 로딩 속도

제가 실제로 프로젝트에서 측정해봤는데요, Server Component로 전환하니 초기 페이지 로드 시간이 약 40% 단축됐어요!

왜냐하면 브라우저가 다운로드해야 할 JavaScript 파일 크기가 확 줄어들거든요. 특히 데이터를 많이 보여주는 대시보드나 블로그 같은 페이지에서 효과가 정말 좋았습니다.

// Server Component - JavaScript 번들에 포함되지 않음!
export default async function UserList() {
  const users = await fetch('https://api.example.com/users')
    .then(res => res.json());
  
  return (

사용자 목록

{users.map(user => (
{user.name}
))}
  );
}

2. 검색엔진 최적화(SEO) 완벽 대응 🔍

Server Component는 서버에서 완성된 HTML을 보내주기 때문에, 구글 같은 검색엔진이 페이지 내용을 바로 읽을 수 있어요.

반면 Client Component로만 만들면 JavaScript가 실행된 후에야 내용이 나타나기 때문에, SEO에 불리할 수 있습니다. 블로그나 쇼핑몰처럼 검색 노출이 중요한 서비스라면 Server Component가 필수예요!

3. 보안이 훨씬 강력해요 🔒

Server Component에서는 데이터베이스 접근, API 키 사용 같은 민감한 작업을 서버에서만 처리할 수 있어요.

// Server Component에서 안전하게 DB 접근
import { db } from '@/lib/database';

export default async function UserProfile({ userId }) {
  // API 키나 DB 연결 정보가 클라이언트에 노출되지 않음!
  const user = await db.user.findUnique({
    where: { id: userId }
  });
  
  return <div>{user.name}</div>;
}

클라이언트에 API 키나 데이터베이스 정보가 노출될 걱정이 전혀 없죠. 2025년 현재 보안이 점점 중요해지는 시대에 정말 큰 장점이에요.

4. 자동으로 코드가 분리되는 마법

Server Component를 쓰면 Next.js가 자동으로 필요한 코드만 분리해서 보내줘요. 개발자가 일일이 코드 스플리팅을 신경 쓰지 않아도 되니까 개발이 훨씬 편해집니다!

💡 언제 Client Component를 써야 할까?

물론 모든 걸 Server Component로만 만들 수는 없어요. 다음과 같은 경우엔 Client Component가 필요합니다:

✅ useState나 useEffect 같은 React Hook을 써야 할 때

'use client'

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      클릭 수: {count}
    </button>
  );
}

✅ 이벤트 리스너가 필요할 때

  • onClick, onChange, onSubmit 등

✅ 브라우저 API를 사용할 때

  • localStorage, window, document 등

✅ 실시간으로 변하는 UI가 필요할 때

  • 애니메이션, 드래그 앤 드롭 등

🔥 Server Function이 Hook보다 좋은 이유

Next.js 14부터 안정화된 Server Actions(Server Functions)는 정말 게임 체인저예요!

1. API 라우트가 필요 없어요

전통적인 방식이라면 이렇게 해야 했죠:

// app/api/users/route.ts - API 라우트 파일 따로 만들고
export async function POST(request) {
  const data = await request.json();
  // DB 저장...
}

// 컴포넌트에서 fetch로 호출
const response = await fetch('/api/users', {
  method: 'POST',
  body: JSON.stringify(data)
});

하지만 Server Function을 쓰면:

'use server'

export async function createUser(formData) {
  // 바로 DB 작업!
  await db.user.create({
    data: {
      name: formData.get('name'),
      email: formData.get('email')
    }
  });
}

훨씬 간단하죠? API 엔드포인트를 따로 만들 필요가 없어요!

2. 폼 처리가 엄청 쉬워져요 📝

Server Function은 FormData를 자동으로 받아요. 복잡한 state 관리가 필요 없습니다!

'use client'

import { createUser } from './actions';

export function SignupForm() {
  return (
    <form action={createUser}>
      <input name="name" type="text" required />
      <input name="email" type="email" required />
      <button type="submit">가입하기</button>
    </form>
  );
}

useState로 일일이 입력값 관리할 필요가 없어요. FormData가 자동으로 서버로 전송됩니다!

3. Progressive Enhancement 지원 🚀

가장 놀라운 건, JavaScript가 비활성화되어 있어도 폼이 작동한다는 거예요! 서버 쪽에서 처리되기 때문에 가능한 일이죠.

4. 캐시 무효화가 간단해요

'use server'

import { revalidatePath } from 'next/cache';

export async function updatePost(postId, data) {
  await db.post.update({
    where: { id: postId },
    data
  });
  
  // 해당 경로의 캐시를 무효화하고 새로 렌더링
  revalidatePath(`/posts/${postId}`);
}

revalidatePath나 revalidateTag를 쓰면 데이터 변경 후 자동으로 페이지가 업데이트돼요!

🤔 실무에서는 어떻게 조합할까?

제 경험상 가장 좋은 패턴은 이거예요:

1. 기본은 Server Component로 시작

  • 대부분의 페이지는 Server Component로 만들어요

2. 인터랙션이 필요한 부분만 Client Component로 분리

  • 버튼, 폼, 모달 같은 것들만 'use client' 추가

3. 데이터 변경은 Server Function 활용

  • 폼 제출, CRUD 작업은 Server Actions로 처리

예를 들어 블로그 포스트 페이지라면:

// app/posts/[id]/page.tsx - Server Component
export default async function PostPage({ params }) {
  const post = await getPost(params.id);
  
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      
      {/* 댓글 작성 폼만 Client Component */}
      <CommentForm postId={params.id} />
    </div>
  );
}

// components/CommentForm.tsx - Client Component
'use client'

export function CommentForm({ postId }) {
  return (
    <form action={addComment}>
      <input type="hidden" name="postId" value={postId} />
      <textarea name="content" />
      <button type="submit">댓글 달기</button>
    </form>
  );
}

⚠️ 주의사항: 이것만은 꼭 기억하세요!

1. Server Component에서 Client Component로는 가능, 반대는 불가능

// ✅ 가능
<ServerComponent>
  <ClientComponent />
</ServerComponent>

// ❌ 불가능
<ClientComponent>
  <ServerComponent />  // 에러!
</ClientComponent>

2. Server Actions는 데이터 조회보다는 변경에 적합

Server Actions는 POST 메서드만 사용해요. 데이터 조회는 Server Component에서 직접 fetch하는 게 더 효율적입니다.

3. 'use client'를 남발하지 마세요

정말 필요한 컴포넌트에만 'use client'를 붙이세요. 한 번 Client Component가 되면 그 안의 모든 자식도 Client Component가 되니까요!

🎁 마무리하며

Server Component와 Server Function은 처음엔 어색하지만, 익숙해지면 정말 강력한 도구예요.

성능, 보안, 개발 편의성 세 마리 토끼를 모두 잡을 수 있는 현대적인 React 개발 방식이죠.


📌 핵심 정리

Server Component 장점: 빠른 로딩, 좋은 SEO, 강력한 보안, 작은 번들 크기
Client Component는: Hook, 이벤트 리스너, 브라우저 API 필요할 때만
Server Function으로: API 라우트 없이 서버 로직 실행, 폼 처리 간편화

 

반응형