개인정보-컴플라이언스-웹애플리케이션 (수정사항 1)
현재 기관회원으로 로그인하고 세션을 발급 받는 시간동안에 새로고침을 하면 로그인창으로 리다이렉트이 이루어져 수정을 해야합니다.
이전 App.jsx
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import Layout from "./components/Layout/Layout";
import { useRecoilState } from "recoil";
import { useEffect } from "react";
import axios from "axios";
import { authState } from "./state/authState";
import Login from "./components/Login/Login";
import Signup from "./pages/Login/Signup";
import SystemManagement from "./pages/manager/SystemManagement";
import MainPage from "./pages/mainpage";
import SelfTestStart from "./pages/SelfTest/SelfTestStart";
import DiagnosisPage from "./pages/SelfTest/DiagnosisPage";
import QualitativeSurvey from "./pages/SelfTest/QualitativeSurvey";
import SignupComplete from "./components/Login/SignupComplete";
import Dashboard from "./pages/SelfTest/Dashboard";
import CompletionPage from "./pages/SelfTest/CompletionPage";
import SystemRegistration from "./components/System/SystemRegistration";
import SuperUserPage from "./pages/superuser/SuperUserPage";
import DiagnosisfeedbackPage from "./pages/feedback/DiagnosisfeedbackPage";
import QualitativeSurveyfeedback from "./pages/feedback/QualitativeSurveyfeedback";
function App() {
const [auth, setAuthState] = useRecoilState(authState);
useEffect(() => {
const fetchUserData = async () => {
try {
// 1. 기관회원 데이터 먼저 확인
const userResponse = await axios.get("http://localhost:3000/user", {
withCredentials: true,
});
if (userResponse.data.user) {
const { id, member_type, ...userData } = userResponse.data.user;
setAuthState({
isLoggedIn: true,
isExpertLoggedIn: false,
user: { id, member_type, ...userData },
});
return; // 여기서 끝내고 전문가 체크 X
}
} catch (error) {
console.warn("기관회원 정보 없음, 전문가 체크 진행");
}
// 2. 기관회원이 없으면 전문가회원 확인
try {
const expertResponse = await axios.get("http://localhost:3000/expert", {
withCredentials: true,
});
if (expertResponse.data.expert) {
const { id, member_type, ...userData } = expertResponse.data.expert;
setAuthState({
isLoggedIn: true,
isExpertLoggedIn: true,
user: { id, member_type, ...userData },
});
return;
}
} catch (error) {
console.warn("전문가회원 정보 없음, 로그아웃 처리");
}
// 3. 두 경우 다 아니면 로그아웃 상태로 설정
setAuthState({
isLoggedIn: false,
isExpertLoggedIn: false,
user: null,
});
};
fetchUserData();
}, [setAuthState]);
return (
<BrowserRouter>
<Layout isExpertLoggedIn={auth.isExpertLoggedIn}>
<Routes>
<Route
path="/"
element={
auth.isLoggedIn ? (
auth.isExpertLoggedIn ? (
<Navigate to="/system-management" replace />
) : (
<Navigate to="/dashboard" replace />
)
) : (
<MainPage isExpertLoggedIn={auth.isExpertLoggedIn} />
)
}
/>
<Route path="/SelfTestStart" element={<SelfTestStart />} />
<Route path="/DiagnosisPage" element={<DiagnosisPage />} />
<Route path="/qualitative-survey" element={<QualitativeSurvey />} />
<Route path="/Login" element={<Login />} />
<Route path="/Signup" element={<Signup />} />
<Route path="/signup-complete" element={<SignupComplete />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/system-register" element={<SystemRegistration />} />
<Route path="/completion" element={<CompletionPage />} />
<Route path="/system-management" element={<SystemManagement />} />
<Route path="/superuserpage" element={<SuperUserPage />} />
<Route
path="/DiagnosisfeedbackPage"
element={<DiagnosisfeedbackPage />}
/>
<Route
path="/QualitativeSurveyfeedback"
element={<QualitativeSurveyfeedback />}
/>
</Routes>
</Layout>
</BrowserRouter>
);
}
export default App;
새로고침 시 세션 데이터가 아직 로드되지 않아서 auth.isLoggedIn이 false로 인식되어 로그인 페이지로 이동하는 문제가 발생할 수 있습니다.
🚨 문제 발생 과정
기관회원 로그인 → authState가 isLoggedIn: true로 설정됩니다.
새로고침 (F5 또는 window.location.reload()):
App 컴포넌트가 다시 마운트됩니다.
useEffect에서 fetchUserData()가 실행되지만, 서버 응답이 느릴 경우 authState가 기본값 (false)로 설정됩니다.
isLoggedIn === false로 처리되어 로그인 페이지로 리다이렉션됩니다.
서버 응답이 완료된 후에야 isLoggedIn: true로 업데이트되지만, 이미 로그인 페이지로 이동한 상태라 의미 가 없습니다.
🚀 해결 방법
1. loading 상태 추가하여 데이터 로딩 전 화면 이동 방지
현재 isLoggedIn이 false가 되면서 로그인 페이지로 리디렉션되는데,
이걸 방지하기 위해 loading 상태를 추가하여 세션 확인이 끝나기 전까지 화면 이동을 막을 수 있습니다.
수정된 App.jsx
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import Layout from "./components/Layout/Layout";
import { useRecoilState } from "recoil";
import { useEffect, useState } from "react";
import axios from "axios";
import { authState } from "./state/authState";
import Login from "./components/Login/Login";
import Signup from "./pages/Login/Signup";
import SystemManagement from "./pages/manager/SystemManagement";
import MainPage from "./pages/MainPage";
import SelfTestStart from "./pages/SelfTest/SelfTestStart";
import DiagnosisPage from "./pages/SelfTest/DiagnosisPage";
import QualitativeSurvey from "./pages/SelfTest/QualitativeSurvey";
import SignupComplete from "./components/Login/SignupComplete";
import Dashboard from "./pages/SelfTest/Dashboard";
import CompletionPage from "./pages/SelfTest/CompletionPage";
import SystemRegistration from "./components/System/SystemRegistration";
import SuperUserPage from "./pages/superuser/SuperUserPage";
import DiagnosisfeedbackPage from "./pages/feedback/DiagnosisfeedbackPage";
import QualitativeSurveyfeedback from "./pages/feedback/QualitativeSurveyfeedback";
function App() {
const [auth, setAuthState] = useRecoilState(authState);
const [loading, setLoading] = useState(true); // ✅ useState import 추가
useEffect(() => {
const fetchUserData = async () => {
try {
// 1. 기관회원 확인
const userResponse = await axios.get("http://localhost:3000/user", {
withCredentials: true,
});
if (userResponse.data.user) {
const { id, member_type, ...userData } = userResponse.data.user;
setAuthState({
isLoggedIn: true,
isExpertLoggedIn: false,
user: { id, member_type, ...userData },
});
setLoading(false); // ✅ 데이터 로딩 완료
return;
}
} catch (error) {
console.warn("기관회원 정보 없음, 전문가 체크 진행");
}
// 2. 전문가 확인
try {
const expertResponse = await axios.get("http://localhost:3000/expert", {
withCredentials: true,
});
if (expertResponse.data.expert) {
const { id, member_type, ...userData } = expertResponse.data.expert;
setAuthState({
isLoggedIn: true,
isExpertLoggedIn: true,
user: { id, member_type, ...userData },
});
setLoading(false);
return;
}
} catch (error) {
console.warn("전문가회원 정보 없음, 로그아웃 처리");
}
// 3. 로그아웃 상태
setAuthState({
isLoggedIn: false,
isExpertLoggedIn: false,
user: null,
});
setLoading(false); // ✅ 데이터 로딩 완료
};
fetchUserData();
}, [setAuthState]);
return (
<BrowserRouter>
{loading ? ( // ✅ 로딩이 끝나기 전까지 "세션 확인 중..." 표시
<div className="h-screen flex items-center justify-center">
<p>세션 확인 중...</p>
</div>
) : (
<Layout isExpertLoggedIn={auth.isExpertLoggedIn}>
<Routes>
<Route
path="/"
element={
auth.isLoggedIn ? (
auth.isExpertLoggedIn ? (
<Navigate to="/system-management" replace />
) : (
<Navigate to="/dashboard" replace />
)
) : (
<MainPage isExpertLoggedIn={auth.isExpertLoggedIn} />
)
}
/>
<Route path="/SelfTestStart" element={<SelfTestStart />} />
<Route path="/DiagnosisPage" element={<DiagnosisPage />} />
<Route path="/qualitative-survey" element={<QualitativeSurvey />} />
<Route path="/Login" element={<Login />} />
<Route path="/Signup" element={<Signup />} />
<Route path="/signup-complete" element={<SignupComplete />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/system-register" element={<SystemRegistration />} />
<Route path="/completion" element={<CompletionPage />} />
<Route path="/system-management" element={<SystemManagement />} />
<Route path="/superuserpage" element={<SuperUserPage />} />
<Route
path="/DiagnosisfeedbackPage"
element={<DiagnosisfeedbackPage />}
/>
<Route
path="/QualitativeSurveyfeedback"
element={<QualitativeSurveyfeedback />}
/>
</Routes>
</Layout>
)}
</BrowserRouter>
);
}
export default App;
🔍 결과
✅ 세션 확인이 완료될 때까지 로그인 화면으로 이동하지 않음.
✅ 새로고침해도 로그인 상태 유지 가능.
✅ 사용자가 페이지를 보기도 전에 리디렉션되는 문제 해결. 🚀
백엔드에서 세션 체크 속도 개선
💡 프론트에서 loading을 추가하는 것도 중요하지만, 백엔드 응답 속도를 빠르게 하는 것도 도움이 됩니다.
/user, /expert 라우트의 쿼리 속도를 개선하기.
Redis 캐시를 활용해 로그인 정보를 저장하면 세션 확인 속도를 더욱 빠르게 할 수 있음.
아직 redis캐시를 사용해서 해본적이 없기 때문에 추후에 개선해 나가보겠습니다.
🔴 Redis란?
Redis(레디스)는 빠른 데이터 저장소입니다.
일반적인 데이터베이스(MySQL, PostgreSQL)보다 훨씬 빠르게 데이터 저장 및 조회가 가능합니다.
메모리(RAM)에 데이터를 저장하여 매우 빠릅니다.
NoSQL 데이터베이스 (키-값 저장 방식) 입니다.
캐시(Cache) 기능을 제공하여 데이터베이스 부담을 줄여줍니다.
만료 시간(TTL)을 설정할 수 있어 일정 시간이 지나면 자동 삭제가 가능합니다.
🚀 Redis를 사용하면?
✅ DB 부하 감소 → 성능 향상
✅ API 속도 개선 (Redis에서 즉시 응답)
✅ 로그인 세션 관리 가능 (JWT 없이도 인증 유지)
✅ TTL(만료 시간) 설정 가능 → 자동으로 캐시 삭제됨