OPEN HYPER STEP
← 목록으로 (stack-analysis)
STACK-ANALYSIS · 14 / 90
stack-analysis
CHAPTER 14 / 90
읽기 약 2
FUNCTION

서버 액션(Server Actions): 폼 처리의 미래


핵심 개념

"use server"·revalidatePath·낙관적 UI — REST API 없이 폼 처리.

본문

Server Action 기본

TSX📋 코드 (41줄)
// app/actions/posts.ts
'use server';

import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';

const PostSchema = z.object({
  title: z.string().min(1).max(200),
  content: z.string().min(1),
});

export async function createPost(formData: FormData) {
  const parsed = PostSchema.safeParse({
    title: formData.get('title'),
    content: formData.get('content'),
  });

  if (!parsed.success) {
    return { error: parsed.error.flatten().fieldErrors };
  }

  const post = await db.post.create({ data: parsed.data });

  revalidatePath('/posts');  // 캐시 무효화
  redirect(`/posts/${post.id}`);
}


// app/posts/new/page.tsx — 서버 컴포넌트
import { createPost } from '@/app/actions/posts';

export default function NewPost() {
  return (
    <form action={createPost}>
      <input name="title" required />
      <textarea name="content" required />
      <button type="submit">저장</button>
    </form>
  );
}

useActionState (React 19)

TSX📋 코드 (23줄)
'use client';
import { useActionState } from 'react';
import { createPost } from '@/app/actions/posts';

export function PostForm() {
  const [state, action, isPending] = useActionState(
    async (prevState: any, formData: FormData) => {
      return await createPost(formData);
    },
    null
  );

  return (
    <form action={action}>
      <input name="title" />
      {state?.error?.title && <p>{state.error.title}</p>}
      <textarea name="content" />
      <button disabled={isPending}>
        {isPending ? '저장 중...' : '저장'}
      </button>
    </form>
  );
}

낙관적 업데이트 (useOptimistic)

TSX📋 코드 (32줄)
'use client';
import { useOptimistic } from 'react';
import { addComment } from '@/app/actions/comments';

export function CommentList({ comments }: { comments: Comment[] }) {
  const [optimisticComments, addOptimistic] = useOptimistic(
    comments,
    (state, newComment: Comment) => [...state, { ...newComment, pending: true }]
  );

  async function handleSubmit(formData: FormData) {
    const text = formData.get('text') as string;
    addOptimistic({ id: 'temp', text, author: '나' });
    await addComment(formData);
  }

  return (
    <>
      <ul>
        {optimisticComments.map(c => (
          <li key={c.id} style={{ opacity: c.pending ? 0.5 : 1 }}>
            {c.text}
          </li>
        ))}
      </ul>
      <form action={handleSubmit}>
        <input name="text" />
        <button>등록</button>
      </form>
    </>
  );
}

다음 챕터

CH.15 "ISR + 온디맨드 재검증" — 캐시 전략.


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 코드의 Server Actions 부분을 분석해서
실전 분석 + 개선 우선순위를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

Server Actions 관련 인기 라이브러리/패턴 5개를
비교 분석해서 패턴 추출를 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 프로젝트 전체에서 Server Actions
최적화 가능 위치를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 프론트엔드 시장의
Server Actions 트렌드를 솔직히 알려줘.

⭐ 이것만 기억하세요
서버 액션(Server Actions): 폼 처리의 미래 이 3가지만 확실히 잡으세요
1.Server Actions = REST API 없이 폼 처리 + 자동 진행 상태 + 타입 안전
2.revalidatePath/revalidateTag로 정확한 캐시 무효화
3.useOptimistic으로 즉시 UI 반영 + 실패 시 자동 롤백


공유하기
진행도 14 / 90