글모음

[ Error ] Error: [PrivateRoute] is not a <Route> component.All component children of <Routes> must be a <Route> or <React.Fragment> 본문

프로그래밍 공부/기타

[ Error ] Error: [PrivateRoute] is not a <Route> component.All component children of <Routes> must be a <Route> or <React.Fragment>

Nova_61 2023. 3. 20. 11:03
728x90
반응형

 

PrivateRouter

 

[ 에러 ]

[Error] Error: [PrivateRoute] is not a <Route> component.
All component children of <Routes> must be a <Route> or <React.Fragment>

 

-> '<Routes> 내부에 있는 모든 컴포넌트들은 <Route> 또는 <React.Fragment> 여야 한다.' 

 

<PrivateRoute> 컴포넌트를 <Route>로 변경하라는 소리

 

ReactRouter V5와 V6에서 PrivateRoute 설정 방법이 달라져서 생기는 에러.

PrivateRoute 컴포넌트와 Route 설정을 새 방법으로 다시 해줘야한다.


< V5(이전) 버전 PrivateRouter >

React Router의 현재 버전에서는 App.js에서 Route 설정부터, PrivateRoute 컴포넌트 생성부터 다 달라졌다.

 

1. PrivateRoute 컴포넌트 생성

인증된 사용자만 액세스 할 수 있는 개인 경로를 만드는 데 사용되는 PrivateRoute 컴포넌트를 만들어줘야 한다.

// src/components/PrivateRoute.tsx
import React, { useContext } from "react";
import { Route, Navigate } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";

interface PrivateRouteProps {
  path: string;
  element: React.ReactElement;
}

const PrivateRoute: React.FC<PrivateRouteProps> = ({ path, element }) => {
  const { state : AuthState } = useContext(AuthContext);

  return AuthState.isLoggedIn ? (
    <Route path={path} element={element} />
  ) : (
    <Navigate to="/login" />
  );
};

export default PrivateRoute;


PrivateRoute 컴포넌트는 path와 element 두 개의 props를 받는다.

1. path : 이동하고 싶은 페이지 경로

2. element는 해당 경로에서 렌더링 할 React 요소

useContext를 사용하여 AuthContext로부터 현재 인증 상태를 가져와서 AuthState.isLoggedIn을 통해 사용자가 로그인되어 있는지 확인하고, 사용자가 로그인되어 있다면 Route 요소를 렌더링 하고, 그렇지 않으면 사용자를 로그인 페이지로 리디렉션 한다.

 

2. PrivateRoute 컴포넌트 라우트 설정

// src/App.tsx

import React, { useContext, useReducer } from "react";
import { Route, Routes } from "react-router-dom";

// 상태관리를 위한 AuthContext
import { AuthContext, authReducer, initialState } from "./context/AuthContext";

/* page components들 생략 */

import PrivateRoute from "./utils/PrivateRoute";

export function App() {
  const [authState, dispatch] = useReducer(authReducer, initialState);

  return (
    <AuthContext.Provider value={{ state: authState, dispatch }}>      
     <Routes>
        <Route path="/" element={<Landing />} />
        <Route path="/login" element={<Login />} />
        <Route path="/signup" element={<SignUp />} />
        <Route path="*" element={<NotFound />} />
        
        // PrivateRoute로 설정해준다
        <PrivateRoute path="/dashboard:id" element={<Dashboard />} />
        <PrivateRoute path="/profile:id" element={<Profile />} />
        
      </Routes>
    </AuthContext.Provider>
  );
}

export default App;

인증이 필요한 페이지에 Route 대신 PrivateRoute 설정을 해주면 된다.

이게 V5에서의 PrivateRoute 설정이다.


< 변경된 현재 버전 PrivateRouter >

 

1. PrivateRoutes 컴포넌트 생성

React Router V6에서는 <Outlet>이라는 새로운 컴포넌트를 사용하는데, 

<Outlet> 컴포넌트는 부모 <Route> 요소 내에서 자식 요소를 렌더링 하는 데 사용된다.

// src/context/AuthContext.tsx

import React, { useContext } from "react";
import { Outlet, Navigate } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";

const PrivateRoutes: React.FC = () => {
  const { state: authState } = useContext(AuthContext);

// 반드시 <Outlet />을 써야한다.
  return authState.isLoggedIn ? <Outlet /> : <Navigate to="/login" />;
};

export default PrivateRoutes;

<PrivateRoutes> 컴포넌트 내부에서는  authState의 isLoggedIn으로 인증 여부를 확인하고, 인증되지 않은 사용자의 경우에는 <Navigate> 컴포넌트를 사용해 원하는 페이지로 리다이렉트 해주는 로직을 구현하면 된다. 

나 같은 경우에는 인증되지 않은 사용자인 경우에는 login 페이지로 리다이렉트 하도록 만들었다.

 

2. PrivateRoutes 컴포넌트 라우트 설정

 

기존의 <Route> 요소에 기능을 추가하는 대신, <Route element> props로 <PrivateRoutes>를 구성해 주고, 하위 component 안에는 PrivateRoute를 적용을 해야 할 Route끼리 묶어서 PrivateRoutes 컴포넌트 안에 모아줘야 한다.

// src/App.tsx

import React, { useContext, useReducer } from "react";
import { Route, Routes } from "react-router-dom";

// 상태관리를 위한 AuthContext
import { AuthContext, authReducer, initialState } from "./context/AuthContext";

/* page components들 생략 */

import PrivateRoutes from "./utils/PrivateRoutes";

export function App() {
  const [authState, dispatch] = useReducer(authReducer, initialState);

  return (
    <AuthContext.Provider value={{ state: authState, dispatch }}>
      <Routes>
        <Route path="/" element={<Landing />} />
        <Route path="/login" element={<Login />} />
        <Route path="/signup" element={<SignUp />} />
        <Route path="*" element={<NotFound />} />

        // PrivateRoutes를 적용해야할 Route 끼리 모아줘야 한다.
        <Route element={<PrivateRoutes />}>
          <Route path="/dashboard:id" element={<Dashboard />} />
          <Route path="/profile:id" element={<Profile />} />
        </Route>
        
      </Routes>
    </AuthContext.Provider>
  );
}

export default App;

 

이전버전(V5)에서는 하나하나 'PrivateRoute' 설정해 준 것과 다르게 현재 버전(V6)에서는 'PrivateRoutes' component안에 접근을 막고 싶은 Route들을 넣는다. 사소하지만 V5에서는 개별적이라 'PrivateRoute'라고 하고, V6에서는 'PrivateRoutes'라고 하는 거에 주의하자

 

 

 

728x90
반응형
Comments