[Next.js] Supabase カレンダーメモアプリ チュートリアル

Next.jsの初学者向けチュートリアルコンテンツです。
カレンダー形式のメモアプリをNext.js及びTypeScriptで開発していきます。 また、入力したメモ情報の格納には、BaaS(Backend as a Service)であるSupabaseを利用します。Supabaseとの連携は、Next.jsの Server Actions を利用します。 クライアントコンポーネントからサーバーコンポーネントの実装までフルスタック型の開発を体得します。

前編、後編と2回に分けてお届けします。前編の今回は、環境構築、Supabaseの設定と、ベースデザインの作成まで行います。

1. はじめに

1.1 前提

本ドキュメントは、Next.jsの初学者向けのチュートリアルコンテンツです。シンプルなカレンダー形式のメモアプリをNext.js及びTypeScriptで開発し体得します。

ソースコード:GitHub

アプリの画面イメージは下図です。メモが入力された日付の箇所は、薄緑の円が表示されています。

日付の箇所をクリックするとメモのモーダルが表示されます。

本チュートリアルでは、Next.jsの環境構築、基本的な使い方から実際にアプリケーションをデプロイしてユーザーが使えるリリースまでを行えます。また、BaaS(Backend as a Service)であるSupabaseによるDB処理を実装し、バックエンドの要素も包括しています。これは、Next.jsの Server Actions を利用して実装します。
Next.jsの持ち味を活かした、クライアントコンポーネントからサーバーコンポネントの実装を行います。

UIはshadcn/ui, Tailwind CSSを利用しています。

Next.jsは、ReactをベースにしたオープンソースのWebアプリケーションフレームワークです。サーバーサイドレンダリング(SSR)や静的サイト生成(SSG)などの機能を備え、高速でSEOに強いWebアプリケーションを構築できます。

ReactはJavaScript言語を用いた、Webサイト上のUIを構築するためのライブラリで、フレームワークとは、開発を効率化するための枠組みです。
Next.jsは「URLルーティング」と呼ばれるリクエストされたURLに対して呼び出すアクションを決定する仕組みや、Webアプリ開発を効率よくするための機能が多く含まれているのが特長です。

本チュートリアルでは、Next.js13以降推奨となっている、App Routerを用いて開発します。

1.2 前提

本ドキュメントの環境、技術スタックは以下の通りです。

  • プラッフォーム:
    • Next.js v15.3.0
    • React v19.0.0
    • React Dom v19.0.0
    • TypeScript
  • UI:shadcn/ui (Tailwind CSS), lucide-react, sonner

  • DB:Supabase
  • SDK:Supabase SDK
  • ホスティング:Vercel

また、本チュートリアルで以下の事が学べます。

  • 技術的側面
    • Next.js App Routerの構成
      app/ ディレクトリの構造やApp Routerの基本の理解
    • React Hooksの活用
      useState, useEffect を通じた、状態管理と副作用の使い方
    • コンポーネント分割
      Calendar, MemoModal などの再利用性の高い UI コンポーネント設計
    • Server Actions の使い方
      app/actions.ts を用いたSupabase等のバックエンドサービスとの連携・データ処理
    • UIライブラリとの連携
      lucide-react, sonner, shadcn/ui などの外部UIライブラリをReactと組み合わせる手法
    • Date APIの応用
      new Date() や toISOString() を使った日付操作・月末計算・曜日処理の理解
    • トースト通知の実装
      処理成功・失敗に応じて toast.success, toast.error を表示し、UXを改善する方法
  • 設計・アーキテクチャ
    • 状態管理の適切なスコープ設計
      カレンダーとモーダルの間の状態共有
    • propsでのデータ受け渡し
      子コンポーネントに必要な情報だけを渡す設計
    • 簡易的な「状態管理的データストア」の構築
      memos をメモリ上に保持して、アプリ全体で状態を同期。
    • イベントドリブンなデザイン
      onDateClick, onSave など、イベント発火によりUIが更新される設計
  • UX・ユーザー体験
    • ビジュアルな「メモあり」インジケータ
      ●マークで日付にメモがあることをわかりやすく表示。
    • モーダルによる集中入力UI
      カレンダー上でクリック → モーダル表示の直感的な操作。
    • 月送りボタンの直感的ナビゲーション
      月の前後移動が簡単に行える。
    • ローディング・エラーハンドリング
      読み込み中や取得失敗時に明確なフィードバックを表示。

