😽

私が考えたReactフロントエンド環境(React Router v7)

に公開

はじめに

https://github.com/dbd-fish/templete_web_system
本プロジェクトのReact Router v7を中心としたWebフロントエンド環境を紹介します。
状態管理やhookはほとんど使用していません。
1人で考えたため、些細なことでも何か意見があればコメントして頂きたいです。

本記事の大部分は生成AIに書かせてみました。
READMEっぽくなってしまう箇所はある程度妥協して筆者が微調整をしました。

全体像

本プロジェクトはDocker Composeを使用したマルチコンテナ構成のWebシステム開発テンプレートです。
フロントエンド、バックエンド、データベース、テスト環境を統合管理し、効率的な開発環境を提供します。

システム構成

  • フロントエンド: React Router v7 + Vite + TypeScript + Tailwind CSS + shadcn/ui
  • バックエンド: FastAPI + PostgreSQL(別コンテナで構築、本記事では詳細省略)
  • E2Eテスト: Cypress(テスト環境)(別コンテナで構築、本記事では詳細省略)
  • データベース: PostgreSQL 13(別コンテナで構築、本記事では詳細省略)
  • 開発環境: MSW + TypeScript strict mode

コンテナ構成

本システムは4つの主要コンテナで構成されています。
フロントエンドコンテナ(React+Vite、ポート5173)がユーザーインターフェースを提供し、HTTPS対応の開発サーバーで動作します。
バックエンドコンテナ(FastAPI、ポート8000)とAPI通信を行い、データベースコンテナ(PostgreSQL、ポート5432)からデータを取得します。
CypressコンテナでE2Eテストが可能となっています。

コンテナ名 技術スタック ポート 役割
フロントエンド React Router v7 + Vite 5173 ユーザーインターフェースの提供(HTTPS対応)
バックエンド FastAPI 8000 API処理とビジネスロジック
データベース PostgreSQL 5432 データの永続化
Cypress Cypress - E2Eテストの実行

データフローとしては、フロントエンドからバックエンドへのAPI通信、バックエンドからデータベースへのデータアクセスします。開発時はMSWによるモック環境でフロントエンド単体での動作確認も可能です。

Docker環境の詳細設定はGitHubを参照してください。

プロジェクト概要

React Router v7 + Vite + TypeScript + Tailwind CSS + shadcn/ui を使用したモダンWebアプリケーションです。
Feature-based Architectureを採用し、認証機能と静的ページを実装しました。

基本技術スタック

  • フレームワーク: React 18.3 + React Router v7.6(SSR対応)
  • ビルドツール: Vite 5.4(高速開発サーバー・ビルドツール)
  • 言語: TypeScript 5.8(strict mode有効)
  • スタイリング: Tailwind CSS 3.4 + shadcn/ui(12コンポーネント実装済み・最新版)
  • 開発ツール: ESLint 8.57 + @typescript-eslint 8.36 + Prettier
  • モックサーバー: MSW 2.10(開発環境で自動起動)
  • 実行環境: ts-node(TypeScript実行環境)
  • 要求環境: Node.js >= 20.0.0 + Docker & Docker Compose
  • CI: GitHub Actions(TypeScript型チェック + ESLint + Prettier)

ディレクトリ構成

フロントエンドの主要なディレクトリ構成は下記の通りです。
docker-compose.ymlなどはGitHubを参照してください。

