はじめに
Next.jsのApp Routerを使ってブログアプリ(最小限の構成)を作った🔰
App Routerの新しい機能を使ったり、CRUD操作をしたりと勉強になったので備忘録を残す😊
作ったもの
✅ブログの投稿を作成、編集、削除ができるアプリを作った。
投稿の一覧
投稿を一覧表示する。
投稿の作成
「タイトル」「本文」を入力して投稿を作成する。
投稿の編集
「タイトル」「本文」を編集する。
投稿の削除
削除ボタンをクリックして削除する。
フォルダ構成
使用したApp Router周りの機能
- App Routerのルーティング
 - サーバーコンポーネント、クライアントコンポーネント
 - Server Actions
 
App Routerって何?
そもそもApp Routerって何?はこちらで解説している。
準備
使用する手法・技術の紹介
事前にインストールしておくもの
- Node.js
 - npm
 - Next.js
 - Prisma
 - サーバー(何でもOK)
例:SQLite、ローカルPCにインストールしたMySQL、ネットワーク上のサーバーなど
 
今からインストールするもの
- Prisma
 
Prismaをインストールする
prismaをインストールする。
npm install prismaPrisma Clientをインストールする。
(プログラムからデータベースにアクセスするために必要)
npm install @prisma/clientPrismaを初期化する
データベースを新規作成
✅これからPrismaでデータベース操作をするので、事前に手動でデータベースそのものを用意する。
今回はMySQLのデータベースを作ってみる。
(PCにインストール済みのMAMPにMySQLが備わっていたのでそれを使う)
【手順1】phpMyAdminにアクセス
【手順2】データベースを新規作成
prisma/schema.prismaの設定
デフォルトは以下のようになっている。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
  provider = "prisma-client-js"
}
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}使用するデータベースに合わせてproviderを変更する。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
  provider = "prisma-client-js"
}
datasource db {
  provider = "mysql" // ✅変更
  url      = env("DATABASE_URL")
}.envの設定
デフォルトは以下のようになっている。
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"自分のデータベースの情報に書き換える。
// ✅変更
DATABASE_URL="mysql://id:pass@localhost:port番号/DB名?schema=public"例:MAMPの場合はデフォルトだと以下のようになる。
DATABASE_URL="mysql://root:root@localhost:3306/nextjs-blog?schema=public"テーブルを新規作成
以下の手順でテーブルが作れる。
【手順1】prisma/schema.prisma にテーブル定義を書く
以下のようにmodelを書けばOK。
今回はPostsテーブルを作成する。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
  provider = "prisma-client-js"
}
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}
// ✅追記
model Posts {
  id        Int      @id @default(autoincrement())
  title     String
  body      String
  createdAt DateTime @default(now())
  updatedAt DateTime?
}【手順2】マイグレートする
【手順1】Server Actionsで使う関数を準備
やりたいこと
✅今回はApp Routerの新機能「Server Actions」を使いたい。
Server Actionstとは?
クライアントからバックエンドの関数を呼び出せる機能✨
具体的にはフォーム送信時などにバックエンドの関数を呼び出せる。
具体的には以下の処理をするバックエンドの関数をフォーム送信時に呼び出したい。
- 投稿の作成
 - 投稿の削除
 - 投稿の更新
 
【補足】投稿の取得はServer Actionsでなくてもいい
(疑問)
「投稿の取得」も作成、削除、編集と同様CRUD操作の一部。
投稿の取得もServer Actionsに含めた方がいい?💭
(答え)
投稿の取得はクライアントから呼び出す関数ではないのでServer Actionsに含めなくていい。
→サーバー側の初期レンダリングで呼び出す。
【手順1-1】データベース接続用モジュールの作成
データベース接続するためのファイルを作っておく。
✅PrismaClientを生成することでデータベース操作ができる。
→今後はこのファイルをインポートするだけでデータベース操作ができる。
src/app/lib/prisma.js
/**
 * Prisma Clientをインポートします。
 */
import { PrismaClient } from "@prisma/client";
let prisma;
/**
 * 環境に応じてPrismaClientのインスタンスを作成します。
 * 本番環境では新しいインスタンスを作成し、それ以外の環境ではグローバルなインスタンスを再利用します。
 */
