Next.js アプリケーションにおける複雑な問題のデバッグと解決

September 6, 2025 Next.js デバッグ Hydrationエラー 国際化 ファビコン

Next.js アプリケーションにおける複雑な問題のデバッグと解決

導入

本記事では、Gemini CLI との対話を通じて、Next.js アプリケーションで発生した複数の技術的な問題を特定し、解決した経緯を詳細に解説します。特に、Next.js のレンダリングメカニズムや国際化の仕組みに関する深い理解が必要でした。Gemini CLI の支援を受けながら、一つずつ問題を掘り下げ、解決策を適用していきました。

問題1: Hydration エラーの解決

初期症状

アプリケーションの初期ロード時、特にトップページへの直接アクセス時に、以下の Hydration エラーがコンソールに表示されました。

Error: Hydration failed because the initial UI does not match what was rendered on the server.
Expected server HTML to contain a matching <div> in <a>.

このエラーは、ページ遷移を挟むと消えるという奇妙な挙動を示していました。

原因と試行錯誤

このエラーは、サーバーサイドでレンダリングされた HTML と、クライアントサイドでハイドレーションされる HTML が一致しない場合に発生します。特に <a> タグの中に <div> がネストされているという具体的なメッセージが手がかりとなりました。

  1. JobCard.tsxLink コンポーネント内のネスト問題:

    • 当初、src/components/JobCard.tsx 内の Link コンポーネントが <a> タグをレンダリングし、その子要素として <div> を直接持っていることが原因と考えられました。HTML のセマンティクスでは <a> タグの中にブロックレベル要素を直接ネストすることはできません。
    • legacyBehavior プロパティを使用し、Link の子要素として明示的に <a> タグを渡し、その <a> タグの中に <div> を配置する修正を試みました。
    • しかし、この修正は Unexpected token Link という構文エラーを引き起こし、手動での修正が必要となりました。最終的に、<a> タグの中に <a> タグがネストされているという別の問題が浮上しました。
  2. JobCard.tsx 内の <a> タグのネスト:

    • JobCard.tsx 内で、求人投稿者のメールアドレスをリンクとして表示する <a> タグが、外側の Link コンポーネントによってレンダリングされる <a> タグの中にネストされていました。
    • これを <span> タグに変更することで、<a> タグのネスト問題を解消しました。
  3. key={Date.now()} の使用:

    • src/app/[locale]/page.tsxJobListingClient コンポーネントに key={Date.now()} が設定されていました。Date.now() はサーバーとクライアントで異なる値を生成するため、Hydration エラーの原因となります。
    • この key プロパティを削除しました。
  4. Analytics.tsxdangerouslySetInnerHTML:

    • src/components/Analytics.tsx 内の Google Analytics のトラッキングコードで、dangerouslySetInnerHTML を使用し、window.location.pathname を直接埋め込んでいました。サーバーサイドでは window オブジェクトが存在しないため、サーバーとクライアントで生成される HTML が異なり、Hydration エラーの原因となっていました。
    • usePathname() フックを使用してパスを取得し、それを dangerouslySetInnerHTML 内に埋め込むように修正しました。
  5. Header.tsxi18n 初期化ロジックの重複:

    • src/components/Header.tsx 内で i18n インスタンスを createInstance() で再初期化していました。これは layout.tsx で既に行われている処理との重複であり、Hydration エラーの原因となる可能性がありました。
    • Header.tsx から i18n の初期化ロジックを削除し、useTranslation フックを使用するように変更しました。
  6. Header.tsxuseTranslation の二重インポート:

    • 上記の修正中に useTranslation が二重にインポートされるエラーが発生しました。これは単純なインポートの重複であり、修正しました。
  7. layout.tsx での I18nProvider の使用:

    • src/app/[locale]/layout.tsxreact-i18nextI18nProvider が使用されていませんでした。クライアントコンポーネントで useTranslation を使用する場合、I18nProvider がコンポーネントツリーの上位に存在する必要があります。
    • layout.tsxI18nProvider を使用するように修正しました。

最終的な解決

