Next.jsとSupabaseでオンライ学習プラットフォーム（LMS）を構築する | エピソード1: プロジェクトの紹介と環境構築

こんにちは！このシリーズでは、Next.jsとSupabaseを使って、オンライ学習プラットフォーム（Learning Management System: LMS）をゼロから構築します。第1回では、LMSの概要を紹介し、開発環境を整えます。Next.js（App Router）のセットアップ、TypeScript、Tailwind CSS、ESLint、Prettierの統合、そしてSupabaseとの接続を行います。最後に、シンプルなホームページを作成して、プロジェクトの基盤を準備します！

## このエピソードのゴール

- LMSの概要とNext.js、Supabaseの役割を理解。
- Next.jsプロジェクトをTypeScript、Tailwind CSS、ESLint、Prettierでセットアップ。
- Supabaseプロジェクトを作成し、Next.jsと接続。
- シンプルなホームページを構築。

## 必要なもの

- Node.js（18.x以上）とnpm。
- テキストエディタ（VS Code推奨）。
- Supabaseアカウント（無料プランで十分）。
- 基本的なJavaScript、React、CSSの知識。

---

## ステップ1: LMSと使用技術の概要

### LMSとは？
LMS（Learning Management System）は、コース管理、ユーザー認証、ビデオ講義、リアルタイムコメントなどを提供するオンライ学習プラットフォームです。このシリーズでは、以下のような機能を実装します：
- ユーザー登録・ログイン（Supabase Auth）
- コースとレッスンの管理（Supabase Database）
- ビデオ講義の配信（Supabase Storage）
- リアルタイムコメント（Supabase Realtime）
- 決済機能（Stripe）

### なぜNext.jsとSupabase？
- **Next.js**: App RouterとReact Server Componentsを活用し、高速でSEOに強いWebアプリを構築。静的生成（SSG）やサーバーサイドレンダリング（SSR）でパフォーマンスを最適化。
- **Supabase**: オープンソースのFirebase代替として、認証、データベース、ストレージ、リアルタイム機能を簡単に統合可能。PostgreSQLベースでスケーラブル。

---

## ステップ2: Next.jsプロジェクトのセットアップ

Next.jsプロジェクトを初期化し、必要なツールを統合します。

1. **プロジェクトの作成**：
   ターミナルで以下を実行して、Next.jsプロジェクトをセットアップ：

```bash
npx create-next-app@latest next-lms --typescript --eslint --tailwind --app --src-dir --import-alias "@/*"
```

以下のオプションを選択：
- TypeScript: Yes
- ESLint: Yes
- Tailwind CSS: Yes
- `src/` directory: Yes
- App Router: Yes
- Customize import alias: `@/*`

2. **Prettierの追加**：
   コードフォーマットを統一するため、Prettierをインストール：

```bash
npm install --save-dev prettier
```

`package.json`にフォーマットスクリプトを追加：

```json
"scripts": {
  "format": "prettier --write \"src/**/*.{ts,tsx}\""
}
```

`.prettierrc`ファイルを作成：

```json
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "tabWidth": 2,
  "useTabs": false
}
```

`.prettierignore`ファイルを作成：

```
.next
node_modules
public
```

ESLintとPrettierの競合を防ぐため、`.eslintrc.json`を更新：

```json
{
  "extends": ["next/core-web-vitals", "plugin:prettier/recommended"],
  "rules": {
    "prettier/prettier": "error"
  }
}
```

3. **プロジェクトのクリーンアップ**：
   - `src/app/page.tsx`を以下に置き換えてシンプルなスタートページを作成：

```tsx
export default function Home() {
  return (
    <main className="container mx-auto p-4">
      <h1 className="text-3xl font-bold">Welcome to Next.js LMS</h1>
    </main>
  );
}
```

   - `src/app/globals.css`を以下に更新してTailwind CSSを適用：

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

---

## ステップ3: Supabaseのセットアップ

Supabaseプロジェクトを作成し、Next.jsと接続します。

