Next.js 14의 서버 액션(Server Actions)과 데이터 조작(Data Manipulation)에 대해 자세히 알아보자.

3분 읽기
2026-03-13
2,184
아래 링크를 통해 쇼핑하면 추가 비용 없이 수수료를 받을 수 있습니다.

Next.js 14의 Server Actions 기능은 서버 측 데이터 처리 방식에 있어서 중요한 진화를 의미합니다. 이 기능을 통해 개발자는 서버 컴포넌트 내에서 직접 비동기 함수를 정의할 수 있으며, 이를 통해 폼 제출, 데이터 변경과 같은 작업을 처리할 수 있습니다. 별도의 API 라우트를 생성할 필요가 없습니다. 이러한 방식은 데이터 흐름을 크게 단순화하고 개발 효율성을 향상시키며, 애플리케이션의 보안성도 강화합니다.

서버 액션(Server Action)이란 무엇인가요?

`Server Actions`는 Next.js에서 서버 측에서 실행되는 함수입니다. 이러한 함수들은 React 컴포넌트 내에서 직접 정의될 수 있으며, 폼이나 이벤트를 통해 호출될 수 있습니다. 가장 큰 장점은 실행 로직이 전적으로 서버 측에서 이루어진다는 점으로, 클라이언트 측은 요청을 보내고 응답을 받는 역할만 수행하므로 민감한 로직이나 액세스 키가 클라이언트 측에 노출되는 것을 자연스럽게 방지할 수 있습니다.

컴포넌트 파일의 상단에 다음 내용을 추가함으로써… ‘server-only’ 지시어나 함수 내부에서 사용됩니다. ‘use server’ 이 명령을 사용하면 함수를 서버 측 작업으로 명확하게 표시할 수 있습니다. 이를 통해 개발자는 컴포넌트 내에서 서버 로직을 원활하게 작성할 수 있으며, 예를 들어 데이터베이스와 안전하게 직접 상호작용하는 것도 가능합니다.

추천 읽기 웹사이트 구축 전체 프로세스 분석: 계획부터 출시까지의 기술 실습 및 핵심 포인트

전형적인 Server Action의 구조는 다음과 같습니다. 이는 서버 환경에서 직접 실행되며, 모든 서버 측 리소스에 접근할 수 있습니다.

워드프레스닷컴 웹사이트 빌더 도우미
워드프레스닷컴 웹사이트 빌더 도우미
99.999% 가용성 + 지역 간 재해 복구, 연중무휴 지원, 블로그 패키지 구매 시 무료 AI 빌드 사이트 제공
울타호스트 웹사이트 빌더 도우미
울타호스트 웹사이트 빌더 도우미
검색 노출을 위해 웹사이트를 최적화하는 데 필요한 SEO 기능을 갖춘 900개 이상의 무료 맞춤형 템플릿을 제공합니다.
// app/actions/todo.ts
‘use server‘;

import { revalidatePath } from ‘next/cache‘;
import { db } from ‘@/lib/database‘;

export async function createTodo(formData: FormData) {
  const title = formData.get(‘title‘) as string;
  // 在服务器端安全地执行数据库操作
  await db.todo.create({ data: { title, completed: false } });
  // 操作成功后,使特定路径的缓存失效以更新数据
  revalidatePath(‘/dashboard‘);
}

Design Philosophy and Working Principles

Server Actions의 설계 철학은 “서버 로직을 컴포넌트에 더 가깝게 가져오는 것”입니다. 전통적인 Next.js 애플리케이션에서는 데이터 변경이 발생할 때 먼저 서버 측에서 처리할 로직을 정의해야 했습니다. pages/api/ 디렉터리 내에 있는 API 라우트들을 확인한 후, 클라이언트 컴포넌트에서 이를 사용하세요. fetch 요청을 시작합니다. 이러한 분리는 아키텍처의 복잡성을 증가시킵니다.

`Server Actions`는 다음과 같은 방법으로 도입됩니다: ‘use server’ 지시사항: 컴파일 시 이러한 함수들을 “서버 측에서만 실행 가능”하도록 표시하세요. 클라이언트가 이 함수들을 호출하면, Next.js는 안전하고 자동으로 생성된 RPC(원격 프로시저 호출) 엔드포인트를 통해 요청을 중계합니다. 시리얼화된 매개변수가 서버로 전송되어 함수가 실행된 후, 그 결과가 다시 시리얼화되어 클라이언트에게 반환됩니다. 이 전체 과정은 개발자에게는 투명하게 이루어지므로, 마치 로컬 함수를 직접 호출하는 것과 같은 느낌을 줍니다.