frontend/
├── Dockerfile                  # フロントエンドコンテナ設定
├── package.json                # Node.js依存関係
├── package-lock.json           # 依存関係ロックファイル
├── vite.config.ts              # Vite設定(HTTPS対応)
├── react-router.config.ts      # React Router設定(SSR有効)
├── tsconfig.json               # TypeScript設定(strict mode)
├── tailwind.config.ts          # Tailwind CSS設定
├── components.json             # shadcn/ui設定
├── .eslintrc.cjs               # ESLint設定
├── .prettierrc                 # Prettier設定
├── app/                        # Reactアプリケーション
│   ├── components/             # 共通UIコンポーネント
│   │   ├── common/             # 汎用コンポーネント(3ファイル)
│   │   │   ├── ErrorMessage.tsx   # エラーメッセージ表示
│   │   │   ├── SimpleCard.tsx     # シンプルカード
│   │   │   └── SiteTitle.tsx      # サイトタイトル
│   │   ├── layout/             # レイアウト関連(6ファイル)
│   │   │   ├── Footer.tsx         # フッター
│   │   │   ├── Header.tsx         # ヘッダー制御
│   │   │   ├── Layout.tsx         # 基本レイアウト
│   │   │   ├── LoggedInHeader.tsx # ログイン時ヘッダー
│   │   │   ├── LoggedOutHeader.tsx # ログアウト時ヘッダー
│   │   │   └── Main.tsx           # メインエリア
│   │   └── ui/                 # shadcn/ui コンポーネント(12ファイル)
│   │       ├── accordion.tsx      # アコーディオン
│   │       ├── badge.tsx          # バッジ
│   │       ├── button.tsx         # ボタン
│   │       ├── card.tsx           # カード
│   │       ├── checkbox.tsx       # チェックボックス
│   │       ├── input.tsx          # 入力フィールド
│   │       ├── radio-group.tsx    # ラジオグループ
│   │       ├── select.tsx         # セレクト
│   │       ├── sheet.tsx          # シート
│   │       ├── switch.tsx         # スイッチ
│   │       ├── tabs.tsx           # タブ
│   │       └── textarea.tsx       # テキストエリア
│   ├── features/               # 機能別モジュール(Feature-based Architecture)
│   │   ├── auth/               # 認証機能(完全実装)
│   │   │   ├── actions/        # React Router アクション
│   │   │   │   └── logoutAction.tsx
│   │   │   ├── apis/           # API関数
│   │   │   │   └── authApi.ts     # 統合認証API
│   │   │   ├── components/     # 認証コンポーネント(5ファイル)
│   │   │   │   ├── LoginForm.tsx
│   │   │   │   ├── ProfileCard.tsx
│   │   │   │   ├── ResetPasswordForm.tsx
│   │   │   │   ├── SendResetPasswordForm.tsx
│   │   │   │   └── SignupForm.tsx
│   │   │   ├── errors/         # 認証エラークラス
│   │   │   │   └── AuthenticationError.tsx
│   │   │   ├── loaders/        # React Router ローダー(2ファイル)
│   │   │   │   ├── authTokenLoader.tsx
│   │   │   │   └── userDataLoader.tsx
│   │   │   ├── pages/          # ページコンポーネント(9ファイル)
│   │   │   │   ├── home.tsx       # ホームページ
│   │   │   │   ├── login.tsx      # ログインページ
│   │   │   │   ├── mypage.tsx     # マイページ
│   │   │   │   ├── signup.tsx     # サインアップ
│   │   │   │   ├── send-signup-email.tsx
│   │   │   │   ├── signup-vertify-complete.tsx
│   │   │   │   ├── send-reset-password-email.tsx
│   │   │   │   ├── send-reset-password-email-complete.tsx
│   │   │   │   ├── reset-password.tsx
│   │   │   │   └── reset-password-complete.tsx
│   │   │   ├── types.ts        # 認証専用型定義
│   │   │   ├── cookies.ts      # Cookie管理
│   │   │   └── passwordValidation.ts # パスワードバリデーション
│   │   └── pages/              # 静的ページ群
│   │       └── pages/          # 静的ページ(6ファイル)
│   │           ├── NotFound.tsx   # 404ページ
│   │           ├── aboutUs.tsx    # 運営者情報
│   │           ├── contact.tsx    # お問い合わせ
│   │           ├── eCommerceLaw.tsx # 特定商取引法
│   │           ├── privacyPolicy.tsx # プライバシーポリシー
│   │           └── termsOfService.tsx # 利用規約
│   ├── hooks/                  # カスタムフック(1ファイル)
│   │   └── useClickOutside.tsx    # 外部クリック検出
│   ├── lib/                    # shadcn/ui専用ユーティリティ
│   │   └── utils.ts               # cn関数(clsx + tailwind-merge)
│   ├── mocks/                  # MSW モックAPI
│   │   ├── data/               # モックデータ(2ファイル)
│   │   │   ├── auth.ts        # 認証関連データ
│   │   │   └── users.ts       # ユーザーデータ
│   │   ├── handlers/           # MSWハンドラー(1ファイル)
│   │   │   └── authHandlers.ts # 認証APIハンドラー
│   │   ├── utils/              # モックユーティリティ(1ファイル)
│   │   │   └── mockHelpers.ts # ヘルパー関数
│   │   ├── browser.ts          # ブラウザ用MSW設定
│   │   └── server.ts           # サーバー用MSW設定
│   ├── utils/                  # アプリケーションユーティリティ(2ファイル)
│   │   ├── apiErrorHandler.ts     # API エラーハンドリング
│   │   └── types.ts               # 共通型定義
│   ├── entry.client.tsx        # クライアントエントリーポイント
│   ├── entry.server.tsx        # サーバーエントリーポイント
│   ├── root.tsx                # ルートコンポーネント
│   ├── routes.ts               # ルーティング設定(15ルート)
│   └── tailwind.css            # Tailwind CSS
├── certs/                      # SSL証明書(HTTPS開発環境用)
│   ├── cert.pem
│   └── key.pem
├── public/                     # 静的ファイル
│   └── mockServiceWorker.js    # MSW Service Worker
└── CLAUDE.md                   # 開発ガイド

