Firebase의 onAuthStateChanged 사용 시 초기 렌더링 문제

Daehyeon Yun·2024년 8월 7일
post-thumbnail

⭐️ Firebase의 onAuthStateChanged 사용 시 초기 렌더링 문제 발생과 해결 과정

Firebase의 onAuthStateChanged 를 사용하여 사용자의 인증 상태를 관리하여 특정 페이지에 인증된 사용자만 접근할 수 있도록 동작하는 코드를 작성하려고 하였다.

// protected-route.tsx
import { Navigate } from "react-router-dom";
import { auth } from "../firebase";
import { useEffect, useState } from "react";

export default function ProtectedRoute({children} : {children:React.ReactNode}){
    const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);

    useEffect(() => {
        // Firebase Auth 상태 변화를 감지
        const unsubscribe = auth.onAuthStateChanged(user => {
            setIsAuthenticated(!!user); // 사용자가 로그인 상태이면 true, 아니면 false
        });

        // 컴포넌트가 언마운트될 때 옵저버 해제
        return () => unsubscribe();
    }, []);

    if(!isAuthenticated){
        // 인증되지 않은 사용자가 접근하였을 경우 리다이렉션
        return <Navigate to="/login" replace />;
    }

    return children;
}
// App.tsx
...
import ProtectedRoute from "./components/protected-route";

const router = createBrowserRouter([
  {
    path: "/",
    // 부모 컴포넌트 지정
    element: (
      // ProtectedRoute -> 인증을 요구하는 React Hook
      <ProtectedRoute>
        <Layout/>
      </ProtectedRoute>
    ),
    // 라우트(자식) 컴포넌트 지정
    children: [
      {
        path: "",
        element: <Home/>,
      },
      {
        path: "profile",
        element: <Profile/>,
      }
    ]
  },
  // 로그인 페이지
  {
    path: "/login",
    element: <Login/>
  },
  // 회원가입 페이지
  {
    path: "/create-account",
    element: <CreateAccount/>
  }
])
...

이때, onAuthStateChanged 의 비동기 동작이 완료되기 전 렌더링이 진행되어버리는 현상이 발생하였다. 즉, 인증상태가 완료되기 전 리다이렉션이 수행되는 상황이다.

이를 해결하는 방안으로 또 하나의 boolean 상태 변수를 활용하여 인증처리가 완료되기 전까지는 로딩상태로 유지하도록 하였다.

  • isAuthenticated : onAuthStateChanged 의 상태 변화를 boolean 으로 확인
  • isCheckingAuth : onAuthStateChanged 의 수행 여부를 boolean 으로 확인

⭐️ 개선사항

  1. 초기 로딩 상태 처리 : isCheckingAuth 상태를 추가하여 FirebaseonAuthStateChanged 가 인증 상태를 확인하는 동안 로딩상태를 유지하도록 하였다.
  2. 인증 상태 확인 후 렌더링 : onAUthStateChanged 가 사용자 인증 상태를 감지한 뒤 렌더링을 수행하는 부분을 분리시켜 명확한 비동기 인증 절차를 확인할 수 있도록 하였다.
import { Navigate } from "react-router-dom";
import { auth } from "../firebase";
import { useEffect, useState } from "react";
import LoadingScreen from "./loading-screen";

export default function ProtectedRoute({children} : {children:React.ReactNode}){
    const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
    const [isCheckingAuth, setIsCheckingAuth] = useState(false);

    useEffect(() => {
        // Firebase Auth 상태 변화를 감지  
        const unsubscribe = auth.onAuthStateChanged(user => {
            setIsAuthenticated(!!user); // 사용자가 로그인 상태이면 true, 아니면 false
            setIsCheckingAuth(true); // true -> 사용자 인증 확인중, false -> 인증 확인 완료
        });

        // 컴포넌트가 언마운트될 때 옵저버 해제
        return () => unsubscribe();
    }, []);

    // 인증 확인 중일땐 로딩스크린 출력
    if(!isCheckingAuth){
        return <LoadingScreen/>
    }

    if(!isAuthenticated){
        // 인증되지 않은 사용자가 접근하였을 경우 리다이렉션
        return <Navigate to="/login" replace />;
    }

    return children;
}

onAuthStatedChanged 의 상태 변화가 발생하기도 전에 기존 상태 변수의 기본값으로 설정되어있던 null 상태가 먼저 실행되어버려 사용자에게 잘못된 정보가 제공된다는 점이 문제가 되었다.

🎉 해결!

profile
열심히 살아야지

0개의 댓글