
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 으로 확인isCheckingAuth 상태를 추가하여 Firebase의 onAuthStateChanged 가 인증 상태를 확인하는 동안 로딩상태를 유지하도록 하였다.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 상태가 먼저 실행되어버려 사용자에게 잘못된 정보가 제공된다는 점이 문제가 되었다.
🎉 해결!