🎯

Reactを使用したSPA構成の構築

に公開

🚀 Reactを使用したSPA構成の構築

📋 TL;DR(要約)

本記事で得られるもの:

  • ✅ React 19 + TypeScriptによる最新SPA開発手法
  • ✅ 効率的な開発環境構築(Vite、ESLint、Prettier)
  • ✅ 実践的なアーキテクチャパターン(Atomic Design、Zustand)
  • ✅ 保守性と拡張性を重視した設計思想

対象読者: Reactの基本を理解し、実践的なSPA開発スキルを身に付けたいエンジニア


🎯 課題(背景・問題提起)

フロントエンド開発の複雑化

モダンなWebアプリケーション開発では、以下の課題に直面することが多い

  • 技術選定の迷い 📊

    • 無数のライブラリから最適な組み合わせを選ぶ困難さ
    • 設定の複雑さによる開発開始の遅延
  • 保守性の問題 🔧

    • 不適切なディレクトリ構造による可読性の低下
    • コンポーネントの責務分離不足
    • 状態管理の複雑化
  • 開発効率の低下

    • ビルド時間の長期化
    • テスト環境の構築困難
    • チーム間での開発標準の不統一

💡 解決策(アプローチ)

1️⃣ 最適化された技術スタックの選定

高速開発環境

  • Vite: webpack比較で起動時間を大幅短縮
  • TypeScript: 型安全性による実行時エラーの削減

効率的な状態管理

  • Zustand: Redux比較でボイラープレート70%削減
  • React Query: サーバー状態管理の自動化

品質保証

  • Vitest: Vite統合による高速テスト実行
  • ESLint + Prettier: 自動コード品質管理

2️⃣ Atomic Designによる体系的コンポーネント設計

components/
├── atoms/      ← 最小単位(Button、Input)
├── molecules/  ← 小さな組み合わせ(SearchBar)
├── organisms/  ← 複雑な構造(UserList)
└── templates/  ← レイアウト構造

3️⃣ 責務を明確にした構造設計

src/
├── api/        ← API通信ロジック分離
├── store/      ← 状態管理の集約
├── domains/    ← ビジネスロジック型定義
├── hooks/      ← カスタムロジックの再利用
└── utils/      ← 汎用関数

🛠️ コード例

プロジェクト初期化

# 高速開発環境の構築
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app

# 必要パッケージの一括インストール
npm install zustand react-router-dom react-hook-form
npm install -D vitest jsdom @testing-library/react tailwindcss

状態管理(Zustand)

// src/store/useCounterStore.ts
import { create } from 'zustand';

interface CounterState {
  count: number;
  increment: () => void;
  decrement: () => void;
}

export const useCounterStore = create<CounterState>(set => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
  decrement: () => set(state => ({ count: state.count - 1 })),
}));

Atomicデザイン実装例

// 🔸 Atom: 最小単位のボタンコンポーネント
export function Button({ variant = 'primary', children, ...rest }: ButtonProps) {
  return (
    <button
      className={clsx(
        'px-4 py-2 rounded font-medium transition-colors',
        {
          'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
          'bg-gray-200 text-gray-800 hover:bg-gray-300': variant === 'secondary',
        }
      )}
      {...rest}
    >
      {children}
    </button>
  )
}

// 🔸 Molecule: Atomsを組み合わせた検索バー
export function SearchBar({ onSearch }: SearchBarProps) {
  const [searchTerm, setSearchTerm] = useState('')

  return (
    <form onSubmit={(e) => { e.preventDefault(); onSearch(searchTerm); }}>
      <Input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      <Button type="submit">検索</Button>
    </form>
  )
}

型安全なフォーム管理

// React Hook Formによる効率的なフォーム処理
export function TodoInputForm({ onAddTodo }: TodoInputFormProps) {
  const { register, handleSubmit, reset, formState: { errors } } = useForm<TodoFormValues>();

  const onSubmit = (data: TodoFormValues) => {
    onAddTodo(data.todoText);
    reset();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('todoText', { required: 'タスクを入力してください' })}
        placeholder="新しいタスクを入力..."
      />
      {errors.todoText && <span className="text-red-500">{errors.todoText.message}</span>}
      <button type="submit">追加</button>
    </form>
  );
}

🔍 詳細解説

Viteの優位性とパフォーマンス影響

開発体験の劇的な改善

  • HMR速度: 変更反映が瞬時(ファイル監視の最適化)
  • バンドルサイズ: Tree shakingによる不要コードの自動除去
// vite.config.ts
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: { '@': path.resolve(__dirname, './src') },
  },
  server: {
    port: 3000,
    open: true,
  },
});

Zustandによる状態管理の最適化

Reduxとの比較優位点

  • コード量: 約70%削減(boilerplateの排除)
  • 学習コスト: シンプルなAPI設計
  • パフォーマンス: 不要な再レンダリングの抑制
// 🔥 永続化とMiddleware活用
export const useAuthStore = create<AuthState>()(
  persist(
    set => ({
      user: null,
      token: null,
      login: async (credentials) => {
        const { user, token } = await authAPI.login(credentials);
        set({ user, token });
      },
    }),
    { name: 'auth-storage' } // localStorage連携
  )
);

Atomic Designの実践的メリット

開発効率向上の具体例

  • 再利用性: Buttonコンポーネント
  • テスト性: 小さな単位でのテスト実装により、カバレッジ向上
  • チーム開発: 責務の明確化により並行開発が容易

テスト駆動開発の実装

// コンポーネントテストの実例
describe('SearchBar', () => {
  it('should call onSearch with correct value', () => {
    const mockOnSearch = vi.fn();
    render(<SearchBar onSearch={mockOnSearch} />);
    
    fireEvent.change(screen.getByPlaceholderText('検索...'), { 
      target: { value: 'test' } 
    });
    fireEvent.submit(screen.getByRole('button'));
    
    expect(mockOnSearch).toHaveBeenCalledWith('test');
  });
});

📈 パフォーマンス最適化のポイント

バンドルサイズの最適化

  • Code Splitting: React.lazyによる遅延ローディング
  • Tree Shaking: 使用していないコードの自動除去
  • Dynamic Import: 必要時のみモジュール読み込み

レンダリング最適化

  • React.memo: 不要な再レンダリング防止
  • useMemo/useCallback: 重い計算処理のメモ化
  • Zustand: 必要な部分のみの状態更新

🎉 まとめ

✨ 達成できること

開発効率の飛躍的向上

  • ⚡ Viteによる高速開発環境
  • 🧩 Atomic Designによる体系的コンポーネント管理
  • 🔄 Zustandによる直感的状態管理

高品質なコードベース

  • 🛡️ TypeScriptによる型安全性
  • 🧪 Vitestによる効率的テスト環境
  • 📏 ESLint + Prettierによる一貫したコード品質

長期的な保守性

  • 📁 明確な責務分離によるディレクトリ構造
  • 🔧 React Hook Formによる堅牢なフォーム管理
  • 📊 ドメイン駆動設計による拡張性

この構成は、以下のようなユースケースに最適です:
• プロジェクトを素早く立ち上げたい
• チーム開発でも迷わずスケールさせたい
• なるべくシンプルなスタックで実運用したい

📝 この構成は、実際に次の案件で使う予定だった構成でしたが、ボツになったので記事として公開しました。


参考資料

Discussion