if (process.env.NODE_ENV === "production") {
    prisma = new PrismaClient();
} else {
    if (!global.prisma) {
        global.prisma = new PrismaClient();
    }
    prisma = global.prisma;
}
export default prisma;【手順1-2】関数を作る
✅1つのファイルに作成、削除、編集の関数をまとめて定義しておく。
src/app/lib/posts.js
// ✅Server Actions
"use server";
// ✅データベース接続用モジュール
import prisma from "@/app/lib/prisma";
/**
 * 新規投稿をデータベースに作成します。
 * @async
 * @function addPost
 * @param {Object} data - 新規投稿のデータ
 */
export const addPost = async (data) => {
  // 入力値を取得
  const title = data.get("title");
  const body = data.get("body");
  // データベースに1レコード作成
  await prisma.posts.create({
    data: {
      title,
      body,
    },
  });
};
/**
 * 指定されたIDの投稿を削除します。
 *
 * @param {FormData} data - フォームデータ。'id'フィールドには削除する投稿のIDが含まれている必要があります。
 * @returns {Promise<void>} 削除操作が完了すると解決するPromise。
 */
export const deletePost = async (data) => {
  // 入力値を取得
  const id = data.get("id");
  // データベースから1レコード削除
  await prisma.posts.delete({
    where: {
      id: parseInt(id),
    },
  });
};
/**
 * 指定されたIDの投稿を更新します。
 * 
 * @param {FormData} data - フォームデータ。'id'フィールドには更新する投稿のIDが含まれている必要があります。
 * @returns {Promise<void>} 更新操作が完了すると解決するPromise。
 */
export const updatePost = async (data) => {
  // 入力値を取得
  const id = data.get("id");
  const title = data.get("title");
  const body = data.get("body");
  // データベースのレコードを更新
  await prisma.posts.update({
    where: {
      id: parseInt(id),
    },
    data: {
      title: title,
      body: body,
    },
  });
};【解説】✅Server Actions
// ✅Server Actions
"use server";Server Actionsで使う関数は「私はクライアントから呼び出せる関数だよ〜」な状態にしておく必要がある。
→ファイルの先頭に"use server";をつけるだけでOK!
【解説】✅データベース接続用モジュール
// ✅データベース接続用モジュール
import prisma from "@/app/lib/prisma";【手順1-1】で作成したファイルをインポートしている。
これだけでデータベース操作が可能になる。
【解説】投稿の作成、削除、編集
/**
 * 新規投稿をデータベースに作成します。
 * @async
 * @function addPost
 * @param {Object} data - 新規投稿のデータ
 */
export const addPost = async (data) => {
  // 入力値を取得
  const title = data.get("title");
  const body = data.get("body");
  // データベースに1レコード作成
  await prisma.posts.create({
    data: {
      title,
      body,
    },
  });
};
/**
 * 指定されたIDの投稿を削除します。
 *
 * @param {FormData} data - フォームデータ。'id'フィールドには削除する投稿のIDが含まれている必要があります。
 * @returns {Promise<void>} 削除操作が完了すると解決するPromise。
 */
export const deletePost = async (data) => {
  // 入力値を取得
  const id = data.get("id");
  // データベースから1レコード削除
  await prisma.posts.delete({
    where: {
      id: parseInt(id),
    },
  });
};
/**
 * 指定されたIDの投稿を更新します。
 * 
 * @param {FormData} data - フォームデータ。'id'フィールドには更新する投稿のIDが含まれている必要があります。
 * @returns {Promise<void>} 更新操作が完了すると解決するPromise。
 */
export const updatePost = async (data) => {
  // 入力値を取得
  const id = data.get("id");
  const title = data.get("title");
  const body = data.get("body");
  // データベースのレコードを更新
  await prisma.posts.update({
    where: {
      id: parseInt(id),
    },
    data: {
      title: title,
      body: body,
    },
  });
};Prismaで単純なデータベース操作をしているだけ。
Prismaの話なので詳細は割愛する。
【手順2】全体のレイアウト
✅layout.jsで全ページ共通のレイアウトを定義する。
layout.jsとは?
共通のレイアウトやメタデータなどを定義するファイル。
App Router専用。
【手順2-1】適当なレイアウトを作成
src/app/layout.js
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
  title: 'ブログアプリ',
  description: 'ブログアプリのデモです。',
}
export default function RootLayout({ children }) {
  return (
    <html lang="ja">
      <body className={inter.className}>{children}</body>
    </html>
  )
}メタデータだけ書き換えた。
好みに合わせて自由に編集してOK✨
【手順3】投稿一覧ページ
✅「投稿一覧」と「新規作成ボタン」を表示するページ。
完成イメージ
【手順3-1】ページを作る
✅URL「/」でアクセスできる投稿一覧ページを作る。
src/app/page.jsx
import Link from 'next/link';
import prisma from "@/app/lib/prisma";
// ✅投稿データ一覧のコンポーネント
import PostList from "./(components)/PostList";
// ✅このページをSSRにする
export const dynamic = 'force-dynamic'
/**
 * 投稿のリストと新規投稿作成へのリンク
 * @async
 * @function Page
 * @returns {JSX.Element} 投稿一覧と新規作成リンクを含むReactコンポーネント
 */