# 別ディレクトリのGitHub Actionsファイル
.github/
└── workflows/
    └── github-actions_frontend_prettier_eslint.yml # フロントエンド品質チェック

ルーティング構成

# 認証関連(9ルート)
/                                    # ホーム
/login                              # ログイン
/mypage                             # マイページ
/signup                             # ユーザー登録
/send-signup-email                  # 仮登録メール送信
/signup-vertify-complete            # メール認証完了
/send-reset-password-email          # パスワードリセットメール送信
/send-reset-password-email-complete # パスワードリセットメール送信完了
/reset-password                     # パスワードリセット
/reset-password-complete            # パスワードリセット完了

# 静的ページ(5ルート)
/privacy-policy                     # プライバシーポリシー
/terms-of-service                   # 利用規約
/e-commerce-law                     # 特定商取引法
/about-us                           # 運営者情報
/contact                            # お問い合わせ

# エラーページ
/*                                  # 404 NotFound

技術仕様

主要依存関係

  • React関連: react 18.3, react-dom 18.3, react-router 7.6
  • ビルド: vite 5.4, @react-router/dev 7.6
  • TypeScript: typescript 5.8, @types/react 18.3, @types/node 22.16
  • スタイリング: tailwindcss 3.4, clsx 2.1, tailwind-merge 3.3
  • UI コンポーネント: shadcn/ui関連パッケージ
  • 開発・テスト: eslint 8.57, prettier, @typescript-eslint 8.36, msw 2.10

HTTPS開発環境

// vite.config.ts
server: {
  host: true,
  port: 5173,
  https: {
    key: './certs/key.pem',
    cert: './certs/cert.pem',
  }
}
  • 開発サーバーはHTTPS対応(https://localhost:5173
  • 証明書はcerts/ディレクトリに配置
  • 本番環境に近い開発体験を提供

MSWモックシステム

開発環境では**MSW(Mock Service Worker)**により、バックエンドAPIをモック化しています。
.envファイルのENV_MODE設定により、モックAPIと実際のバックエンドAPIを柔軟に切り替えることができます。
ENV_MODE='development'の場合はMSWモックAPIが起動し、ENV_MODE='production'の場合はAPI_URLで指定された実際のバックエンドAPIに接続します。

// app/entry.client.tsx
if (process.env.NODE_ENV === 'development') {
  const { worker } = await import('./mocks/browser');
  await worker.start({ onUnhandledRequest: 'warn' });
}

この仕組みにより、開発時はモック環境で独立した開発を行い、
統合テスト時は実際のバックエンドAPI(API_URL=http://backend:8000)、本番環境では本番API(API_URL=https://api.example.com)と、状況に応じて適切なAPIを使用できます。

モックAPIエンドポイント:

POST /api/v1/auth/login                      # ログイン
POST /api/v1/auth/logout                     # ログアウト
POST /api/v1/auth/me                         # ユーザー情報取得
POST /api/v1/auth/signup                     # ユーザー登録
POST /api/v1/auth/send-verify-email          # 認証メール送信
POST /api/v1/auth/send-password-reset-email  # パスワードリセットメール
POST /api/v1/auth/reset-password             # パスワードリセット
PATCH /api/v1/auth/me                        # ユーザー情報更新
DELETE /api/v1/auth/me                       # ユーザーアカウント削除

ESLint設定

プロジェクトでは以下のESLintルールを適用しています:

  • @typescript-eslint/no-unused-vars: 未使用変数のエラー検出
  • @typescript-eslint/no-explicit-any: any型の使用禁止
  • react-hooks/rules-of-hooks: React Hooksルールの強制
  • no-useless-catch: 不適切なcatch句の検出無効化

format設定

Prettierによるコードフォーマット統一を実現しています:

{
  "singleQuote": true,
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": true,
  "printWidth": 80
}
  • シングルクォート使用でJavaScript標準に準拠
  • 末尾カンマありで差分を最小化
  • インデント幅2でコンパクトな記述
  • セミコロンありで明確な文末表示
  • 行幅80文字で読みやすさを確保

typecheck設定

TypeScript型チェックとReact Router型生成を組み合わせて実行:

{
  "scripts": {
    "typecheck": "react-router typegen && tsc"
  }
}

実行プロセス:

  1. React Router型生成(.react-router/types/ディレクトリ作成)
  2. TypeScript型チェック実行(tsc)

strict mode有効でより厳密な型チェックを実現し、React Router v7の自動型生成機能により、ルーティング情報の型安全性も確保しています。

プロジェクトでは以下のTypeScript設定を適用:

  • strict: true(全ての厳格なチェックを有効化)
  • noUnusedLocals: 未使用変数の検出
  • noUnusedParameters: 未使用パラメータの検出
  • exactOptionalPropertyTypes: オプショナルプロパティの厳密チェック
  • moduleResolution: Bundler(Vite用最適化)
  • パスエイリアス: ~/*./app/*
  • 型定義: React Router, Vite, Node.js
  • 自動型生成: .react-router/types/ディレクトリ

CI

GitHub Actionsによる自動品質チェックを実現:

name: Frontend Code Quality (Linting & Type Check)
on:
  pull_request:
    branches: [ main, develop ]
  push:
    branches: [ main, develop ]

実行内容:

  1. ESLintチェック(npm run lint
  2. Prettierフォーマットチェック(npm run format:check
  3. TypeScript型チェック(npm run typecheck
  4. ビルドテスト(npm run build

Docker環境で実行することで、本番環境と同等の環境での品質チェックを実現し、プルリクエストとpushトリガーで自動実行されます。

単体テスト

公式ドキュメントのような単体テストやJestなどを実施することがベストですが、今回は単体テストは実装していません。

理由としては

  • フロントエンド側で複雑なロジックがない(→Jestなどのロジックに対するテスト省略)
  • Cypress(E2Eテスト)時に下記2つの環境に対するテストを実施予定のため
    • モックAPIで動くフロントエンドサーバー(→フロントエンドの結合テストとして代用)
    • バックエンドAPIで動くフロントエンドサーバー(→本来のE2Eテスト)

のためです。

最後に

FastAPIのバックエンド側に続いてフロントエンド側も記事にしましたが、フロントエンド側の方が苦手なため大変でした。
React Router V7のActionとLoaderの考え方は今回で初めて知りましたが、ActionとLoaderに対するベストな設計があまり検索してもでてこないので大変でした。
Reactについてはhookや状態をうまく使いこなした設計ができず、まだまだ勉強が必要だなと実感しました。

まだまだ改善点がありそうですが、フロントエンドを0から作成したため良い経験となりました。

Discussion