# Next.js 16 の Server Actions【2026】ミューテーション・リバリデーション・面接質問
> Next.js 16 の Server Actions がミューテーション、リバリデーション、pending 状態、楽観的 UI、セキュリティをどう扱うか、そして各概念を試す面接質問を解説します。
- Published: 2026-06-24
- Updated: 2026-07-06
- Author: SharpSkill
- Tags: next.js, server-actions, react, mutations, revalidation, interview
- Reading time: 11 min
---
Next.js の Server Actions は、素朴な非同期関数をサーバーサイドのミューテーションエンドポイントへと変え、フォームから直接呼び出せるようにします。API ルートも、クライアント側の `fetch` も、手動の JSON シリアライズも不要です。Next.js 16 ではデータミューテーションを記述する標準的な手段となっており、2026 年のシニア React 面接ではほぼ確実に登場します。本記事では、Server Actions がどのように実行されるのか、リバリデーションがどのように最新データを UI へ伝播させるのか、pending 状態やエラー状態をどうモデル化するのか、そして多くの開発者がつまずくセキュリティのルールを詳しく掘り下げます。
> **一文での定義**
>
> Server Action とは、`"use server"` が付与された非同期関数であり、サーバー上でのみ実行されます。クライアントコンポーネントやフォームから、あたかもローカル関数であるかのように呼び出せる一方で、Next.js がネットワークリクエスト、引数のシリアライズ、そして CSRF 対策を自動的に処理します。
## Server Actions は内部でどう実行されるのか
Server Action は、コンパイラが生成したエンドポイントを介してネットワーク越しに呼び出される、サーバー専用の関数です。関数に `"use server"` ディレクティブが付いている場合、Next.js のコンパイラはその本体をブラウザへ送信しません。代わりに、関数を軽量な参照へ置き換えます。ハッシュ化されたアクション ID を、生成された POST ルートに対応付けるのです。クライアントからアクションを呼び出すと、そのルートへリクエストが送られ、実際の関数はサーバー上で実行され、シリアライズされた結果が呼び出し元へストリーミングされて戻ります。
この設計には、プロダクションコードでも面接でも覚えておく価値のある帰結が三つあります。第一に、引数と戻り値はシリアライズ可能でなければなりません。エンコードされたペイロードとしてネットワークを越えるからです。第二に、関数本体がクライアント向けにバンドルされることはないため、シークレットはサーバー上に留まります。第三に、`
)
}
```
各フィールドの `name` 属性が、`formData.get()` で読み取られるキーになります。ブラウザがフォームからこのオブジェクトをどのように組み立てるかは、[MDN の FormData リファレンス](https://developer.mozilla.org/en-US/docs/Web/API/FormData) が説明しています。より細かな制御が必要な場合、`next/form` は同じアクションベースの契約を保ちながら、ネイティブ要素にクライアントサイドナビゲーションとプリフェッチを追加します。
## useActionState で pending 状態とエラー状態を管理する
実運用のフォームには、バリデーションのフィードバックとローディングインジケーターが欠かせません。React 19 のフック `useActionState` は Server Action をラップし、各送信を通じて state 値を受け渡し、`isPending` フラグを公開します。これは以前の `useFormState` という名称に取って代わるものであり、現在の state、ラップされたアクション、そして pending を示す真偽値のタプルを返します。
```tsx
// app/posts/new-post-form.tsx
"use client"
import { useActionState } from "react"
import { createPost } from "./actions"
const initialState = { error: "" }
export default function NewPostForm() {
// state carries the last return value; isPending tracks the in-flight request
const [state, formAction, isPending] = useActionState(createPost, initialState)
return (
)
}
```
フックがアクションを駆動する場合、関数のシグネチャは `FormData` の前に `prevState` という第一引数を持つようになります。例外をスローする代わりにオブジェクトを返すことで、コンポーネントはエラーバウンダリなしでインラインバリデーションを描画できます。
```typescript
// app/posts/actions.ts
"use server"
import { revalidateTag } from "next/cache"
import { PostService } from "@/lib/services/post-service"
type FormState = { error: string }
export async function createPost(
prevState: FormState,
formData: FormData,
): Promise {
const title = formData.get("title") as string
// Validate on the server; client validation is never trustworthy alone
if (!title || title.length < 3) {
return { error: "Title must be at least 3 characters." }
}
await PostService.create({ title })
// Invalidate by tag rather than path for finer-grained control
revalidateTag("posts")
return { error: "" }
}
```
[react.dev の useActionState リファレンス](https://react.dev/reference/react/useActionState) では、ハイドレーション前の送信に対する permalink サポートといった高度なパターンも扱われています。構造化された state を返すことで、正常系とエラー系を予測可能な一箇所にまとめられます。
## revalidatePath と revalidateTag:正しい無効化方法を選ぶ
ミューテーションは仕事の半分にすぎません。UI がその結果を反映しなければならないのです。Next.js は `next/cache` から二つの無効化関数を公開しており、正しい方を選ぶことは面接で頻出の判別ポイントになります。
| 関数 | 無効化の対象 | 適した場面 |
|----------|-------------|-----------|
| `revalidatePath("/posts")` | あるルートに対するすべてのキャッシュエントリ | ミューテーションが一つの明確なページまたはレイアウトに影響する場合 |
| `revalidateTag("posts")` | そのタグが付与されたすべてのキャッシュ済み fetch | 同じデータが複数のルートに現れる場合 |
`revalidatePath` は粗く、ルート中心です。URL とそのレイアウトツリーのキャッシュをクリアします。`revalidateTag` は細かく、データ中心です。その文字列でタグ付けされた `fetch` やキャッシュ済み関数は、どこに存在していても無効化されます。タグは大規模アプリでよりよくスケールします。一度のミューテーションで、サイドバー、一覧、そして詳細ページを単一の呼び出しで更新できるからです。どちらの関数もデータを即座に再取得するのではなく、古い(stale)とマークします。そのため、次のリクエストでコンテンツが再生成されます。
Next.js 16 の Cache Components では、タグ付けが `cacheTag` を通じて `"use cache"` ディレクティブと統合され、アプリ全体でキャッシュをどう捉えるかが変わります。この相互作用については、SharpSkill の [Next.js 16 Cache Components ガイド](/blog/react-next/nextjs-16-cache-components-use-cache-ppr-interview-questions) で詳しく解説しています。
## useOptimistic による楽観的 UI 更新
ネットワークの往復は、知覚できるほどのレイテンシを加えます。`useOptimistic` フックは期待される結果を即座に描画し、その後アクションが解決した時点でサーバーのレスポンスと整合させます。現在の state と、楽観的なバージョンを生成するリデューサーを受け取ります。
```tsx
// app/posts/like-button.tsx
"use client"
import { useOptimistic } from "react"
import { likePost } from "./actions"
export function LikeButton({ postId, likes }: { postId: string; likes: number }) {
// optimisticLikes updates before the server confirms
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(current) => current + 1,
)
async function handleLike() {
addOptimisticLike(null) // update the UI immediately
await likePost(postId) // then run the real mutation
}
return (
)
}
```
アクションが失敗した場合、React は楽観的な値を確定済みのサーバー state へ自動的に戻すため、手動でのロールバックは不要です。[react.dev の useOptimistic リファレンス](https://react.dev/reference/react/useOptimistic) では、整合サイクルがトランジションとどう相互作用するかが詳述されています。このパターンこそが、Server Action のフォームを、完全にクライアントレンダリングされた SPA と同じくらい反応の良いものに感じさせるものです。
## 引数のバインドとイベントハンドラからのアクション呼び出し
すべてのミューテーションがフォーム送信から始まるわけではありません。行の削除、設定の切り替え、リストの並べ替えは、多くの場合ボタンのクリックやその他のイベントから発火します。Server Action はクライアント上では通常の関数参照なので、任意のハンドラから呼び出せます。ただし、UI の応答性を保つために、その呼び出しはトランジション内で実行する必要があります。
フォームフィールドではない追加の引数は `bind` で付与します。これは、それらの値を先頭に付けた新しいアクションを生成します。送信された `FormData` と並べて ID を渡す際の慣用的な方法です。
```tsx
// app/posts/post-actions.tsx
"use client"
import { useTransition } from "react"
import { deletePost, updatePost } from "./actions"
export function PostActions({ postId }: { postId: string }) {
const [isPending, startTransition] = useTransition()
// Bind the id so the action receives it before FormData
const updateWithId = updatePost.bind(null, postId)
return (
{/* Event-handler invocation wrapped in a transition */}
)
}
```
バインドされた引数は、他のクローズオーバーされた値と同様に暗号化されるため、`postId` が転送中に改ざんされることはありません。とはいえ、アクションは呼び出し元がそのレコードを操作してよいかを依然として確認しなければなりません。直接の呼び出しを `startTransition` でラップすれば、React はインターフェースをインタラクティブに保ち、フォームのラッパーなしで pending 状態を表面化できます。
## Server Actions のセキュリティ:すべてのアクションを公開 API として扱う
最も危険な誤解は、Server Action はサーバーコードの隣に書かれているからプライベートだ、というものです。実際には、アクションが一度使われると、Next.js は誰でも細工したリクエストで呼び出せる公開 POST エンドポイントを生成します。フレームワークは安全なアクション ID を提供し、`next build` の際に未使用のアクションを排除しますが、呼び出し元を認可することはありません。
> **すべてのアクションの内部で認可する**
>
> Server Actions は到達可能な HTTP エンドポイントです。レイアウトやミドルウェアでのセッションチェックはこれらを保護しません。攻撃者はアクションへ直接 POST できるからです。データに触れる前に、アクション本体の中でセッションと呼び出し元の権限を検証してください。
```typescript
// app/admin/actions.ts
"use server"
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { UserService } from "@/lib/services/user-service"
export async function deleteUser(userId: string) {
// Authenticate on every invocation, not in a wrapper component
const session = await auth.api.getSession({ headers: await headers() })
if (!session || session.user.role !== "admin") {
throw new Error("Unauthorized")
}
await UserService.delete(userId)
}
```
さらに二つのルールが重要です。インラインアクションによってクローズオーバーされた値は、クライアントへ送信される前に暗号化され、サーバー上で復号されます。そのためキャプチャされたトークンが平文で漏れることはありませんが、それに依存するのは脆弱です。シークレットはクロージャから完全に排除してください。そして、クライアントから来る引数はすべて信頼できない入力であり、REST のボディとまったく同じように検証しなければなりません。[Next.js のデータセキュリティガイド](https://nextjs.org/docs/app/guides/data-security) は、これらの保証の背後にある taint モデルを定式化しています。これらのトレードオフを体系的に練習するには、SharpSkill の [Next.js Server Actions 面接モジュール](/technologies/react-next/interview-questions/nextjs-server-actions) が、採用パネルが尋ねるまさにその質問を鍛えてくれます。
## Next.js 16 Server Actions の面接質問
これらの質問は、2026 年のシニア React およびフルスタックの面接ループで繰り返し登場します。
**なぜ Server Action はシリアライズ可能な値しか受け取れず、返せないのか。** 引数と結果が、エンコードされたペイロードとしてクライアントとサーバーの間のネットワーク境界を越えるからです。関数、クラスインスタンス、DOM ノードはシリアライズできないため、それらを渡すと例外がスローされます。`FormData`、プレーンオブジェクト、配列、プリミティブは安全です。
**Server Actions ではプログレッシブエンハンスメントはどう機能するのか。** アクションをフォームの `action` プロパティへ直接渡すと、ブラウザは React のハイドレーション前でも POST リクエストでフォームをネイティブに送信します。Next.js がこれをインターセプトしてサーバーサイドでアクションを実行するため、フォームはクライアント JavaScript なしで機能し、ハイドレーション後にはクライアント駆動の体験へアップグレードされます。
**アクションでエラーをスローすることと返すことの違いは何か。** スローは最も近いエラーバウンダリへ伝播し、予期しない失敗に適しています。`useActionState` を通じて構造化された state オブジェクトを返すのは、フォームをアンマウントせずにインラインで描画されるべき、想定内のバリデーションエラーに適しています。
**revalidatePath を呼ぶとデータは即座に再取得されるのか。** いいえ。キャッシュエントリを古い(stale)とマークし、次のリクエストでそれらを再生成させます。現在のレスポンスは影響を受けません。だからこそ、ミューテーション後のリダイレクトは新しく描画されたコンテンツに着地します。
これらのミューテーションが実行される、より広範なレンダリングの文脈については、[プロダクションにおける React 19 Server Components](/blog/react-next/react-19-server-components-production) の解説が、Server Actions の知識と自然に組み合わさります。
## まとめ
Next.js 16 の Server Actions は、ミューテーション、リバリデーション、そしてプログレッシブエンハンスメントを、単一のサーバーファーストなプリミティブへと統合します。要点は次のとおりです。
- ミューテーションには `"use server"` を付け、フォームの `action` プロパティへ渡すことで、ネットワーク処理とプログレッシブエンハンスメントを無償で得られます。
- pending とバリデーションの state は `useActionState` で駆動し、想定内のエラーではスローせずに構造化オブジェクトを返します。
- 変更が一つのルートに及ぶときは `revalidatePath` を、同じデータが複数のルートにまたがるときは `revalidateTag` を選びます。
- `useOptimistic` を使って期待される結果を即座に描画し、React に整合またはロールバックを自動で任せます。
- 各アクションはミドルウェアだけでは保護されない公開 HTTP エンドポイントなので、すべてのアクション本体の内部で認可と検証を行います。
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/ja/blog/react-next/nextjs-16-server-actions-mutations-revalidation-interview-questions