const Page = async () => {
  // ✅投稿データのリスト
  const posts = await prisma.posts.findMany();
  return (
    <div className="m-8">
      <h1 className="text-2xl font-bold mb-4">投稿一覧</h1>
      <PostList posts={posts} />
      <Link href="/create" className="inline-block bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700">新規作成</Link>
    </div>
  );
};
export default Page;【解説】✅投稿データ一覧のコンポーネント
// ✅投稿データ一覧のコンポーネント
import PostList from "./(components)/PostList";以下の部分を表示するコンポーネント。
【手順3-2】で作る。
【補足】コンポーネントは必ず分ける
コンポーネントを分けないとサーバーコンポーネント、クライアントコンポーネントの使い分けができない。
(問題点)
- サーバーコンポーネント、クライアントコンポーネントはそれぞれできないことがある。
- 例えば…
 - サーバーコンポーネントではuseStateが使えない。
 - クライアントコンポーネントでは直接Prismaを使ったデータベース操作はできない。
 
 - もし1つのファイルにまとめて書くとサーバーコンポーネント、クライアントコンポーネントどちらかの処理しか書けない💦
 
【解説】✅このページをSSRにする
// ✅このページをSSRにする
export const dynamic = 'force-dynamic'これがないとSSGになってしまう。
(問題点)
SSGのままだと投稿を作成や編集をしても一覧データが更新されない💦
具体的にはこの部分がずっと古いまま。
(解決)
SSRにすることで常に最新のデータが取得できる✨
ちなみに「SSRにする他の方法」や「ISRで解決する方法」もある。
【解説】✅投稿データのリスト
【手順3-2】投稿一覧を表示するコンポーネントを作る
✅投稿一覧を表示するPostListコンポーネントを作る。
具体的には以下の部分。
src/app/(components)/PostList.jsx
// ✅クライアントコンポーネント
"use client";
import { useState } from "react";
// ✅投稿コンポーネント
import Post from "./Post";
// ✅投稿削除関数
import { deletePost } from "@/app/lib/posts";
/**
 * 投稿のリストを表示
 * @function PostList
 * @param {Object} props - プロパティオブジェクト
 * @param {Array} props.posts - 投稿データの配列
 * @returns {JSX.Element} 投稿リストを含むReactコンポーネント
 */
const PostList = (props) => {
  // 投稿データのリスト
  const [posts, setPosts] = useState(props.posts);
  /**
   * ✅投稿データを削除
   * @function handleDelete
   * @param {Object} data - 削除する投稿データ
   */
  const handleDelete = (data) => {
    deletePost(data); // データベースから削除
    const id = parseInt( data.get('id') );  // 数値に変換
    setPosts(posts.filter((post) => post.id !== id)); // 画面から削除
  };
  return (
    <table className="mt-8 w-full table-auto">
      <tbody>
        {posts.map((post) => (
          <Post
            key={post.id}
            post={post}
            handleDelete={handleDelete}
          />
        ))}
      </tbody>
    </table>
  );
};
export default PostList;【解説】✅クライアントコンポーネント
// ✅クライアントコンポーネント
"use client";useStateを使うためにクライアントコンポーネントにした。
→useStateで投稿一覧の状態を管理して、投稿が減ると即時反映したい。
イメージ
【他の方法】revalidatePath関数を使う
useStateで投稿一覧を状態管理しなくても、削除後にrevalidatePath関数を実行することで最新データを取得しなおすこともできる。(ISR)
この方法だとPostListコンポーネントをクライアントコンポーネントではなくサーバーコンポーネントのままにしておける。
【解説】✅投稿コンポーネント
【解説】✅投稿削除関数
// ✅投稿削除関数
import { deletePost } from "@/app/lib/posts";投稿を削除(テーブルから1レコード削除)する関数。
deletePost関数は以下のファイルで定義済み。
【再掲】src/app/lib/posts.js
/**
 * 指定されたIDの投稿を削除します。
 *
 * @param {FormData} data - フォームデータ。'id'フィールドには削除する投稿のIDが含まれている必要があります。
 * @returns {Promise<void>} 削除操作が完了すると解決するPromise。
 */
