Just Do IT!

[React] 도메인 이동 막기 (Protected Route) 본문

개발 공부/React

[React] 도메인 이동 막기 (Protected Route)

MOON달 2024. 11. 8. 17:22
728x90
반응형

Protected Route 란?

해당 페이지로 이동하기 전에 조건을 명시해주고, 이 조건을 만족했을 때 해당 페이지로 이동가능하고,

조건을 만족 못한다면 특정 페이지로 돌아가게 할 수 있다.

 

react-router-dom을 설치한 후 route를 사용할 때 적용할 수 있다.

 

이번 프로젝트에서는,

로그인 여부에 따라 접근 불가능한 곳이 있기 때문에 Protected Route를 이용해서 해주었다.

 

 

 

 

 

react-router-dom 설치

npm install react-router-dom

 

우선은 당연히 react-router-dom을 설치해야 route를 사용할 수 있으니까 설치한다.

나는 기존에 이미 route 설정을 해주었기 때문에 생략한다.

 

 

 

 

 

 

 

 

ProtectedRouter 설정

const ProtectedRoute = ({ element, isMemberOnly = false }) => {
  const { isAuthenticated } = useAuth();
  const { openModal, Modal, closeModal, isModalOpen } = useModal(); // useModal 사용
  const [redirect, setRedirect] = useState(false); // 리디렉션 상태 추가

  // 인증되지 않은 경우 로그인 안내 모달을 띄우기
  useEffect(() => {
    if (!isAuthenticated) {
      openModal(); // 인증되지 않으면 모달 띄우기
    }
  }, [isAuthenticated, openModal]);

  // 리디렉션 후, 스크롤을 복원
  useEffect(() => {
    if (redirect) {
      document.body.style.overflow = "unset";
    }
  }, [redirect]);

  const handleCloseModal = () => {
    closeModal();
    setRedirect(true);
  };

  // 리디렉션 상태가 true로 변경되면, 메인 페이지로 리디렉션
  if (redirect) {
    return <Navigate to="/" replace />;
  }

  return (
    <>
      {element}
      {/* 로그인 안내 모달 */}
      {isModalOpen && (
        <Modal>
          <div className="text-center p-4">
            <h2 className="text-xl font-semibold">로그인해야 합니다</h2>
            <p className="mt-2">로그인이 필요합니다. 로그인 후 이용해주세요.</p>
            <button
              onClick={handleCloseModal}
              className="mt-4 px-6 py-2 bg-blue-500 text-white rounded-md"
            >
              닫기
            </button>
          </div>
        </Modal>
      )}
    </>
  );
};

export default ProtectedRoute;

 

이게 내가 설정한 protect 설정이다.

 

 

인증 여부 확인

 const { isAuthenticated } = useAuth();

 

useAuth라는 context를 통해서 로그인했는지 여부를 확인한다.

이건 각자의 프로젝트에 따라 다를 거라고 예상하고...우선은 내가 진행하는 프로젝트에서는

useContext를 적용해서 인증 여부를 확인했다.

 

 

로그인 모달 띄우기

  // 인증되지 않은 경우 로그인 안내 모달을 띄우기
  useEffect(() => {
    if (!isAuthenticated) {
      openModal(); // 인증되지 않으면 모달 띄우기
    }
  }, [isAuthenticated, openModal]);

 

만약 인증되지 않은 경우에는,

로그인 해야 한다는 모달이 나오도록 조건문을 추가해주었다.

 

그리고 여기서 내가 오랫동안 고민했던 부분이 redirect 부분이다.

redirect를 usestate로 상태관리를 해주는데, true/false로 여부를 확인한다.

 

내가 모달을 만들 때, 모달이 열리면 스크롤이 없어지도록 코드를 구현했다.

그리고 모달이 닫히면 다시 스크롤이 생기도록 했는데, redirect 여부가 없었을 때는 아무리 모달을 닫아도 새로고침 하지 않는 이상 스크롤이 다시 생기지 않는 것이다.

또, navigate가 먹히지 않았다. 

비회원이 접근 불가능한 페이지를 클릭하면 모달이 나오고, 모달 닫기를 클릭하면 다시 메인페이지로 돌아오도록 의도한건데 의도대로 되지 않는 것이다.

 

그래서 redirect 상태 관리를 추가하고,

모달에서 닫기 버튼을 누르면 모달이 닫히면서 메인 페이지로 돌아가게끔 만들어주었다.

 

  // 리디렉션 상태가 true로 변경되면, 메인 페이지로 리디렉션
  if (redirect) {
    return <Navigate to="/" replace />;
  }

 

그래서 redirect 상태가 true인 경우에 메인페이지로 redirect 되도록 해주었다.

모달이 닫히면서 redirect가 true가 되고, 메인 페이지로 이동하는 것이다.

 

 

 

 

 

 

 

Route 수정

 {/* 비회원도 접근 가능 */}
 <Route path="/" element={<Main />} />
<Route path="/search" element={<MainSearch />} />
<Route path="/oauth/:provider" element={<OAuthVertification />} />
<Route path="/book" element={<BookMain />} />
<Route path="/book/:id" element={<BookPage />} />
<Route path="/book/:id/questions/:questionId"  element={<QuestionPage />} />
<Route path="/study" element={<StudyMain />} />
<Route path="/mypage/:userId" element={<MyPage />} />

{/* 회원만 접근 가능*/}
<Route path="/book/create" element={<ProtectedRoute element={<CreateBook />} isMemberOnly />} />

 

페이지가 너무 많아서 일부만 들고 왔다.

위의 그냥 Route가 비회원도 접근 가능한 path이고

아래 문제집 생성 페이지는 회원만 접근 가능한 path이다.

 

위의 ProtectedRoute를 보면,

{element}로 받기 때문에

element={<ProtectedRoute element={<CreateBook />} isMemberOnly

 

ProtectedRoute 컴포넌트를 사용하여 인증 여부를 체크하고, 인증된 사용자만 해당 페이지에 접근할 수 있도록 한다.

또,

isMemberOnly prop을 ProtectedRoute에 전달하여 이 경로가 인증된 사용자만 접근할 수 있음을 명시한다.

 

이렇게 회원만 접근 가능한 페이지들을 PRotectedRoute로 감싸주면 정상적으로 작동된다.

 

 

이런식으로,

로그인을 하지 않고서 회원만 접근 가능한 페이지로 이동했을 때,

로그인 요청 Modal이 나오도록 추가해주었다.

 

 

 

 

 

 

 

 

 

 


 

사실 이 기능을 추가한지 오래되었는데 내일 작성할 에러 기록과 비슷할것 같아서 블로그로 정리해본다.

프로젝트를 하면서 react 공부도 덤으로 열심히 하게 되는 듯 하다...

 

728x90