0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🔐 Supabase + Next.js 認証がハングした原因とその修正:StepEasy開発ログ

0
Posted at

こんにちは、習慣化タスク管理アプリ「StepEasy」を開発している @naonao96 です。
本記事では、Supabaseの認証処理でログイン・ログアウトが完全にハングする問題
に直面した際の原因分析と修正内容をまとめました。

👇 該当プロダクトはこちら
🔗 StepEasy on Solomaker


❗ 問題の概要

StepEasyの認証機能で、以下の深刻な問題が発生:

  • ログインしても画面が遷移しない
  • ログアウト後もセッションが切れず、状態がフリーズ
  • ローカルストレージ削除やPC再起動でしか直らない

🧠 原因分析

1. 認証フローの競合状態

await supabase.auth.signInWithPassword() // → 95msで成功
router.push('/menu') // → 即座に画面遷移
// ↓
// 認証状態変更で onAuthStateChange 発火
// setUserFromSession() が重いDB処理を同期で呼び出す

→ 結果:画面遷移時にユーザー情報が中途半端な状態になり、フロント全体がハング


2. 非同期データ取得の同期化

await ensureUserExists() // DBにユーザーがなければ作成
await fetchUserFromDatabase() // 最新のユーザー情報取得

この2つを同期で連続実行していたため、認証フロー全体がブロックされていました。


3. 無限ループの待機処理

await new Promise<void>((resolve) => {
  const checkAuthState = () => {
    if (user && !isLoading) resolve();
    else setTimeout(checkAuthState, 100); // ← 無限ループ
  };
  checkAuthState();
});

→ 状態が更新されない限り永遠にsetTimeoutを回し続ける。


🛠 修正内容

✅ 修正1:認証フローの簡素化

Before:

const signInWithEmail = async (email: string, password: string) => {
  const { error } = await supabase.auth.signInWithPassword({ email, password });
  if (error) throw error;
  await waitForAuthState(); // 無限ループ
  router.push('/menu');
};

After:

const signInWithEmail = async (email: string, password: string) => {
  const { data, error } = await supabase.auth.signInWithPassword({ email, password });
  if (error) throw error;
  router.push('/menu'); // 成功即遷移
};

✅ 修正2:セッション情報を即座に設定 → DB処理はバックグラウンドに

const setUserFromSession = useCallback(async (session) => {
  const sessionUser = {
    id: session.user.id,
    email: session.user.email ?? '',
    displayName: session.user.user_metadata?.display_name ?? '',
    planType: 'free',
  };

  setUser(sessionUser); // 即設定(UIに即反映)

  // バックグラウンド処理
  ensureUserExists(...).catch(console.warn);
  fetchUserFromDatabase(...).then(setUser).catch(console.warn);
}, []);

→ UIの初期応答性が劇的に向上


✅ 修正3:ログアウト処理の簡略化&安定化

const signOut = async () => {
  try {
    await supabase.auth.signOut();
  } catch (error) {
    console.error('SignOut error:', error);
  }
  setUser(null);
  router.push('/lp'); // ログインページへ
};

✅ 修正による効果

項目 Before After
ログイン体験 ハング・フリーズ 即応答&遷移
ログアウト処理 タイムアウト多発 安定してセッション切断
ユーザー情報取得 同期処理でブロック 非同期で安全更新
UI体験 ストレス大 スムーズな動作

📘 技術的学びまとめ

🔧 認証フローの設計原則

  • ユーザー操作には即座に応答
  • 重い処理は非同期&バックグラウンドで実行
  • 段階的に情報を更新&上書き

💡 Supabase運用Tips

  • onAuthStateChange()を活用しすぎない
  • セッション情報を信頼してまず表示する
  • ユーザーDB同期処理は「あとでやる」

🧠 React/Next.jsのBest Practice

  • useCallback()で依存最小に
  • async/awaitUIブロックしないよう注意
  • 状態の「初期値」と「更新後」を明確に分ける

📝 今後の改善予定

  • 認証ログのエラーレポート自動通知(Sentry等)
  • ローディング状態のUI改善(スケルトン表示など)
  • Supabase側のレート制限/リトライ制御の検討

✨ 最後に

この修正によって、StepEasyの認証機能は大幅に安定し、ユーザー体験が改善されました。
同様にSupabaseを使っている方にとって、この記事が**「落とし穴回避マニュアル」**になると嬉しいです。

ご質問・フィードバックなど大歓迎です🙌
Twitter: @naonao96_dev


🔗 関連リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?