서버 액션을 정의하고 사용하는 방법

서버 액션(Server Actions)을 정의하고 사용하는 방법에는 주로 두 가지가 있습니다: 인라인 정의(In-line Definition)와 모듈화 정의(Modular Definition)입니다. 각 방법은 다른 사용 시나리오에 적합하며, 공통적으로는 서버 액션을 사용해야 한다는 점이 필수적입니다. ‘use server’ 명령어는 실행 환경을 명확히 지정해야 합니다.

내联 정의 방식 (Inline Definition Method)

서버 컴포넌트 내에서는 함수 본문의 맨 위에 직접 해당 코드를 사용할 수 있습니다. ‘use server’ 내장형 Server Action을 생성하기 위한 지침입니다. 이 방법은 논리가 간단하고 재사용성이 낮은 작업에 적합하며, 관련 코드를 한곳에 모아 관리하는 데 도움이 됩니다.

추천 읽기 현대 웹사이트 구축 전 과정 가이드: 제로에서 원까지의 완전한 실습 및 핵심 기술 분석

// app/page.tsx (一个服务器组件)
export default function ServerPage() {
  async function handleLogin(formData: FormData) {
    ‘use server‘;
    const email = formData.get(‘email‘);
    const password = formData.get(‘password‘);
    // 在此处进行身份验证逻辑
    // ...
  }

return (
    <form action={handleLogin}>
      <input type="email" name="email" />
      <input type="password" name="password" />
      <button type="submit">登录</button>
    </form>
  );
}

참고로, 최적의 타입 안전성과 Tree-shaking 최적화를 위해 Next.js 공식에서는 모듈화된 정의 방식을 사용하는 것을 더 권장합니다.

모듈화 정의 방식

보다 복잡하거나 여러 곳에서 재사용해야 하는 로직의 경우, Server Actions를 별도의 모듈 파일에 정의하는 것이 좋습니다. 예를 들어… app/actions/ 디렉터리 내에 파일들을 정리하는 것은 코드의 모듈화와 유지보수성을 높이는 데 도움이 되며, 단위 테스트를 수행하는 데에도 편리합니다.

먼저, 독립적인 파일에서 당신의 액션을 정의하고 해당 파일을 내보내세요:

블루호스트 웹사이트 빌더
AI 웹사이트 제작 도구, 연중무휴 라이브 채팅 및 전화 지원, 1년간 무료 도메인 이름, 무료 CDN, 99.99% 가동 시간 SLA를 제공합니다.
// app/actions/user.ts
‘use server‘;

import { z } from ‘zod‘;
import { db } from ‘@/lib/db‘;
import { revalidateTag } from ‘next/cache‘;

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
});

export async function createUser(prevState: any, formData: FormData) {
  const validatedFields = UserSchema.safeParse({
    name: formData.get(‘name‘),
    email: formData.get(‘email‘),
  });

if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
    };
  }

await db.user.create({ data: validatedFields.data });
  revalidateTag(‘user-list‘); // 使用标签重新验证缓存
  return { message: ‘用户创建成功!‘ };
}

그런 다음, 클라이언트 또는 서버 구성 요소에 해당 모듈을 가져와서 사용합니다. 클라이언트 구성 요소에서는 React와 함께 사용해야 합니다. useActionState Hook를 사용하여 상태를 관리하세요.

// app/components/CreateUserForm.tsx
‘use client‘;

import { createUser } from ‘@/app/actions/user‘;
import { useActionState } from ‘react‘;

export function CreateUserForm() {
  const [state, formAction, isPending] = useActionState(createUser, null);

return (
    <form action="/ko/formAction/" data-trp-original-action="{formAction}">
      <div>
        <label htmlfor="name">이름 및 성</label>
        <input id="name" name="name" />
        {state?.errors?.name && <span>{state.errors.name}</span>}
      </div>
      <div>
        <label htmlfor="email">받은 편지함</label>
        <input id="email" name="email" type="email" />
        {state?.errors?.email && <span>{state.errors.email}</span>}
      </div>
      <button type="submit" disabled="{isPending}">
        {isPending ? ‘작성 중…‘ : ‘사용자 생성 중‘}
      </button>
      {state?.message && <p>{state.message}</p>}
    <input type="hidden" name="trp-form-language" value="ko"/></form>
  );
}

Server Actions의 핵심 장점과 실제 활용 사례

`Server Actions`의 도입은 전체 스택 애플리케이션 개발 과정에서 발생하는 여러 가지 핵심적인 문제들을 해결했으며, 보안성부터 개발 경험에 이르기까지 모든 측면에서 상당한 개선을 가져왔습니다.

