SPAルーティング問題のトラブルシューティング - 失敗から学んだ最適解

AWS
CloudFront
GitHub Actions
SPA
ルーティング
トラブルシューティング
CDN
CI/CD

8/10の記事で言及した解決策1の失敗理由を詳しく解説。GitHub Actionsでのファイル処理から始まり、最終的にCloudFront FunctionsによるURLリライトに至ったトラブルシューティングの全過程を時系列で整理します

はじめに

Web開発では、些細な問題が大きな発見につながることがあります。今回は、SPAルーティング問題の解決に向けて試行錯誤した過程を振り返り、失敗から学んだことを整理してみます。

前回の記事で触れた「解決策1」がなぜ失敗したのか、その理由と最終的な解決策に至るまでのトラブルシューティングの全過程を時系列で整理します。

トラブルシューティングの開始:HTML拡張子の削除

最初に試した解決策

Next.jsでSSGした静的サイトをS3でホスティングする際のCSR後リロード時404問題の解決という記事において、Next.jsのnext exportで生成されるportfolio.htmlabout.htmlなどのファイルを、URLで.htmlなしでアクセスできるよう、拡張子なしのファイルに変換する手法が提案されていました。

この手法では追加のインフラリソースを必要とせず、GitHub Actionsのワークフロー内での処理を修正するだけで解決できそうだったので、CloudFront Functionsより先に一番最初に試してみました。

実装したアプローチ

GitHub Actionsのワークフロー内で、HTMLファイルの拡張子を削除する処理を実装しました:

# Process HTML files for SPA routing
- name: Process HTML files for SPA routing
  working-directory: frontend
  run: |
    # Remove unnecessary files
    find ./out -name ".DS_Store" -delete
    find ./out -name ".keep" -delete

    # Store original HTML file paths before renaming
    html_files=$(find ./out -name "*.html" ! -path "*/index.html" ! -path "*/_next/*" ! -path "*/node_modules/*" ! -path "*/\.*")

    if [ -n "$html_files" ]; then
      # Store the list for later use in Content-Type setting
      echo "$html_files" > /tmp/html_files_list.txt

      # HTML file extension removal
      for file in $html_files; do
        new_name="${file%.html}"
        mv "$file" "$new_name"
      done
    else
      touch /tmp/html_files_list.txt
    fi

また、拡張子なしのファイルに変換した後、S3にアップロードする際にContent-Typeをtext/htmlに設定する処理も含めていました。これにより、拡張子なしのファイルでもブラウザがHTMLとして正しく認識できるようになります。

予想外の問題発生:ファイル名とディレクトリ名の衝突

問題の発見

HTML拡張子の削除を試みた際に、予想外の問題が発生しました。それは拡張子なしのportfolioとlifeのファイルが作成されず、それぞれのディレクトリ配下にhtmlファイルが作成されるという問題でした。

問題の詳細

理想的な構成:

./out/
├── portfolio/         # 既存のディレクトリ
├── life/             # 既存のディレクトリ
├── portfolio         # 拡張子なしのファイル
├── about             # 拡張子なしのファイル
├── life              # 拡張子なしのファイル
└── contact           # 拡張子なしのファイル

実際の構成:

./out/
├── portfolio/
│   ├── 2024-0629.md
│   └── portfolio.html # ディレクトリ配下に作成されてしまう
├── life/
│   ├── 2025-0720.md
│   └── life.html      # ディレクトリ配下に作成されてしまう
├── about.html
└── contact.html

なぜこうなるのか

  • portfoliolifeという名前のディレクトリが既に存在
  • GitHub Actionsで利用されているUbuntuOSでは、同じ階層に同じ名前のファイルとディレクトリを共存できない
  • ワークフロー内の処理で既存ディレクトリ配下に格納されてしまう

最終的な解決策:CloudFront Functionsによる根本的解決

ここまでくると、HTMLファイルの拡張子を削除する手法での解決は難しそうです。というわけで、CloudFront Functionsによって解決することになりました。

詳細な実装方法については、以前の記事で詳しく解説しています。

失敗から学んだこと:GitHub Actionsの最適化

シンプル化されたワークフロー

CloudFront Functionsによる解決策が確立されたことで、GitHub Actionsの役割が明確になり、HTML拡張子の削除という複雑な処理が不要になりました。

現在のワークフロー:

# シンプル化されたファイル処理
- name: Process HTML files for SPA routing
  working-directory: frontend
  run: |
    echo "Processing HTML files for SPA routing..."

    # Remove unnecessary files only
    find ./out -name ".DS_Store" -delete
    find ./out -name ".keep" -delete

    echo "SPA routing optimization applied"

改善されたポイント

  1. HTML拡張子削除の削除: 複雑なファイル処理ロジックを削除
  2. Content-Type設定の削除: S3でのContent-Type設定処理も不要に
  3. シンプルな構造: 必要最小限の処理のみ実行
  4. 保守性の向上: ファイル構造に依存しない設計
  5. エラーリスクの削減: ファイル名とディレクトリ名の衝突による失敗が発生しない

トラブルシューティングから学んだこと

1. 技術選定の重要性

問題の性質に応じて、適切な技術を選択することが重要です。今回の場合は、GitHub ActionsでのHTML拡張子の削除よりもCDNレベルでのURLリライトが適していました。

2. 解決手法の使い分け

GitHub ActionsでのHTML拡張子の削除による解決は、追加のインフラリソースを必要としない利点がありますが、ファイルシステムの制約により実現が困難でした。一方、CloudFront Functionsによる解決は、インフラレベルでの柔軟性を提供し、より確実にSPAルーティングを実現できます。状況に応じて適切な手法を選択することが重要です。

3. CI/CDでの適切な役割分担

CI/CDパイプラインでは、複雑なファイル処理よりも、シンプルで確実な処理に集中することが重要です。

まとめ

GitHub Actionsのワークフロー内でのHTML拡張子削除から始まり、ファイル名とディレクトリ名の衝突という根本的な問題に気づき、最終的にCloudFront FunctionsによるURLリライトに至った今回のトラブルシューティングは、Web開発における問題解決の典型的なパターンを示しています。

失敗から学んだことで、より適切な技術選定とCI/CDの役割分担ができ、最終的にはCloudFront Functionsによる解決により、ユーザーはどのページからでも安全にページを更新できるようになり、サイトの使いやすさが大幅に向上しました。


関連記事:

参考資料: