--- id: react-rsc-server-actions-deep title: RSC + Server Actions — 데이터 / 변형 / 캐시 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [react, rsc, server-actions, next, vibe-coding] tech_stack: { language: "TS / Next.js / React", applicable_to: ["Frontend"] } applied_in: [] aliases: [React Server Components, Server Actions, useFormStatus, revalidatePath, "use server"] --- # RSC + Server Actions > 서버에서만 실행되는 컴포넌트 (RSC) + 서버 함수 직접 호출 (Server Actions). **Client bundle 0 — DB / API 직접**. `'use server' / 'use client'` 경계 + revalidate. ## 📖 핵심 개념 - RSC: 서버 렌더, JS bundle 0, async OK. - Client component: `'use client'` — 상호작용. - Server Action: 서버 함수, 클라가 form / RPC 처럼 호출. - revalidatePath / revalidateTag: cache 무효화. ## 💻 코드 패턴 ### 기본 RSC (Next.js App Router) ```tsx // app/posts/page.tsx — 서버 컴포넌트 async function Page() { const posts = await db.posts.findMany(); // DB 직접 return ( ); } ``` ### Client + Server 조합 ```tsx // app/posts/page.tsx (server) import { LikeButton } from './LikeButton'; async function Page() { const posts = await db.posts.findMany(); return posts.map(p => (

{p.title}

)); } ``` ```tsx // LikeButton.tsx 'use client'; import { useState } from 'react'; import { likePost } from './actions'; export function LikeButton({ postId, initialCount }: ...) { const [count, setCount] = useState(initialCount); return ( ); } ``` ### Server Action ```ts // app/posts/actions.ts 'use server'; import { revalidatePath, revalidateTag } from 'next/cache'; import { z } from 'zod'; const Like = z.object({ postId: z.string().uuid() }); export async function likePost(postId: string) { Like.parse({ postId }); const userId = await getUser(); if (!userId) throw new Error('UNAUTHORIZED'); await db.posts.update({ where: { id: postId }, data: { likes: { increment: 1 } } }); revalidatePath(`/posts/${postId}`); revalidateTag('posts'); } ``` ### Form action ```tsx // app/new-post/page.tsx import { createPost } from './actions'; export default function NewPost() { return (