2. 環境構築

それでは、始めます。最初は開発にあたって必要な環境を構築していきます。Next.jsプロジェクトの作成・設定、Supabaseプロジェクトの作成・設定などです。

2.1 Next.jsプロジェクトの作成

まずは、JavaScript実行環境のnode.jsとパッケージマネージャのnpmをインストールします。
(既に環境がある方はスキップしてください)
macでのインストールはbrewを使う場合もありますが、無難にインストーラパッケージをインストールしました。Windowsの方も同じくインストーラからインストールです。

インストール完了したら、ターミナルでnode.jsとnpmのバージョン確認をしましょう。
-vはバージョン確認のコマンドです。

node -v
v22.12.0

npm -v
11.0.0

次にNext.jsプロジェクトを作成します。
ターミナル上で、Next.jsプロジェクトを作成したいディレクトリに移動し、
npx create-next-app でプロジェクト作成を実行します。
プロジェクト名は「calendar-memo-app」で
他の選択肢は下記の通りです。

npx create-next-app next-learnings-firebase
Need to install the following packages:
create-next-app@15.3.0
Ok to proceed? (y)

 Would you like to use TypeScript?   Yes
 Would you like to use ESLint?  Yes
 Would you like to use Tailwind CSS?  No
 Would you like your code inside a `src/` directory?  No
 Would you like to use App Router? (recommended) … Yes
 Would you like to use Turbopack for `next dev`? … No
 Would you like to customize the import alias (`@/*` by default)? … No

これでNext.jsの初期環境構築は完了です。
続けてプロジェクトディレクトリに移動し、開発サーバー起動をnpm run devで行います。

cd calendar-memo-app
npm run dev

開発サーバーが、http://localhost:3000/ で起動されると思います。
アクセスすると以下画面が表示されます。

これで、Next.jsのプロジェクト作成は完了です。

2.2 関連パッケージのインストール

続いて、サーバを起動したターミナルで「ctrl-c」を入力し一度サーバ停止するか、別のターミナルを開いてlearning-firebaseディレクトリに移動して、shadcn/uiのインストールを行います。

npx shadcn@latest init

 Preflight checks.
 Verifying framework. Found Next.js.
 Validating Tailwind CSS config. Found v4.
 Validating import alias.
 Which color would you like to use as the base color?  Neutral
 Writing components.json.
 Checking registry.
 Updating app/globals.css
Installing dependencies.

It looks like you are using React 19.
Some packages may fail to install due to peer dependency issues in npm (see https://ui.shadcn.com/react-19).

 How would you like to proceed?  Use --force
 Installing dependencies.
 Created 1 file:
- lib/utils.ts

Success! Project initialization completed.
You may now add components.

対話的なインタフェースになってますので、そのままデフォルトで進めます。
なお、執筆時点では、React19に未対応(検証未了)の為、警告が出てきますが、Use –forceを選択し、強制インストールします。

次に、今回のアプリで利用する、shadcn/uiの必要なコンポーネントをインストールします。
button, dialog, textarea, sonnerです。

npx shadcn@latest add button dialog textarea sonner

同様に、React19の警告が出る場合は、Use –forceを選択します。

続いて、Lucideアイコンと、tailwindcss-animateをインストールします。

npm install lucide-react tailwindcss-animate

次は、必須ではありませんが、あると便利な拡張機能です。
本記事ではエディタとしてVSCodeを利用していきます。

ローカルで開発される方は、拡張性の高さ、使い勝手の良さ、作業効率などで、VSCodeが圧倒的にお薦めです。そのVSCodeの拡張機能です。また、Chromeの拡張機能も利用していきます。

VSCode拡張機能

ES7+ React/Redux/React-Native snippets

rafce と入力すると、react の雛形を自動で作成してくれます。本記事でも活用します。

Eslint

コードを静的に分析して、問題をすばやく見つけます。また自動修正機能もあります。

Petiter

自動でソースコードを整形する、コードフォーマッター。

Japanese Language Pack for Visual Studio Code

VSCodeの日本語化キット

Chromeの拡張機能

React Developer Tools

chromeの検証ツールで、React コンポーネントの階層を検査できるようになります。stateやpropsの状況を可視化出来ます。

3. Supabaseの設定

続いて、Supabase関連の設定、環境準備を行います。Supabaseは、オープンソースのフルスタックバックエンドサービスです。PostgreSQLデータベースを基盤とし、リアルタイムデータベース、認証、ファイルストレージ、サーバーレス関数、APIなど、モダンなアプリケーション開発に必要な機能を包括的に提供します。

3.1 Supabaseプロジェクト作成

最初にSupabaseのプロジェクトを作成します。
まず、Supabaseのアカウントを作成します。Supabaseのサイトに行き、Sign Upをします。

https://supabase.com

GitHubアカウントとの連携も出来ます。

アカウント作成後、NewProjectで新しいプロジェクトを作成します。プロジェクト名はお好きなものをつけてください。Regionの選択はデフォルトで結構です(違うものでもいいかと思います)。

3.2 テーブルの作成

Table Editorを利用する場合

作成したプロジェクトを選択の上、左側にhover時表示されるメニューから「Table Editor」を選択します。

「Create a new table]を選択します。