1. **Supabaseプロジェクトの作成**：
   - [Supabaseダッシュボード](https://supabase.com/)にログインし、「New Project」を作成。
   - プロジェクト名（例: `next-lms`）を入力し、リージョンを選択。
   - 作成後、プロジェクトのAPIキー（`anon public`）とURLを取得。

2. **Supabaseクライアントのインストール**：
   プロジェクトにSupabaseクライアントをインストール：

```bash
npm install @supabase/supabase-js
```

3. **環境変数の設定**：
   `.env.local`ファイルを作成し、Supabaseの認証情報を追加：

```env
NEXT_PUBLIC_SUPABASE_URL=あなたのSupabaseプロジェクトURL
NEXT_PUBLIC_SUPABASE_ANON_KEY=あなたのSupabase公開キー
```

**注意**: 機密情報は`.env.local`に保存し、GitHubにアップロードしないよう`.gitignore`に追加してください。

4. **Supabaseクライアントの初期化**：
   `src/lib/supabase.ts`ファイルを作成：

```ts
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const supabase = createClient(supabaseUrl, supabaseKey);
```

このコードは、Supabaseクライアントを初期化し、プロジェクト全体で使用可能にします。

5. **Supabase接続のテスト**：
   `src/app/page.tsx`を更新して、Supabaseが正常に接続されているか確認：

```tsx
import { supabase } from '@/lib/supabase';

export default async function Home() {
  // テストクエリ: 空のテーブルをクエリして接続確認
  const { data, error } = await supabase.from('courses').select('*').limit(1);

  return (
    <main className="container mx-auto p-4">
      <h1 className="text-3xl font-bold">Welcome to Next.js LMS</h1>
      <p className="mt-4">
        {error ? 'Supabase接続エラー' : 'Supabase接続成功！'}
      </p>
    </main>
  );
}
```

**注意**: この時点では`courses`テーブルは存在しないため、エラーが表示される可能性があります。次のエピソードでテーブルを作成します。

---

## ステップ4: ホームページの構築

シンプルなホームページを構築して、プロジェクトの基盤を完成させます。`src/app/[locale]/layout.tsx`を作成し、基本的なレイアウトを定義（多言語対応を考慮）：

```tsx
import '../styles/globals.css';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body className="bg-gray-100">
        <header className="bg-primary text-white p-4">
          <div className="container mx-auto">
            <h1 className="text-2xl font-bold">Next.js LMS</h1>
          </div>
        </header>
        {children}
      </body>
    </html>
  );
}
```

`src/app/page.tsx`を更新して、Tailwind CSSを使ったレスポンシブなホームページを作成：

```tsx
export default function Home() {
  return (
    <main className="container mx-auto p-4">
      <section className="text-center py-12">
        <h1 className="text-4xl font-bold mb-4">Next.js LMSへようこそ</h1>
        <p className="text-lg text-gray-600 mb-6">
          高性能なオンライ学習プラットフォームで、どこでも学べます。
        </p>
        <a
          href="/courses"
          className="bg-primary text-white px-6 py-3 rounded hover:bg-opacity-90"
        >
          コースを探索
        </a>
      </section>
    </main>
  );
}
```

`src/styles/globals.css`にカスタムスタイルを追加（必要に応じて）：

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
```

---

## ステップ5: 動作確認

1. 開発サーバーを起動：

```bash
npm run dev
```

2. `http://localhost:3000`にアクセスし、以下の点を確認：
   - ホームページが表示され、Tailwind CSSのスタイルが適用されている。
   - ヘッダーに「Next.js LMS」が表示される。
   - 「コースを探索」ボタンが表示され、クリックすると`/courses`に遷移（まだ実装していないため404エラー）。
3. コードをフォーマット：

```bash
npm run format
```

4. Supabaseダッシュボードでプロジェクトが正しく作成されていることを確認。

エラーがあれば、`.env.local`のSupabaseキーやプロジェクトの設定を確認してください。

---

## まとめと次のステップ

このエピソードでは、LMSプロジェクトの概要を紹介し、Next.jsとSupabaseの開発環境をセットアップしました。TypeScript、Tailwind CSS、ESLint、Prettierを統合し、シンプルなホームページを構築しました。これで、プロジェクトの基盤が整いました！

次回のエピソードでは、Supabase Authを使ってユーザー認証（登録、ログイン、Google OAuth）を実装します。React Hook Formを使ったフォームバリデーションやプロテクトルートも追加しますので、引き続きお楽しみに！

---

この記事が役に立ったと思ったら、ぜひ「いいね」を押して、ストックしていただければ嬉しいです！次回のエピソードもお楽しみに！