export const deletePost = async (data) => {
  // 入力値を取得
  const id = data.get("id");
  // データベースから1レコード削除
  await prisma.posts.delete({
    where: {
      id: parseInt(id),
    },
  });
};【解説】✅投稿データを削除
/**
 * ✅投稿データを削除
 * @function handleDelete
 * @param {Object} data - 削除する投稿データ
 */
const handleDelete = (data) => {
  deletePost(data); // データベースから削除
  const id = parseInt( data.get('id') );  // 数値に変換
  setPosts(posts.filter((post) => post.id !== id)); // 画面から削除
};投稿の削除(テーブルから1レコード削除)を呼び出し、画面からも削除する関数。
【手順3-3】投稿1つを表示するコンポーネントを作る
✅投稿1つを表示するPostコンポーネントを作る。
具体的には以下の部分。
src/app/(components)/Post.jsx
// ✅クライアントコンポーネント
"use client";
import Link from 'next/link'
/**
 * ✅単一の投稿データを表示
 * @function Post
 * @param {Object} props - プロパティオブジェクト
 * @param {Object} props.post - 投稿データ
 * @param {Function} props.handleDelete - 投稿削除のハンドラ関数
 * @returns {JSX.Element} 投稿データを表示するReactコンポーネント
 */
const Post = ({post, handleDelete}) => {
  return (
    <tr key={post.id} className="border-t border-gray-200">
      <td className="px-4 py-2">{post.id}</td>
      <td className="px-4 py-2">{post.title}</td>
      <td className="px-4 py-2">{post.body}</td>
      <td className="px-4 py-2 flex">
				{/* 編集ボタン */}
        <Link href={`edit/${post.id}`} className="text-white bg-blue-500 hover:bg-blue-700 px-2 py-1 rounded">編集</Link>
				{/* 削除ボタン */}
				<form action={handleDelete}>
          <input type="hidden" name="id" value={post.id} />
          <button
            className="text-white bg-red-500 hover:bg-red-700 px-2 py-1 rounded ml-2"
          >
            削除
          </button>
        </form>
      </td>
    </tr>
  );
};
export default Post;【解説】✅クライアントコンポーネント
// ✅クライアントコンポーネント
"use client";親コンポーネント(PostList)がクライアントコンポーネントのため、Postもクライアントコンポーネントにした。
(クライアントコンポーネントからサーバーコンポーネントはインポートできないため)
【解説】✅単一の投稿データを表示
/**
 * ✅単一の投稿データを表示
 * @function Post
 * @param {Object} props - プロパティオブジェクト
 * @param {Object} props.post - 投稿データ
 * @param {Function} props.handleDelete - 投稿削除のハンドラ関数
 * @returns {JSX.Element} 投稿データを表示するReactコンポーネント
 */
const Post = ({post, handleDelete}) => {
  return (
    <tr key={post.id} className="border-t border-gray-200">
      <td className="px-4 py-2">{post.id}</td>
      <td className="px-4 py-2">{post.title}</td>
      <td className="px-4 py-2">{post.body}</td>
      <td className="px-4 py-2 flex">
				{/* 編集ボタン */}
        <Link href={`edit/${post.id}`} className="text-white bg-blue-500 hover:bg-blue-700 px-2 py-1 rounded">編集</Link>
				{/* 削除ボタン */}
				<form action={handleDelete}>
          <input type="hidden" name="id" value={post.id} />
          <button
            className="text-white bg-red-500 hover:bg-red-700 px-2 py-1 rounded ml-2"
          >
            削除
          </button>
        </form>
      </td>
    </tr>
  );
};
投稿1件を表示しているだけ。
具体的には以下を表示している。
- id、タイトル、本文
<td className="px-4 py-2">{post.id}</td> <td className="px-4 py-2">{post.title}</td> <td className="px-4 py-2">{post.body}</td> 
- 編集ボタン
{/* 編集ボタン */} <Link href={`edit/${post.id}`} className="text-white bg-blue-500 hover:bg-blue-700 px-2 py-1 rounded">編集</Link>- ただのリンク。
 - リンク先はidを使って動的に決めている。
 - リンク先の編集ページは【手順5】で作る。
 
 