Nameは「calendar_memos」で(※お好きなもので結構です)。
Descriptionは任意で
Enable Row Level Security (RLS)Recommended は、デフォルトのONのままで
Enable Realtime は不要です。
Columnsは以下内容で設定してください。

NameTypeDefault ValuePrimaryExtra options
iduuiduuid_generate_v4()
datedateIs Unique
memotext
created_attimestamptznow()Allow Nullable
updated_attimestamptznow()Allow Nullable

Foreign keysは不要です。

SQLを利用する場合

SQLを利用してテーブルを作成する事も可能です。なお、ここでは、SupabaseのWEBコンソール上のSQLエディタを利用する場合を紹介します。Supabase CLIをローカル環境にセットアップしてSQLを実行する事も可能ですが、Docker環境が必要となりますので、ここでは割愛します。

WEBコンソールで作成したプロジェクトを選択の上、左側にhover時表示されるメニューから「SQL Editor」を選択します。

右側に表示される、SQLの入力欄に以下のSQL文を貼り付けます。

-- calendar_memos テーブルの作成
CREATE TABLE IF NOT EXISTS calendar_memos (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  date DATE NOT NULL UNIQUE,
  memo TEXT NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- インデックスの作成
CREATE INDEX IF NOT EXISTS calendar_memos_date_idx ON calendar_memos(date);

-- RLSポリシーの設定
ALTER TABLE calendar_memos ENABLE ROW LEVEL SECURITY;

右下の「Run」をクリックします。

下のResultsの箇所にSuccessと表示されれば成功です。

Success. No rows returned

WEBコンソールの「Table Editor」でテーブルcalendar_memosが生成されていると思います。

3.3 ポリシーの作成

続いて作成したテーブルに対して、ポリシー作成を行います。これはテーブルのデータ操作(SELECT,INSERT,UPDATE,DELETE)に対する権限を設定するものです。

作成テーブルcalendar_memosの右側にある、・・・の箇所をクリックすると表示されるメニューから、View Policies を選択します。

表示される画面の右側の、Create Policyをクリックします。

以下画面のように設定していきます。

  • Policy Name:任意のポリシー名を入力します。ここではcalendar_memos policyとしてます。
  • Polciy Command:ALLを選択、全ての操作に共通のポリシーとしてます。
  • Target Roles:anonを選択
  • Use options above to edit:7行目, 9行目に、tureと記載
  • Use check expression:チェックする(デフォルト)

この内容で、画面下にある、Save policyをクリックしてポリシーを保存します。

3.4 Supabase連携機能

続いて、Supabaseとの連携の為の環境を整備していきます。
まずは、supabaseのパッケージをインストールします。
ターミナルにて、プロジェクトのディレクトに移動し、下記コマンドを投入します。

 npm install @supabase/supabase-js

続いてVSCode等のエディタでプロジェクトを開きます。
Supabaseの環境設定ファイルをプロジェクトルート直下に、また、Supabaseのクライアント機能のファイルを/lib配下に作成します。

  • supabaseの環境設定:/.env.local
  • supabaseのクライアント機能:/lib/supabase.ts

まずは、.env.localです。ここには、Supabase接続の為の機密情報を記載しています。

// /.env.local
NEXT_PUBLIC_SUPABASE_URL=Supabaseのアクセス先URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=SupabaseAPIキー

ここで、NEXT_PUBLIC_SUPABASE_URLとNEXT_PUBLIC_SUPABASE_ANON_KEYを記載しますが、それぞれの内容は、Supabaseのサイトにて確認します。Supabaseのプロジェクトの設定 > Data APIから、Project URL及びProject API Keys内のanon keyをコピー(下図参照)し、上記の.env.localにそれぞれ貼り付けます。

続いて、/lib/supabase.tsです。下記コードを記載します。他のコンポーネントからDBデータの参照や更新等の処理を行う際は、この機能を呼び出して利用します。

// /lib/supabase.ts

import { createClient } from "@supabase/supabase-js";

// 環境変数から接続情報を取得
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";

// Supabaseクライアントを作成
export const supabase = createClient(supabaseUrl, supabaseAnonKey);

Supabase SDKである、@supabase/supabase-jsより、createClientメソッドをインポートし、Supabaseクライアントを生成しています。supabaseUrlとsupabaseAnonKeyを先に作成した.env.localより取得しています。取得できない場合は、””で空設定としてします。

この環境変数を.env.localで分離しているのは、セキュリティ上の理由です。機密情報をコード上にハードコーディングするのは好ましくない為です。

以上でSupabaseの設定は完了です。
次からアプリの開発を進めていきます。

4. ベースレイアウト

続いて、アプリのベース構造、レイアウトを作成します。

4.1 既存ファイルの更新

まず、アプリ全体のレイアウトファイルである、/app/layout.tsxについて、デフォルトで記載されているものを更新します。以下内容となります。

// /app/layout.tsx

import type { Metadata } from "next";
-// import { Geist, Geist_Mono } from "next/font/google";//削除
+import { Inter } from "next/font/google";//追加
import "./globals.css";
+import { Toaster } from "sonner"; //追加

-//以下削除
-// const geistSans = Geist({
-//   variable: "--font-geist-sans",
-//   subsets: ["latin"],
-// });

-// const geistMono = Geist_Mono({
-//   variable: "--font-geist-mono",
-//   subsets: ["latin"],
-// });

+const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
- // title: "Create Next App",
- // description: "Generated by create next app",
+ title: "カレンダーメモアプリ",
+ description: "日付ごとにメモを記録できるカレンダーアプリ",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
-   // <html lang="en">
+   <html lang="ja">
      <body
-       // className={`${geistSans.variable} ${geistMono.variable} antialiased`}
+       className={`${inter.className}, antialiased`}
      >
        {children}
        {/* Toaster追加 */}
+       <Toaster position="top-right" richColors />
      </body>
    </html>
  );
}

