SEN PRODUCT BLOG

千株式会社のエンジニアによるブログ

#内定者レポート SPAの実装、Inertiaで解決できること・できないこと

こんにちは!12月03日の@iwadoooo_77さんの記事にてご紹介いただいた25卒内定者の@riku_ftです!

今回は個人開発で採用したLaravel + Inertia + React + TailwindCSSという構成について、実装を通じて発見した利点と課題を話したいと思います。

きっかけ:個人開発でSPAの複雑さに悩んでいた

個人で開発し、公開、運営までしたいWebアプリケーションでの開発段階での話です。

そのアプリではユーザー体験を考慮してSPAの実装を考えていました。

 

ですが、「いざ作ってみよう!」と意気込んで実装を始めたものの、調べれば調べるほど考慮すべき要素が増えていきました:

  • REST APIの設計と実装
  • OpenAPI(Swagger)でのAPI定義
  • ドキュメント管理
  • フロントエンド側でのAPI Client実装
  • 状態管理ライブラリの選定と実装(Redux/Zustandなど)
  • JWT/Sanctumなどでの認証管理
  • CSRFトークンの管理

特に開発を進めていく中で気になったのが、フロントエンドとバックエンドの分離による影響です:

  • 型定義の二重管理
  • API仕様の同期
  • 開発環境の2重管理
  • 変更が発生した際の両側への反映

個人開発とはいえ、これら全ての管理を一人で行うのは大変そうだな...と悩んでいたときに出会ったのがInertiaです。



そもそもInertiaとは

Inertiaは、従来のモノリシックアプリケーションとSPAの中間的なアプローチを提供するライブラリです。

主な特徴として:

  • サーバーサイドのルーティングをそのまま活用できる
  • SPAのようなスムーズなページ遷移とインタラクションを実現
  • フロントエンドとバックエンドの通信を自動化
  • データの受け渡しがシームレス

などがあります。

 

実装してみて分かったInertiaの特徴

まず印象的だったのが、普段使っているLaravelの開発体験がほぼそのまま活かせることで学習コストが比較的低い点です。

従来のLaravelアプリケーションでの実装では

// routes/web.php
Route::get('/users', [UserController::class, 'index']);

// UserController.php
public function index()
{
    return view('users.index', [
        'users' => User::paginate(10)
    ]);
}

Inertiaでの実装では

// routes/web.php (変更なし!)
Route::get('/users', [UserController::class, 'index']);

// UserController.php
public function index()
{
    return Inertia::render('Users/Index', [
        'users' => User::paginate(10)
    ]);
}

ここではInertiaが自動的にデータをJSONとしてフロントエンドに受け渡し、Reactコンポーネントでレンダリングしてくれています。

そのおかげで

  • APIエンドポイントの定義が不要
  • 型の整合性が自動的に保証
  • 変更箇所が一箇所で完結

などの恩恵を感じました。

メリット:フロントエンド実装の簡素化

従来のReactでの実装と比較すると

// 従来のReact実装(React Query/SWRを使用する場合)
function UserList() {
  const { data: users, isLoading, error } = useQuery(
    'users',
    async () => {
      const response = await axios.get('/api/users');
      return response.data;
    },
    {
      retry: 3,
      staleTime: 60000,
      onError: (error) => {
        console.error('Failed to fetch users:', error);
        Notification.error({
          message: 'Failed to load users'
        });
      }
    }
  );

  if (isLoading) return <LoadingSpinner />;
  if (error) return <ErrorMessage error={error} />;

  return <UserTable users={users} />;
}

これがInertiaでは

// Inertiaでの実装
const UserList = ({ users }) => {
  return <UserTable users={users} />;
}

実装が大幅に簡素化することができています。

データの取得処理も、ローディング状態の管理も、エラーハンドリングも全て不要になりました。

最初は「これで本当に大丈夫なのか」と不安になるほどでしたが、実際の動作は非常に安定していて驚きです。

ページ遷移とユーザー体験の向上

