팀 프로젝트 진행 도중, 특정 메뉴 (mypage 등)에 로그인한 사용자만 접근할 수 있도록 설정해야 했습니다.
로그인 인증은 JWT기반으로 관리하고 있었고, 페이지 이동은 React Router로 관리하던 도중, 인증되지 않은 사용자가 URL로 직접 접근 시 페이지가 노출되는 문제가 발생했습니다.
예를 들어, 로그인을 하지 않은 상태에서 마이페이지에 접근할 수 있었습니다.
마이페이지는 사용자의 프로필 사진이나 닉네임과 같은 개인화된 정보를 표시해야 하는데, 로그인되지 않은 경우 이러한 정보가 모두 비어 있어 페이지가 제대로 작동하지 않는 듯한 인상을 주었습니다. 이는 사용자 경험 측면에서 혼란을 야기하고, 서비스 신뢰도를 저하시킬 수 있는 상황이었습니다.
문제 화면

React Router는 기본적으로 페이지 접근 권한을 제한하지 않습니다. 인증 상태를 기반으로 페이지 접근을 제어할 수 있는 Protected Router가 필요했습니다.
Protected Router는 사용자의 인증 상태를 확인하여 권한이 없는 경우 특정 페이지에 접근하지 못하도록 제한하는 기능을 제공합니다. React Router와 연동되며, 주로 JWT와 같은 인증 방식을 활용합니다.
JWT는 사용자 인증 정보를 포함한 JSON 형식의 토큰입니다. 클라이언트는 이 토큰을 서버에 요청을 보낼 때 사용하며, Protected Router는 이를 통해 인증 상태를 확인합니다.
JWT를 localStorage 또는 sessionStorage에 저장
Protected Router에서 토큰 유효성 검사
유효하지 않은 경우 로그인 페이지로 리디렉션
JWT 에 대한 보다 자세한 설명은 JWT 관련 포스팅에서 확인하실 수 있습니다.
useAuth 훅으로 인증 상태 가져오기const { token, isLoading } = useAuth();
useEffect로 경고 메시지 표시useEffect(() => {
const handleAlert = async () => {
if (!isLoading && !token && !hasAlerted && alertMessage) {
await ShowAlert("info", "", alertMessage);
setHasAlerted(true);
}
};
handleAlert();
}, [isLoading, token, hasAlerted, alertMessage]);
isLoading이 false이고, token이 없으며, hasAlerted가 false이고, alertMessage가 존재할 때 경고 메시지를 표시합니다.
경고 메시지를 표시한 뒤 hasAlerted를 true로 설정해 중복 표시를 방지합니다.
if (isLoading) {
return null;
}
if (!token && hasAlerted) {
return <Navigate to="/login" replace />;
}
Navigate를 사용하여 로그인 페이지로 리디렉션합니다.replace 속성을 사용해 브라우저 기록에 해당 리디렉션을 추가하지 않습니다.import React, { useState, useEffect } from "react";
import { Navigate } from "react-router-dom";
import { useAuth } from "../provider/AuthProvider";
import { ShowAlert } from "../utils/AlertUtils.js";
const ProtectedRoute = ({ children, alertMessage }) => {
const { token, isLoading } = useAuth();
const [hasAlerted, setHasAlerted] = useState(false);
useEffect(() => {
const handleAlert = async () => {
if (!isLoading && !token && !hasAlerted && alertMessage) {
await ShowAlert("info", "", alertMessage); // SweetAlert2 알림 표시
setHasAlerted(true);
}
};
handleAlert(); // 비동기 함수 호출
}, [isLoading, token, hasAlerted, alertMessage]);
// 로딩 중 상태 처리
if (isLoading) {
return null; // 로딩 상태일 때 아무 것도 렌더링하지 않음
}
// 인증이 되지 않았을 경우 경고 후 리디렉션
if (!token && hasAlerted) {
return <Navigate to="/login" replace />;
}
// 인증된 경우 자식 컴포넌트 렌더링
return children;
};
export default ProtectedRoute;
<Route
path="/mypage"
element={
<ProtectedRoute alertMessage="로그인 후 이용 가능합니다.">
<MyPage />
</ProtectedRoute>
}
/>
/mypage, /stamp 등)에 접근하면 ProtectedRoute가 실행됩니다.isLoading 상태에서 인증 확인이 진행됩니다.