Simplified data flow and end-to-end type safety

전통적인 “API 라우팅 +” 방식은 서버에서 클라이언트의 요청을 받아 적절한 API를 호출하고, 그 결과를 클라이언트에게 반환하는 과정을 의미합니다. 이 방식은 웹 개발에서 매우 일반적으로 사용되며, 다양한 애플리케이션과 서비스 간의 데이터 교환을 가능하게 합니다. fetch”이 모드에서는 컨텍스트 전환과 타입 중단점이 존재합니다. Server Actions를 사용하면 서버 함수를 모듈로 가져와 사용할 수 있으며, TypeScript와 결합하여 데이터베이스 스키마(DB Schema)에서 프론트엔드 폼에 이르기까지의 엔드투엔드 타입 안전성을 실현할 수 있습니다. 액션 함수 내에서 입력 매개변수에 대한 Zod 또는 유사한 스키마를 정의할 수 있으며, 클라이언트 측에서는 정확한 타입 정보와 유효성 검사를 받을 수 있어 런타임 오류를 크게 줄일 수 있습니다.

추천 읽기 Tailwind CSS를 선택한 이유: 그 핵심 강점과 최고 사용법에 대한 자세한 설명

강화된 보안성 및 내장된 보호 기능

서버 작업(Server Actions)은 기본적으로 다양한 보안 조치에 의해 보호됩니다. Next.js는 모든 서버 작업에 대해 자동으로 보안 기능을 적용합니다. POST 이 메서드의 Server Actions는 크로스사이트 요청 위조(CSRF) 공격을 방지하기 위한 조치를 취하며, 이는 요청을 검사함으로써 이루어집니다. Origin 그리고 Host 먼저 요청의 출처를 확인합니다. 더 중요한 것은, 비즈니스 로직(예: 데이터베이스 쿼리, 제3자 서비스 호출)이 서버에서 실행되기 때문에 민감한 환경 변수와 API 키가 결코 클라이언트 번들 패키지에 유출되지 않는다는 점입니다.

효율적인 캐시 재확인 메커니즘

`Server Actions`는 Next.js의 캐싱 시스템과 긴밀하게 통합되어 있습니다. 데이터가 변경되었을 때, 이를 즉시 반영시키기 위해 `Server Actions`를 사용할 수 있습니다. revalidatePath 또는 revalidateTag 이 함수는 특정 경로나 특정 태그가 있는 캐시 데이터를 정확하게 무효화합니다. 즉, 블로그 글을 생성하는 작업이 완료된 후에 즉시 글 목록 페이지의 캐시를 갱신하여 사용자가 증분적으로 생성되는 정적 콘텐츠(ISR: Incremental Static Reproduction)의 주기를 기다리지 않고도 최신 데이터를 볼 수 있도록 해줍니다.

호스팅.com
무료 SSL, Cloudflare CDN, WAF, 40개 이상의 글로벌 서버실 선택, 가까운 곳에서 대기 시간 단축, 연중무휴 서비스 지원, 최대 67% 절약, AI 빌드 및 SEO 최적화 지원!
‘use server‘;
import { revalidatePath, revalidateTag } from ‘next/cache‘;

export async function updatePost(id: string, content: string) {
  await db.post.update({ where: { id }, data: { content } });
  // 方式一:重新验证特定路径
  revalidatePath(`/posts/${id}`);
  // 方式二:重新验证所有带有该标签的缓存数据
  revalidateTag(‘posts‘);
}

점진적인 기능 개선(Progressive Enhancement)과 사용자 경험 최적화(User Experience Optimization)

설령 클라이언트 측의 JavaScript가 로드에 실패하거나 완전히 비활성화되었더라도, 해당 기능은 여전히 사용할 수 있습니다. action 속성이 Server Action을 가리키는 HTML 폼도 여전히 정상적으로 작동하며, 이는 점진적인 개선(Progressive Enhancement)의 기본 요구사항을 충족시킵니다. 현대 브라우저에서는 이러한 기능을 활용할 수 있습니다. useFormStatususeOptimistic React Hook을 기다려 우수한 사용자 경험을 제공하세요. 예를 들어, 서버 응답을 기다리는 동안 로딩 상태를 표시하거나 낙관적인 업데이트를 수행할 수 있습니다.

‘use client‘;
import { experimental_useOptimistic as useOptimistic } from ‘react‘;
import { sendMessage } from ‘./actions‘;

function Thread({ messages }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) =&gt; [...state, { text: newMessage, sending: true }]
  );