フォントを、Interに変更しています。Interは、モダンで読みやすいサンセリフフォントです。様々な画面サイズや解像度で一貫した表示が可能で、日本語と英語の混在テキストでも美しく表示されます。Next.jsの公式サイトでも推奨されています。
また、Next.jsのnext/font/googleを使用することで、フォントが自動的に最適化されます。

他、title, descriptionの変更や、JSX箇所、にしてます。それと、データを保存・更新した際などに、トースト(処理が終了した、エラーが発生した等のメッセージ通知を行うボックス)を実装する為、そのインポートとJSX箇所にとして配置しています。これは、トップレイアウトコンポーネントでラップする事が望ましいです。
なお、position=”top-right” richColorsは、トーストの表示箇所が右上、配色はリッチカラーのオプションを指しています。

ReactではHTMLの部分とjsの部分を1つのファイルに書くことができます。これを、JSXと呼びます。
// /app/page.tsx

// import Image from "next/image";//削除

export default function Home() {
  return <div>Home</div>;
}

Homeとだけ表示される内容です。

4.2 コンポーネントの作成

続いて、必要となるクライアントコンポーネントを作成します。

/appフォルダ配下に新たにcomponentsフォルダを作成します。このフォルダの中に、以下のファイルを作成します。

  • Calendar.tsx ・・・ カレンダーUIを提供するコンポーネント
  • MemoModal.tsx ・・・ メモ入力・更新を行うUIコンポーネント

それぞれのファイルで雛形を記載します。2.2でご紹介した、VSCodeの拡張機能、ES7+ React/Redux/React-Native snippets を導入していれば、空の状態に”rafce”と入力するだけで自動で雛形コードが生成されます。

// /app/components/Calendar.tsx

import React from 'react'//react17以降は不要

const Calendar = () => {
  return (
    <div>Calendar</div>
  )
}

export default Calendar

// /app/components/MemoModal.tsx

import React from 'react'//react17以降は不要

const MemoModal = () => {
  return (
    <div>MemoModal</div>
  )
}

export default MemoModal