- 削除ボタン
{/* 削除ボタン */} <form action={handleDelete}> <input type="hidden" name="id" value={post.id} /> <button className="text-white bg-red-500 hover:bg-red-700 px-2 py-1 rounded ml-2" > 削除 </button> </form>- Server Actionsを使って、削除ボタンクリック時にhandleDelete関数を呼び出す。
 - Server Actionsを使うにはformタグが必須。
 - handleDelete関数(【手順3-2】で作った親コンポーネントの関数)が呼ばれると、データベースと画面から投稿が削除される。
 - handleDelete関数にidを渡したいため、非表示の<input>タグを入れている。
 
 - Server Actionsを使って、削除ボタンクリック時にhandleDelete関数を呼び出す。
 
【手順4】投稿作成ページ
✅「タイトル」「本文」を入力して投稿を作成するページ。
完成イメージ
【手順4-1】ページを作る
✅URL「/create」でアクセスできる投稿作成ページを作る。
src/app/create/page.jsx
import { addPost } from "@/app/lib/posts";
/**
 * 新規投稿を作成するためのフォームを表示します。
 * @async
 * @function Page
 * @returns {JSX.Element} 新規投稿作成フォームを含むReactコンポーネント
 */
const Page = async () => {
  // ✅投稿の内容を入力するフォーム
  return (
    <div className="m-10">
      <h1 className="text-xl font-bold">投稿作成</h1>
      <form className="flex mt-8 flex-col" action={addPost}>
        <label htmlFor="title">タイトル:</label>
        <input
          type="text"
          name="title"
          className="border mx-4 p-1 w-full text-black"
        />
        <label htmlFor="body" className="mt-4">
          本文:
        </label>
        <textarea
          name="body"
          rows="4"
          className="border mx-4 p-1 w-full text-black"
        />
        <button
          type="submit"
          className="bg-blue-600 px-2 py-1 rounded-lg text-sm text-white mx-4 mt-4"
        >
          作成
        </button>
      </form>
    </div>
  );
};
export default Page;【解説】✅投稿の内容を入力するフォーム
  // ✅投稿の内容を入力するフォーム
  return (
    <div className="m-10">
      <h1 className="text-xl font-bold">投稿作成</h1>
      <form className="flex mt-8 flex-col" action={addPost}>
				省略
      </form>
    </div>
  );
Server Actionsを使って、作成ボタンクリック時にaddPost関数を呼び出す。
addPost関数は以下のファイルで定義済み。
【再掲】src/app/lib/posts.js
/**
 * 新規投稿をデータベースに作成します。
 * @async
 * @function addPost
 * @param {Object} data - 新規投稿のデータ
 */
export const addPost = async (data) => {
  // 入力値を取得
  const title = data.get("title");
  const body = data.get("body");
  // データベースに1レコード作成
  await prisma.posts.create({
    data: {
      title,
      body,
    },
  });
};【手順5】投稿編集ページ
✅「タイトル」「本文」を編集するページ。
完成イメージ
【手順5-1】ページを作る
✅URL「/edit/〇〇」でアクセスできる投稿編集ページを作る。
(〇〇には編集したい投稿のidが入る)
src/app/edit/[id]/page.jsx
※[id]は動的なパス(URLの〇〇の部分)を表す。
import prisma from "@/app/lib/prisma";
import { updatePost } from "@/app/lib/posts";
/**
 * 指定されたIDの投稿を編集するためのフォームを表示します。
 * @async
 * @function Page
 * @param {Object} params - パラメータオブジェクト
 * @param {string} params.id - 編集する投稿のID
 * @returns {JSX.Element} 投稿編集フォームを含むReactコンポーネント
 */