async function formAction(formData) {
    const message = formData.get(‘message‘);
    addOptimisticMessage(message); // 立即乐观更新UI
    await sendMessage(message); // 调用 Server Action
  }

return (
    <>
      {optimisticMessages.map((msg, idx) =&gt; (
        <div key="{idx}">{msg.text} {msg.sending &amp;&amp; ‘(전송 중…)‘}</div>
      ))}
      <form action="/ko/formAction/" data-trp-original-action="{formAction}">...<input type="hidden" name="trp-form-language" value="ko"/></form>
    </>
  );
}

요약

Next.js 14의 Server Actions는 혁신적인 기능으로, 서버 로직을 UI 컴포넌트에 더 긴밀하고 선언적인 방식으로 통합함으로써 전체 스택 개발의 복잡성을 크게 줄여줍니다. 이는 단순히 API 라우팅을 대체하는 기술적인 솔루션을 넘어, 더 안전하고 효율적이며 개발자에게 더 친화적인 데이터 처리 방식을 대표합니다. 단순화된 데이터 흐름, 강력한 보안 기본값, 정교한 캐싱 제어, 그리고 점진적인 기능 개선에 대한 지원을 제공함으로써 Server Actions는 현대 웹 애플리케이션을 구축하는 경험과 품질을 크게 향상시킵니다. 이 기능을 잘 이해하고 활용하는 것은 차세대 고성능이며 유지보수가 용이한 Next.js 애플리케이션을 구축하는 데 있어 핵심입니다.

자주 묻는 질문

클라이언트 컴포넌트 내에서 Server Action을 직접 정의할 수 있나요?

할 수 없습니다.‘use server’ 이 지시는 서버 환경에서 모듈 내에서만 사용할 수 있습니다. 특정 부분에는 “…”과 같은 표시가 되어 있습니다. ‘use client’ 클라이언트 컴포넌트 파일 내에서 Server Action을 직접 정의하면 빌드 오류가 발생합니다. 올바른 방법은 Server Action을 별도의 모듈 파일이나 서버 컴포넌트에 정의한 후, 클라이언트 컴포넌트에서 해당 Server Action을 가져와서 호출하는 것입니다.

`Server Actions`은 폼 데이터를 처리하는 데만 사용되는 것인가요?

아닙니다. 비록 폼 제출이 Server Actions의 가장 흔한 사용 사례이긴 하지만, FormData 이 함수들은 특정 객체 형식을 요구하지만, 문자열, 숫자, 불리언 값, 배열, 일반 객체를 포함한 모든 직렬화 가능한 데이터를 매개변수로 받을 수 있습니다. 일반적인 비동기 함수를 호출하는 것처럼 이 함수들을 호출하고 필요한 매개변수를 전달할 수 있습니다.

서버 액션(Server Action)을 실행하는 도중에 오류가 발생하면 어떻게 처리해야 할까요?

당신은 `Server Action` 함수 내부에서 그것을 사용해야 합니다. try...catch 먼저, 오류를 캡처하고 처리하기 위해 해당 블록을 사용합니다. 그런 다음, 오류 정보가 포함된 객체를 반환함으로써 클라이언트에게 알릴 수 있습니다. 클라이언트 측에서는 이 정보를 활용하여 적절한 조치를 취할 수 있습니다. useActionState `Hook` 함수가 반환한 상태를 기반으로 오류 메시지를 렌더링해야 합니다. 민감한 서버 측 오류 스택을 클라이언트에게 그대로 반환하지 말고, 사용자에게 친화적인 오류 메시지를 보내야 합니다.

Server Actions를 사용하면 제 애플리케이션의 성능에 영향이 있을까요?

올바르게 사용될 때, Server Actions는 일반적으로 성능 인식을 향상시키는 데 도움이 됩니다. 이는 로직이 서버에서 실행되기 때문에 클라이언트 측 JavaScript 패키지의 크기를 줄여줍니다. 정확한… revalidatePath 또는 revalidateTag 함수를 호출하면 불필요한 전체 페이지 새로고침을 방지할 수 있습니다. 하지만 액션 함수의 실행 효율에 주의를 기울여야 하며, 장시간 실행되는 동기식 작업이 요청을 차단하는 것을 방지해야 합니다. 시간이 많이 소요되는 작업의 경우 비동기 큐를 사용하여 처리하는 것을 고려해야 합니다. 또한, 자주 호출되는 소규모 서버 액션은 네트워크 트래픽을 증가시킬 수 있으므로, 클라이언트 상태 관리를 사용하는 것이 적절한지 여부를 신중하게 판단해야 합니다.