Next.js × Laravel SPAのチラつき完全解消!Auth HintとSWRで爆速UXを実現

システム開発

こんにちは、編集長Kです。

SPAで認証周りを作っていると、画面のチラつき、気になりませんか?

ページをリロードした瞬間や、言語を切り替えた時。 一瞬だけ「未認証」のヘッダーが出て、すぐに直るアレです。

せっかくのSPAなのに、少しカッコ悪いですよね。

Cookieを使ったセキュアな認証(Sanctumなど)では、よく起きる現象です。 フロント側から「ログイン中か?」がすぐに分からないのが原因です。

そこで今回は、「Auth Hint(認証ヒント)」という技術を使います。 超有名SNSサービスなどでも採用しているベストプラクティスです。

この記事で解説する手順の全体像は、以下の通りです。

  • 手順①:Auth Hint用の定数を準備する
  • 手順②:Axiosのインターセプターを改修する
  • 手順③:AuthContextをSWRで劇的進化させる

【前提条件】

  • フロントエンド:Next.js (App Router)
  • バックエンド:Laravel (SanctumによるCookie認証)
  • データフェッチ:SWR導入済み

それでは、さっそく実装していきましょう!

スポンサーリンク
OANDA証券 MT4

手順①:Auth Hint用の定数を準備する

まずは、マジックストリング(文字列の直書き)を防ぎます。 タイポによるバグをなくすための、現場の鉄則ですね。

定数管理用のファイルに、下記を追加してください。 (例:lib/constants.ts)

// lib/constants.ts
export const AUTH_HINT_KEY = 'auth_hint';

これでOKです。 今後キー名が変わっても、ここを直すだけで済みます。

手順②:Axiosのインターセプターを改修する

続いて、API通信の要であるAxiosを修正します。

セッション切れなどで、バックエンドから401エラーが返ってきたとします。 その時、フロントエンドの「ヒント」も確実に消す仕組みが必要です。

Axiosの設定ファイル(lib/axios.ts など)に、下記を追記してください。

import { AUTH_HINT_KEY } from './constants';
// レスポンスのエラーハンドリング部分
api.interceptors.response.use(
    (response) => response,
    (error) => {
        if (error.response) {
            const status = error.response.status;
// 認証エラー (401, 419) のハンドリング
            if (status === 401 || status === 419) {
                // サーバー側で認証切れの場合、ヒントも同期的に削除
                if (typeof window !== 'undefined') {
                    localStorage.removeItem(AUTH_HINT_KEY);
                }
                
                // (※既存のリダイレクト処理などが続きます)
            }
        }
        return Promise.reject(error);
    }
);

これで、サーバーとブラウザの認識ズレを防げます。 セキュリティ的にも安心ですね。

手順③:AuthContextをSWRで劇的進化させる

いよいよ本丸です。

useStateで管理していたユーザー情報を、SWRに置き換えます。 これにより、コンポーネントが再マウントされても一瞬で状態が復元されます。

まずは、AuthContext.tsxを開いて、下記のように修正してください。

"use client";
import useSWR from "swr";
import { AUTH_HINT_KEY } from "@/lib/constants";
// (その他のインポートは省略)
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
    // 1. localStorageからヒントを同期的に取得
    const getAuthHint = () => {
        if (typeof window !== 'undefined') {
            return localStorage.getItem(AUTH_HINT_KEY);
        }
        return null;
    };
    const authHint = getAuthHint();
    // 2. SWRを使ってユーザー情報をフェッチ
    // ヒントが無い場合は、無駄なAPI通信を一切行いません!
    const { data: user, error, mutate } = useSWR(
        authHint ? "/user" : null,
        async (url) => {
            const response = await api.get(url);
            localStorage.setItem(AUTH_HINT_KEY, 'true'); // ヒントを確実にする
            return response.data;
        }
    );
    // 3. ローディング状態の厳密な判定
    const loading = !!authHint && user === undefined && !error;
    // 4. ログイン・ログアウト処理
    const login = async (userData) => {
        localStorage.setItem(AUTH_HINT_KEY, 'true');
        await mutate(userData, false);
    };
const logout = async () => {
        localStorage.removeItem(AUTH_HINT_KEY);
        await mutate(undefined, false);
        // (ログアウトAPIの呼び出しとリダイレクト処理)
    };
return (
        <AuthContext.Provider value={{ user: user || null, loading, login, logout }}>
            {children}
        </AuthContext.Provider>
    );
};

これで完成です! ブラウザで確認してみてください。画面のチラつきは完全に消え去ったはずです。

Q
実装したのに、ヘッダーが未認証のまま表示される?
A

実装直後に、一番起きやすい現象です。 結論から言うと、コードのバグではありません。

あなたのブラウザに、古いセッション(Cookie)が残っているかも。 ヒント(localStorage)だけが空っぽ、という過渡期の状態です。

解決策:一度だけ、改めてログインし直してください。

再ログインすることで、Cookieとヒントが綺麗に同期します。 その後は、何度リロードしても滑らかな挙動になるはずです。

まとめ

お疲れ様でした!

「セキュリティの強さはそのままに、UXだけを最高水準に引き上げる。」 まさにシステムアーキテクトとしての腕の見せ所ですね。

無駄なAPI通信も減るので、サーバーのお財布にも優しいというわけです。

Trade Agencyでは、こうした現場のリアルな開発ノウハウをどんどん発信しています。 トレードシステムの裏側に興味がある方は、ぜひブックマークをお願いします!

それでは、また次回の記事でお会いしましょう。編集長Kでした!

この記事を書いた人
Trade Agency編集長K

■ 投資歴: 10年以上
■ スタイル: Fintokei(プロップファーム)× 株式投資
■ 職業: ITコンサルタント、アプリ開発

【詳細】
USD/JPYの裁量トレードでプロップファーム攻略に挑むエンジニアトレーダー。
Fintokeiで稼いだ利益を、堅実なバリュー・グロース株へ投資して資産を最大化する「資金循環スタイル」を実践中。

本業のWeb開発スキルを活かし、FXに役立つツール(Lot計算機など)を自作・無料公開しています。

Trade Agency編集長Kをフォローする
システム開発
スポンサーリンク
Trade Agency編集長Kをフォローする

コメント

タイトルとURLをコピーしました