프로젝트

개인정보-컴플라이언스-웹애플리케이션 (수정사항 1)

알럽유 2025. 1. 29. 16:54
728x90
반응형

현재 기관회원으로 로그인하고 세션을 발급 받는 시간동안에 새로고침을 하면 로그인창으로 리다이렉트이 이루어져 수정을 해야합니다.

 

이전 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(만료 시간) 설정 가능 → 자동으로 캐시 삭제됨