const Page = async ({params}) => {
  const id = params.id;
  // 投稿データを取得
  const post = await prisma.posts.findUnique({
    where: {
      id: parseInt(id),
    },
  });
	// ✅投稿の内容を編集するフォーム
  return (
    <div className="m-8">
      <h1 className="text-2xl font-bold mb-4">投稿編集</h1>
      <p>ID: {id}</p>
      <form action={updatePost}>
          <input type="hidden" name="id" value={post.id} />
          <input name="title" type="text" defaultValue={post.title} className="w-full px-3 py-2 placeholder-gray-300 border border-gray-300 rounded-md focus:outline-none focus:ring focus:ring-indigo-100 focus:border-indigo-300 dark:bg-gray-700 dark:text-white dark:placeholder-gray-500 dark:border-gray-600 dark:focus:ring-gray-900 dark:focus:border-gray-500" />
          <textarea name="body" defaultValue={post.body} className="w-full px-3 py-2 mt-4 placeholder-gray-300 border border-gray-300 rounded-md focus:outline-none focus:ring focus:ring-indigo-100 focus:border-indigo-300 dark:bg-gray-700 dark:text-white dark:placeholder-gray-500 dark:border-gray-600 dark:focus:ring-gray-900 dark:focus:border-gray-500" />
          <button className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700" >更新</button>
      </form>
    </div>
  );
};
export default Page;【解説】✅投稿の内容を編集するフォーム
// ✅投稿の内容を編集するフォーム
return (
  <div className="m-8">
    <h1 className="text-2xl font-bold mb-4">投稿編集</h1>
    <p>ID: {id}</p>
    <form action={updatePost}>
      省略
    </form>
  </div>
);
Server Actionsを使って、更新ボタンクリック時にupdatePost関数を呼び出す。
updatePost関数は以下のファイルで定義済み。
【再掲】src/app/lib/posts.js
/**
 * 指定されたIDの投稿を更新します。
 * 
 * @param {FormData} data - フォームデータ。'id'フィールドには更新する投稿のIDが含まれている必要があります。
 * @returns {Promise<void>} 更新操作が完了すると解決するPromise。
 */
export const updatePost = async (data) => {
  // 入力値を取得
  const id = data.get("id");
  const title = data.get("title");
  const body = data.get("body");
  // データベースのレコードを更新
  await prisma.posts.update({
    where: {
      id: parseInt(id),
    },
    data: {
      title: title,
      body: body,
    },
  });
};まとめ
投稿一覧ページ
| 投稿一覧ページ | |
|---|---|
| ファイルパス | src/app/page.jsx | 
| URL | / | 
| ページの役割 | ・全投稿を表示 ・投稿の削除  | 
| 使用コンポーネント | ・PostList(投稿一覧) ・Post(単一の投稿)  | 
| SSG/SSR/ISR | ・SSR | 
| Server Actions | ・投稿削除で使用 | 
| サーバーコンポーネント/クライアントコンポーネント | ・一部クライアントコンポーネント | 
投稿作成ページ
| 投稿作成ページ | |
|---|---|
| ファイルパス | src/app/create/page.jsx | 
| URL | /create | 
| ページの役割 | ・投稿を新規作成 | 
| 使用コンポーネント | ー | 
| SSG/SSR/ISR | ・SSG | 
| Server Actions | ・投稿作成で使用 | 
| サーバーコンポーネント/クライアントコンポーネント | ・サーバーコンポーネント | 
投稿編集ページ
| 投稿編集ページ | |
|---|---|
| ファイルパス | src/app/edit/[id]/page.jsx | 
| URL | /edit/〇〇 | 
| ページの役割 | ・投稿を編集 | 
| 使用コンポーネント | ー | 
| SSG/SSR/ISR | ・SSR | 
| Server Actions | ・投稿編集で使用 | 
| サーバーコンポーネント/クライアントコンポーネント | ・サーバーコンポーネント | 
所感
- Server Actionsを使うと簡単にフォーム送信時にバックエンドの処理が呼び出せた。
 - コンポーネントを分けてサーバーコンポーネントとクライアントコンポーネントを分離することが重要。
- useStateなどはクライアントコンポーネントにする。
 - Prismaのデータベース操作などはサーバーコンポーネントにする。
 
 - ページを作るときはSSG/SSR/ISRを意識する必要がある。
- 投稿一覧ページはSSGだと一覧データが更新されない。SSRなどにする必要がある。
 
 



