それぞれ、コメントに記載している通り、冒頭のimport React from ‘react’はReact17以降は記述しなくても自動でインポートされますので、不要です。削除しても構いません。本ドキュメントでも削除して進めます。

なお、コンポーネントの定義で、export default function Home() {
と言うfunction定義による記載の仕方と、下記のようにアロー関数で記載する仕方の双方を目にすると思います。
const Calendar = () => { …. export default Calendar
これは一概に良し悪しは言えなく、どちらでも良いと言う見解が多いようです。この辺はプロジェクトのルールや個人の好みでいいのかなと思います。なお、Next.jsの公式ドキュメント等は、export default functionの記載で統一されていますが、これを推奨している訳ではないようです。

続いて、/app/page.tsxを以下のように変更します。作成したコンポーネントを配置します。

// /app/page.tsx

+import Calendar from "./components/Calendar";//インポート追加
+import MemoModal from "./components/MemoModal";//インポート追加

export default function Home() {
  return (
    <div>
      Home
      {/* コンポーネント配置追加 */}
+     <Calendar />
+     <MemoModal />
    </div>
  );
}

これにより、Homeの文字の下に、Calendar.tsx、MemoModal.tsxの内容が表示されます。以下画面となると思います。

更に、/app/page.tsxを少しスタイリングします。以下内容に変更します。

// /app/page.tsx

import Calendar from "./components/Calendar";
import MemoModal from "./components/MemoModal";

export default function Home() {
  return (
+   <div className="container mx-auto p-4">
+     <h1 className="text-2xl font-bold mb-4">カレンダーメモアプリ</h1>
      <Calendar />
      <MemoModal />
    </div>
  );
}

div, h1のclassNameとして、shadcn/uiのエンジンとなる、Tailwind CSSのCSS設定を指定しています。これは、VSCodeであれば、例えば、mx-autoの箇所をマウスオーバーすると、下図のようにポップアップでそのクラスの内容が確認出来ます。margin-inline: autoと言う内容です。

このマウスオーバ表示を実現するには、VSCodeの拡張機能、Tailwind CSS IntelliSenseのインストールが必要です。
https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss

これで下図のような表示となります。

4.3 URLルーティングについて

今回のアプリのチュートリアルではあまり出てこないですが、Next.jsの持つ特長の一つ、URLルーティングの仕組みについて触れておきます。Next.jsはURLルーティングを自動生成してくれます。初期化時に生成されたフォルダにファイルを配置すると、それに合わせてURLが生成されます。

例えば、トップページである、/app/page.tsxですが、page.tsxと言うファイルですが、http://localhost:3000/ でアクセスすると表示されます。このpage.tsxはそのディレクトリに配置されると、ディレクトリのパスをURLとして自動で表示されます。

ちょっと試してみます。
/app配下に新たにsampleというフォルダを作成します。その中にpage.tsxを作成します。

rafceで雛形を作成します。コンポーネント名はSamplePageとしましょう。

// /app/sample/page.tsx

const SamplePage = () => {
  return <div>SamplePage</div>;
};

export default SamplePage;

これで、http://localhost:3000/sample/ にアクセスします。
以下の通り/app/sample/page.tsxが表示されます。

続いて/app/sample/の配下に[id]と言うフォルダを作成します。同様にその中にpage.tsxを作成します。

同様にrafceで雛形を作成します。コンポーネント名は、DynamicSamplePageとします。

// /app/sample/[id]/page.tsx

const DynamicSamplePage = () => {
  return <div>DynamicSamplePage</div>;
};

export default DynamicSamplePage;

そして、http://localhost:3000/sample/ の下に適当な文字列を追加してアクセスしてみます。例えば、http://localhost:3000/sample/aojodosl324 とかです。そうすると、以下の通り、/app/sample/[id]/page.tsx が表示されます。

この[]で囲まれたものは、動的ルーティングとして機能します。上記では[id]としてますが、idを動的なパラメータとして付与すると、それに従ったURLをで表示することが出来ます。
この動的ルーティング機能で商品idやメンバーidの詳細表示等、id毎のページを作る必要はありません。

前編はここまでで終了です。
後編では、カレンダー、メモモーダルの実装、Server Actionsの開発、GitHubリポジトリ作成とプッシュ、Vercelへのデプロイなどについて、掲載します。

No responses yet

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

AD




TWITTER


アーカイブ
OTHER ISSUE
PVアクセスランキング にほんブログ村