
사용자를 관리하는 프로젝트에서는 의도하지 않은 페이지 접속을 차단해야 할 경우가 있다.
예를 들어,
비로그인 유저는 프로필, 기록 데이터 등 유저 데이터가 필요한 페이지에 접근할 수 없다.로그인 유저는 로그인, 회원가입 페이지에 다시 접속할 수 없다.이를 막기 위해서 아래와 같은 코드를 각 페이지마다 작성할 수도 있다.
useEffect(() => {
if (user) {
navigate("/record");
} else {
navigate("/");
}
}, [user, navigate]);
하지만 매번 각 페이지마다 이런 코드를 작성하면 중복 코드가 많아지기 때문에 더 효율적인 방법이 필요하다.
보통 App.tsx 파일에 아래와 같이 라우터를 설정한다.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignupPage />} />
<Route path="/record" element={<RecordsPage />} />
<Route path="*" element={<ErrorPage />} />
</Routes>
</BrowserRouter>;
이렇게 설정해주면 각 페이지에서 Link 또는 useNavigate를 사용하여 다른 페이지 이동할 수 있다.
이럴 때, 특정 페이지에 접근을 제한하고 싶다면 privateRoute 컴포넌트를 사용하면된다.
const PrivateRoute = ({ children }) => {
const { user } = useAuth();
return user ? children : <Navigate to="/login" />;
};
라우터에 적용하는 방법은 다음과 같다.
<Route
path="/record"
element={
<PrivateRoute>
<RecordsPage />
</PrivateRoute>
}
/>
이렇게 설정해두면, 로그인하지 않은 사용자가 RecordsPage에 직접 접근하려 할 경우 자동으로 /login 페이지로 리다이렉트된다. 반대로 로그인한 사용자는 정상적으로 페이지 접근이 가능하다.
반대로 로그인한 사용자가 login 페이지나 signup 페이지 접근을 제한하기 위해서 publicRoute를 구현했다.
const PublicRoute = ({ children }) => {
const { user } = useAuth();
return user ? <Navigate to="/" /> : children;
};
이를 App.tsx에 적용하면 다음과 같은 구조가 된다.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<PublicRoute><LoginPage /></PublicRoute>}/>
<Route path="/signup" element={<PublicRoute><SignupPage /></PublicRoute>}/>
<Route path="/record" element={<PrivateRoute><RecordsPage /></PrivateRoute>}/>
<Route path="/my-records" element={<PrivateRoute><MyRecordsPage /></PrivateRoute>}/>
<Route path="*" element={<ErrorPage />} />
</Routes>
</BrowserRouter>;
Firebase를 사용하여 데이터를 관리하면서 사용자 인증을 비동기적으로 처리하게 되었다.
이 과정에서 privateRoute와 PublicRoute를 사용하여 페이지별 접근 제한를 설정했다.
하지만 특정 상황에서 무한 리다이렉트 문제가 발생했다.
문제 발생 시 브라우저에서는 다음과 같은 경고 메시지를 출력했다.
Throttling navigation to prevent the browser from hanging. See https://crbug.com/1038223. Command line switch --disable-ipc-flooding-protection can be used to bypass the protection
이 경고는 지나치게 많은 리다이렉트로 인해 브라우저가 강제로 중지하는 상황을 의미한다.
상황을 확인하니
로그인한 유저가 PrivateRoute로 접근이 제한된 페이지에 진입할 때 문제가 발생했다.
인증 정보가 비동기적으로 로드되면서, 처음에는 user 데이터가 null 값을 가진다. 이후 user 데이터가 정상적으로 로딩되지만, PrivateRoute는 초기 상태에서 user가 없는 것으로 판단해 사용자를 로그인 페이지로 리다이렉트했다.
그 결과, 로그인 페이지에서도 사용자가 이미 인증된 상태로 감지되면서, 다시 원래 페이지로 이동하려는 무한 리다이렉트가 발생하게 되었다.
이 문제는 loading 상태를 추가하여 인증 정보가 완전히 로드되기 전까지 리다이렉트를 방지하는 방식으로 해결하였다.
const PrivateRoute = ({ children }) => {
const { user, loading } = useAuth();
if (loading) {
return null;
}
return user ? children : <Navigate to="/login" />;
};