React - Protected Router로 로그인 여부에 따른 페이지 접근 관리하기

손근영·2024년 12월 23일

React

목록 보기
10/10

문제 상황

팀 프로젝트 진행 도중, 특정 메뉴 (mypage 등)에 로그인한 사용자만 접근할 수 있도록 설정해야 했습니다.

로그인 인증은 JWT기반으로 관리하고 있었고, 페이지 이동은 React Router로 관리하던 도중, 인증되지 않은 사용자가 URL로 직접 접근 시 페이지가 노출되는 문제가 발생했습니다.

예를 들어, 로그인을 하지 않은 상태에서 마이페이지에 접근할 수 있었습니다.

마이페이지는 사용자의 프로필 사진이나 닉네임과 같은 개인화된 정보를 표시해야 하는데, 로그인되지 않은 경우 이러한 정보가 모두 비어 있어 페이지가 제대로 작동하지 않는 듯한 인상을 주었습니다. 이는 사용자 경험 측면에서 혼란을 야기하고, 서비스 신뢰도를 저하시킬 수 있는 상황이었습니다.

문제 화면

Pretected Router를 이용한 문제 해결

일반 Router의 한계와 Protected Router의 필요성

React Router는 기본적으로 페이지 접근 권한을 제한하지 않습니다. 인증 상태를 기반으로 페이지 접근을 제어할 수 있는 Protected Router가 필요했습니다.

Protected Router 개념 이해

Protected Router란?

Protected Router는 사용자의 인증 상태를 확인하여 권한이 없는 경우 특정 페이지에 접근하지 못하도록 제한하는 기능을 제공합니다. React Router와 연동되며, 주로 JWT와 같은 인증 방식을 활용합니다.

페이지 보호를 위한 기본 원리

  1. 사용자 인증 상태 확인
  2. 인증되지 않은 사용자는 로그인 페이지로 리디렉션
  3. 인증된 사용자는 요청한 페이지에 접근 허용

JWT 기반 인증 시스템과 연계하기

JWT를 이용한 로그인 상태 확인

JWT는 사용자 인증 정보를 포함한 JSON 형식의 토큰입니다. 클라이언트는 이 토큰을 서버에 요청을 보낼 때 사용하며, Protected Router는 이를 통해 인증 상태를 확인합니다.

JWT와 Protected Router의 통합 개념

  • JWTlocalStorage 또는 sessionStorage에 저장

  • Protected Router에서 토큰 유효성 검사

  • 유효하지 않은 경우 로그인 페이지로 리디렉션

JWT 에 대한 보다 자세한 설명은 JWT 관련 포스팅에서 확인하실 수 있습니다.

React에서 Protected Router 구현하기

1. useAuth 훅으로 인증 상태 가져오기

const { token, isLoading } = useAuth();
  • useAuth를 사용하여 사용자 인증 상태(token)와 로딩 상태(isLoading)를 가져옵니다.
  • token은 사용자가 인증된 상태인지 확인하는 데 사용됩니다.

2. 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로 설정해 중복 표시를 방지합니다.

3. 로딩 상태 처리

if (isLoading) {
  return null;
}
  • isLoading이 true인 동안 아무것도 렌더링하지 않습니다.
  • 인증 상태 확인이 완료될 때까지 화면 깜박임을 방지합니다.

4. 인증되지 않은 사용자의 접근 제한

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>
  }
/>
  1. 사용자가 보호된 경로(/mypage, /stamp 등)에 접근하면 ProtectedRoute가 실행됩니다.
  2. isLoading 상태에서 인증 확인이 진행됩니다.
  3. 인증이 완료되면:
    • 토큰이 없으면 경고 메시지가 표시되고 로그인 페이지로 리디렉션됩니다.
    • 토큰이 있으면 해당 경로의 자식 컴포넌트가 렌더링됩니다.

0개의 댓글