🔁
Next.js × Supabase認証でハマった無限ループ問題
はじめに
AIにコードを書かせて開発を加速するのはもはや日常ですが、そのまま鵜呑みにすると地獄を見ることもある──そんな体験談です。
今回は Next.js × Supabase 認証実装で無限ループに陥った問題を紹介しつつ、その仕組みと解決策を整理しました。
症状:無限ループにハマる
- ページが延々と「loading...」
- コンソールに同じエラーが連発
まさに 無限ループ地獄。
なぜ無限ループが起きたのか?
1. ReactのuseEffectの落とし穴
useEffectは依存配列の値が変化するたびに実行されます。依存配列を空にしても、副作用内で状態更新を繰り返すと再レンダリングが誘発され、結果としてループに陥ることがあります。
2. SupabaseのonAuthStateChange
Supabaseの認証状態はイベントで通知されます。
SIGNED_INSIGNED_OUTTOKEN_REFRESHED
AIが生成したコードは、TOKEN_REFRESHED のたびに checkUserRole() を呼び出し、状態更新が再レンダリングを誘発。結果として「イベント発火 → 状態更新 → 再レンダリング → イベント発火…」の無限ループになりました...
悪い例と良い例
悪い例(AI生成コード)
useEffect(() => {
const subscription = supabase.auth.onAuthStateChange(async (event, session) => {
if (event === 'TOKEN_REFRESHED') {
await checkUserRole(); // 毎回実行
}
});
}, []); // 依存配列が空
このままだと「TOKEN_REFRESHEDのたびに状態更新 → 再レンダリング → イベント発火…」で無限ループ。
良い例(修正版)
const [isChecking, setIsChecking] = useState(false);
useEffect(() => {
const subscription = supabase.auth.onAuthStateChange(async (event, session) => {
if (event === 'TOKEN_REFRESHED' && !isChecking) {
if (!user || !isAdmin) {
setIsChecking(true);
await checkUserRole();
setIsChecking(false);
}
}
});
return () => subscription.unsubscribe();
}, [user, isAdmin, isChecking]);
修正ポイント:
-
isCheckingフラグで多重実行を防止 - 必要なときだけ
checkUserRoleを実行 -
unsubscribeを返してクリーンアップ
まとめ
AIは便利ですが、「副作用の制御」「イベント駆動の挙動」といった文脈依存の強い領域では誤ったコードを提案することがあります。
個人的には、特に認証周りでかなり不安の残る実装が多い気がしています。
Discussion