createInertiaApp({
  progress: {
    color: '#29d',
    showSpinner: true,
  },
});

これだけで全ページにプログレスバーが実装され、ユーザー体験の向上が図れます。

従来のSPAだと、React RouterとReact Query/SWRなどを組み合わせて実装する必要があり悩んでいた点です。

SEO対策とSSRサポート

SPAを選んだ時点で悩ましかったのがSEO対策でした。

一般的なSPAだと:

  • Nuxt.js/Next.jsの導入
  • プリレンダリングの設定
  • メタタグの動的な管理
  • クローラー対策

など、追加で考慮する必要がありますが、InertiaではSSR機能を使用して、この課題にも対応できました。



ただし、SSR機能の導入には以下のような準備が必要で最初は苦戦しました...。

1. 環境構築

  • Nodejsサーバーのセットアップ
  • 必要なパッケージのインストール(@inertiajs/server等)
  • 本番環境での実行環境の準備

2. サーバー設定の最適化

// server.js
const express = require('express')
const createServer = require('@inertiajs/server')
const app = express()

createServer((page) => 
  import(`./resources/js/Pages/${page.component}.jsx`)
).then(({ default: handler }) => {
  app.use(handler)
  app.listen(13714)
})

3. エラー時のフォールバック処理

// HandleInertiaRequests.php
try {
    $response = Http::get('http://localhost:13714/...');
    // SSRの処理
} catch (\Exception $e) {
    // SSRに失敗した場合のフォールバック
    return $this->fallbackToClientSide();
}

 

 

注意が必要な点

1. データ構造への配慮

Propsとして渡せるのはシリアライズ可能なデータのみで、循環参照を含むデータ構造は要注意!
大量データの場合はページネーションなどの適切な設計が必須!

2. SSRのセットアップ

本番環境を見据えた際に検討が必要な点:

  • @inertiajs/serverパッケージの設定
  • Nodejs環境のセットアップと管理
  • プロダクション環境での運用方針

3. 特定ケースでのパフォーマンス考慮

  • 初期ロードでのJavaScriptバンドルサイズ
  • 大量データ処理時のメモリ使用
  • リアルタイム通信実装時の考慮点

開発を進める中で見えてきた課題

1. 大規模データ処理への対応

a) ページネーション
  • Laravelのページネーション機能との連携
  • 無限スクロールの実装方法
  • キャッシュ戦略の検討
b) パフォーマンス最適化
  • データの事前ロード(Eager Loading)の活用
  • 部分的な更新の実装
  • クエリの最適化

2. リアルタイム機能の実装

a) Laravel Echo + Pusherの活用
  • WebSocket接続の確立
  • イベントの発行と購読
  • 状態の同期管理
b) ポーリングによる実装
  • 定期的なデータ更新
  • 差分更新の実装
  • バックグラウンド処理との連携

3. テスト環境の構築

a) E2Eテスト
  • Cypressを使用したテスト実装
  • テストシナリオの設計
  • CI/CDパイプラインへの組み込み
b) コンポーネントテスト
  • Jest + React Testing Libraryの活用
  • テストカバレッジの設定
  • スナップショットテストの活用
c) 統合テスト
  • LaravelのテストツールとInertiaの連携
  • APIレスポンスのモック
  • テストデータの管理

 

まとめ

良かった点

  • シンプルな実装で高度な機能が実現できる
  • フロントエンドとバックエンドの連携が容易
  • 学習コストを抑えながらモダンな開発ができる

注意が必要な点

  • 大規模なデータ処理が必要な場合
  • リアルタイム性が重要な機能
  • マイクロサービスアーキテクチャとの統合

まだまだ改善の余地はありますが、個人開発での経験を通じて、Inertiaの可能性を実感できて楽しかったです。

今後はこの経験を活かしていければと思います。

今回の内容はこちらの記事のアプリにも今後実装していく予定なのでよかったらぜひご確認ください!!

 

productblog.sencorp.co.jp