😎

【1日1zenn - day18】prefetchってなんぞや。ヘルスチェックとは??

2025/01/15に公開

prefetchというワードがよく出てくるなぁと思いつつ、タイミングがきたら調べようと思ってたところ、prefetchが打ち手の候補になってるタスクが飛んできたのでざっくり調べます

やりたいこと

ある外部サイトで認証処理を行ったあとに、リダイレクトが行われてサービスを利用できる仕組みだが、その外部サイトがダウンしていた場合などはサービスを利用できなくなる。
そうならないように、当該サイトのヘルスチェックを行ってから、問題がない場合にのみ認証処理を走らせるようにしたい。
この方法の一つとしてprefetchが挙げられてました。

調べる

いつものように箇条書き。

MDN

https://developer.mozilla.org/ja/docs/Glossary/Prefetch

  • 代表的な使い方は、ヘルスチェックではなく事前にページを取得しておくことでそのページを早く表示するとかっぽい
  • ナビゲーションのために文書の先読みは投機ルールAPIを使う
  • 広く使うには<link rel="prefetch">
    • だけどぱっと見ドンピシャに見えない...

記事

「prefetch api call」とかで調べても、ページをプリロードする使い方ばかり出てくる。
一旦キーワード拾うためにAIと話そう。

AIとおしゃべり

問い

エンジニアになって初めて担当しているプロダクトが、ある外部サイトで認証処理を行ったあとに、リダイレクトが行われてサービスを利用できる仕組みなのですが、その外部サイトがダウンしていた場合などはサービスを利用できなくなる仕様になっています。
そうならないように、当該サイトのヘルスチェックを行ってから、問題がない場合にのみ認証処理を走らせるようにしたいと言われたのですが、どうすればいいですか?
フロントはReactとTypeaScriptで、バックエンドはKotlinとSpring Bootです。
prefetchするのがいいのでは?とか言われたのですがよくわかっておらず、他の可能性も含めて複数パターン提案してください。

答え

1. フロントエンドでの事前ヘルスチェック (prefetch)

  • 仕組み
    • 外部サイトに対して軽量なAPIリクエストを送信し、応答が正常かを確認します(HTTPステータスコードや特定のレスポンス内容をチェック)。
    • 正常であれば認証処理を続行し、異常であればエラーメッセージを表示してユーザーに案内します。
  • 実装概要
    • フロントエンド:
      • Reactで認証フローを実行する前にuseEffectやonClickイベント内でfetchを使って外部サイトのステータスを確認。
      • 外部サイトが利用不可の場合、適切なメッセージを表示。
const checkHealthAndAuthenticate = async () => {
  try {
    const response = await fetch("https://external-site.example.com/health");
    if (response.ok) {
      // 外部サイトが正常
      authenticateUser(); // 認証処理を実行
    } else {
      throw new Error("外部サイトが利用できません");
    }
  } catch (error) {
    alert("現在、外部サイトが利用できないため認証が行えません。");
  }
};

このパターンは/healthのAPIが必要で、今回は適していなそう。
useEffectで実行するとかにはなる気がするので契機の参考にはなった。

2. バックエンドでのヘルスチェック API を用意

  • 仕組み
    • バックエンドに「外部サイトのヘルスチェックを行うエンドポイント」を作成し、フロントエンドはそのAPIを通じてチェック結果を取得。
    • フロントエンドはバックエンドの結果に基づいて認証を続行するか判断。
  • 実装概要
    • バックエンド:
      • 外部サイトへの定期的なヘルスチェックを行い、キャッシュした結果をフロントエンドに返す。
      • Spring BootのRestTemplateやWebClientを使用して外部サイトにリクエストを送信。
@RestController
@RequestMapping("/api/health")
class HealthCheckController {

    @GetMapping
    fun checkExternalSiteHealth(): ResponseEntity<Boolean> {
        return try {
            val response = restTemplate.getForEntity("https://external-site.example.com/health", String::class.java)
            if (response.statusCode.is2xxSuccessful) {
                ResponseEntity.ok(true)
            } else {
                ResponseEntity.ok(false)
            }
        } catch (ex: Exception) {
            ResponseEntity.ok(false)
        }
    }
}
  • フロントエンド
    • フロントからバックエンドのヘルスチェックAPIを呼び出し、その結果を利用。
const checkHealthAndAuthenticate = async () => {
  try {
    const response = await fetch("/api/health");
    const isHealthy = await response.json();
    if (isHealthy) {
      authenticateUser(); // 認証処理を実行
    } else {
      throw new Error("外部サイトが利用できません");
    }
  } catch (error) {
    alert("現在、外部サイトが利用できないため認証が行えません。");
  }
};

この方向性になる気がするが、どのみち/healthを実装されていない外部サイトだと難しそう。
というよりフロントはいらなくて、バックエンドの実行前に何か軽量なAPIを投げるとかでもいいのかも?
ResponseEntity.ok(true)とかにするとステータスコード200でbodyが{"body":true}となるレスポンスを返せるらしい、とかは参考になる。

問い

prefetchを組み合わせるとどうなる?

答え

  • バックエンドでのヘルスチェック API 自体にはprefetchは直接関係しませんが、フロントエンドで事前に結果を取得・キャッシュしておくことで、操作性を向上できます。
  • 特に、React QueryやSWRなどのデータフェッチライブラリを使う場合に、prefetchを組み合わせると、認証処理の待機時間を削減できます。
  • ユーザー体験を向上させたい場合は、バックエンドのヘルスチェック API に加えて、フロントエンドでprefetchの仕組みを導入するのがおすすめです。
    要は認証処理に入る前にAPIでヘルスチェックしてキャッシュに詰めておけば、認証処理を行う時にAPIを叩くのは大丈夫になりそう。

方向性

今回使う外部サイトで、リダイレクトされない+ダミーデータとかで動くような適当なAPIをprefetchしておき、認証に入ったタイミングでキャッシュにレスポンスOKが入ってたら実行するような感じにするのが良さそう?
実際にそのサイトにそういうAPIがあるかは調べる。

おわり

もうちょい調べようと思ったけど一旦軽めにこんな感じで。
とりあえず今後会話にprefetchが登場した時は「あれのことね」と理解できそうなのでOKということで。

Discussion