上記の複数の修正を適用した結果、開発環境での Hydration エラーは解消されました。ただし、Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded,data-gr-ext-installed のような警告は残る場合がありますが、これらは通常ブラウザ拡張機能によるものであり、アプリケーションの機能には影響しません。

問題2: ファビコンが404になる問題の解決

初期症状

アプリケーションにアクセスすると、コンソールに GET /favicon.ico 404 というエラーが表示され、ファビコンが正しく表示されませんでした。

原因と解決

Next.js の App Router では src/app/icon.png を配置することで自動的にファビコンとして認識されますが、ブラウザは依然として favicon.ico を要求することがあります。また、public ディレクトリに favicon.ico が存在しないことが原因でした。

  1. src/app/icon.png の削除: app ディレクトリのファビコン設定との競合を避けるため、src/app/icon.png を削除しました。
  2. public/favicon.ico の配置: プロジェクトルートにあった konnichiwork_favicon.pngpublic ディレクトリに移動し、favicon.ico 形式に変換して配置しました(この変換と配置はユーザーによる手動操作)。
  3. layout.tsx からのファビコンリンクの削除: public/favicon.ico が自動的に認識されるため、src/app/[locale]/layout.tsx に明示的に追加していたファビコンリンクを削除しました。

これらの修正により、ファビコンが正しく表示されるようになりました。

問題3: Post Job ページの画像アップロードボタンの多言語化

初期症状

Post job ページの画像アップロードボタンのテキスト(例: 「ファイルを選択していません」)が日本語のみで表示され、多言語化されていませんでした。

原因と解決

input type="file" のデフォルトの表示はブラウザに依存するため、直接多言語化することはできません。

  1. 「ファイルが選択されていません」表示の削除: 以前追加した、ファイルが選択されていない場合に表示されるメッセージを削除しました。
  2. カスタムファイル入力フィールドの実装:
    • 元の input type="file" を非表示にし、カスタムの labelspan 要素を組み合わせた UI を実装しました。
    • label には t('form_select_file', 'ファイルを選択') を、span には t('form_no_file_chosen', '選択されていません') または t('form_file_uploaded', 'ファイルがアップロード済み') を表示するようにしました。
  3. 翻訳キーの追加: form_select_file, form_no_file_chosen, form_file_uploaded という新しい翻訳キーを各言語の public/locales/{lang}/common.json ファイルに追加しました(これはユーザーによる手動操作)。

これにより、画像アップロードボタンのテキストが多言語化され、表示が改善されました。

問題4: 投稿した求人が翻訳されない問題

初期症状

求人詳細ページで、投稿された求人情報がユーザーが選択した言語に翻訳されずに表示されました。

原因と解決

この問題は、Google Cloud Translation API を使用した翻訳ロジックに関連しています。

  1. デバッグログの追加: src/app/api/translate/route.ts にデバッグログを追加し、APIキーの設定状況、API呼び出しの成功/失敗、およびレスポンスの内容を確認できるようにしました。
  2. 環境変数とAPIの確認: GOOGLE_TRANSLATE_API_KEY が正しく設定されているか、APIキーが有効であるか、Google Cloud Platform で Translation API が有効になっているか、ネットワーク接続に問題がないかなどを確認する必要がありました。

現状: デバッグログの追加は完了しましたが、この問題はまだ完全に解決していません。ユーザーによる GOOGLE_TRANSLATE_API_KEY の設定確認と、サーバーのコンソールログの確認が必要です。これらの情報に基づいて、さらなるデバッグと解決策の適用が必要となります。

結論

Gemini CLI との協業により、Next.js アプリケーションにおける Hydration エラー、国際化、ファビコン、そして求人翻訳といった多岐にわたる複雑な問題を特定し、解決することができました。特に Hydration エラーのデバッグは困難を極めましたが、各コンポーネントの役割と Next.js のレンダリングライフサイクルを深く理解することで、根本原因に迫ることができました。一部の問題はユーザーによる手動操作が必要でしたが、CLI との連携により効率的なデバッグと修正プロセスを実現できました。

← Previous Entry: Konnichiworkサイト改善レポート:翻訳機能の修正からアカウント削除機能の実装までNext Entry: 多言語対応の改善:翻訳漏れの修正
← Back to Blog List