글 작성자: bbangson
반응형

개요

 3편 작성하겠습니다. 

 

 1편은 전체적인 프로젝트 소개와 저의 역할을 중점으로 작성한 회고.

https://bbangson.tistory.com/83

 

[기업 과제] Jaranda 팀 프로젝트 리뷰 ①

개요 2021.08.02 (월) ~ 2021.08.06(금)  원티드 프리온보딩 프론트 엔드 코스 교육에서 진행한 Jaranda 기업의 기업 과제 입니다. 총 8명에서 약 5일 동안 진행한 프로젝트입니다. 큰 규모의 프로젝트는

bbangson.tistory.com

 2편과 3편은 팀원이 구현한 기능에 대한 공부와 설명.

https://bbangson.tistory.com/85

 

[기업 과제] Jaranda 팀 프로젝트 리뷰 ②

개요 1편에 이어 2편 작성하겠습니다. 1편은 전체적인 프로젝트 소개와 저의 역할을 중점으로 작성한 회고. https://bbangson.tistory.com/83 [기업 과제] Jaranda 팀 프로젝트 리뷰 ① 개요 2021.08.02 (월) ~ 20..

bbangson.tistory.com

4편은 팀 프로젝트 셋업 내용과 리팩토링 및 최종 회고.

 

이렇게 구성되어있습니다. 

 

 

 3편은 팀원들이 구현한 기능 중에서 제가 참여하지 못했던 기능들을 몇 가지 뽑아 공부하고 설명하는 포스팅입니다. 

2편에 이어 팀원의 코드를 분석하는 시간이 될 것 같습니다. 글이 길어지는 점 양해 바라겠습니다. 

 

 다음 순서로 설명하겠습니다. 

 

1. 관리자 페이지 - 검색 기능, 페이지 네이션. 유저 메뉴아이템 변경 기능

2. 메뉴바 - 관리자 페이지에서 계정별 메뉴 설정 시 해당 계정에게 선택된 메뉴만 보이는 기능

3. 접근 제한 - 권한별 접근 가능한 페이지 이동

 

 

 


관리자 페이지

관리자 페이지는 다음과 같이 구현되어있습니다. (이름은 개인 정보상 가렸습니다.)

관리자 페이지

 

 

 

□ 검색 기능 

 

  관리자 페이지의 주 기능중 하나인 검색 기능입니다. 

검색 기능

 위 사진 처럼 "user10"을 검색하였더니, 그에 해당하는 정보를 잘 렌더링 하는 모습입니다. 

 

 이 기능은 어떻게 구현됐을까요?

검색 기능의 핵심

 

  검색 기능에 핵심이 되는 부분이라고 생각합니다. 검색 입력폼에 ID 혹은 이름이 입력되면 onChange 이벤트로 구현된 onHandleSearch 함수로 이벤트가 넘어갑니다. 이 함수에서 입력값에 해당하는 useState인 searchValue에 값을 입력시켜줍니다. searchValue가 업데이트되면 useEffect가 발동하고 미리 import 해두었던 getUserInfo 함수를 통해서 검색된 값에 해당하는 데이터를 새로 렌더링하게 됩니다.

 

 참고로 getUserInfo 함수의 인자인 pages는 현재 페이지를 의미하며, LIMIT은 데이터를 한 페이지에 렌터링하는 최대 개수인 10을 저장한 상수, searchValue는 검색 값입니다. 

 

 눈치채신 분도 있겠지만, useEffect Hook 함수에서 페이지 네이션, 각 유저의 메뉴 아이템 변경 기능도 이루어집니다. 

 

 이해를 돕기위해 미리 코드를 첨부하겠습니다. 코드가 길어서 글을 다 읽고 봐주시는 것을 권장합니다.

 

Admin.js

더보기
import React, { useEffect, useState } from 'react';
import Modal from 'Modal';
import SignUp from 'Pages/SignUp';
import searchIcon from 'Assets/search.png';
import { style } from './AdminStyle';
import { MENUS, LOCAL_STORAGE, LIMIT } from 'utils/constants';
import { getUserInfo } from 'utils/getUserInfo';
import Checkbox from 'Components/Checkbox';
import Layout from 'Components/Layout';
import { AiOutlineCheck } from 'react-icons/ai';
import userDataForm from 'utils/storage/userDataForm';

function Admin() {
  const [data, setData] = useState([]);
  const [searchValue, setSearchValue] = useState('');
  const [checkedArray, setCheckedArray] = useState({});
  const [modalStyle, setModalStyle] = useState(false);
  const [showModal, setShowModal] = useState(false);
  const [pages, setPages] = useState(1);
  const [maxPage, setMaxPage] = useState(1);
  const [clickCheck, setClickCheck] = useState(false);
  const [isSubmit, setIsSubmit] = useState(false);
  const checkedKeys = Object.keys(checkedArray);

  // 초기 저장된 메뉴 아이템 선택 렌더링
  useEffect(() => {
    initSelected(LOCAL_STORAGE.get('userData'));
  }, []);
  
  // 페이지, 검색 입력 값, 메뉴 아이템 useEffect
  useEffect(() => {
    const userInfo = getUserInfo(pages, LIMIT, searchValue);
    setData(userInfo.userData);
    setMaxPage(userInfo.maxPage);
    setClickCheck(false);
  }, [pages, searchValue, clickCheck]);

  // 검색 입력폼 onChange
  const onHandleSearch = (e) => {
    setSearchValue(e.target.value);
  };
  
  // 현재 저장된 userData에서 userId와 menubar만 추출한다.
  const initSelected = (userData) => {
    let obj = new Object();
    obj = userData.reduce(
      (acc, cur) => ({ ...acc, [cur.userId]: cur.menubar }),
      {},
    );
    setCheckedArray(obj);
  };
  
  // 각 메뉴를 클릭하면 발생하는 onClick 함수이다.
  // 체크된 메뉴와 체크 해지된 메뉴를 구분하여 체크 해지된 삭제한다.
  const onClickChckBtn = (page, path, userId) => {
    const seletedInfo = checkedKeys.includes(userId);
    let obj = new Object();
    let newSelected = [];

    let innerObj = new Object();
    innerObj['name'] = page;
    innerObj['path'] = path;

    if (seletedInfo === false || checkedArray[userId].length <= 0) {
      newSelected.push(innerObj);
    } else {
      let selectedIndex = true;
      for (const key in checkedArray[userId]) {
        if (checkedArray[userId][key].name !== innerObj.name) {
          selectedIndex = false;
        } else {
          selectedIndex = true;
          break;
        }
      }

      if (selectedIndex === false) {
        newSelected = newSelected.concat(checkedArray[userId], innerObj);
      } else {
        newSelected = newSelected.concat(checkedArray[userId]);
        const rmvFindIndx = newSelected.indexOf(
          newSelected.find((elem) => elem.name === innerObj.name),
        );
        newSelected.splice(rmvFindIndx, 1);
      }
    }
    for (const [key, value] of Object.entries(checkedArray)) {
      obj[key] = value;
    }
    obj[userId] = newSelected;
    setCheckedArray(obj);
  };
  
  /*
	선택된 메뉴인지 확인한다. 기존 렌더링할 메뉴의 이름과 선택된 메뉴의 이름이 같으면 true,
    아니면 false를 리턴한다.
  */
  const isSelected = (name, userId) => {
    if (checkedKeys.length > 0 && checkedKeys.includes(userId.toString())) {
      for (const [key] of Object.entries(checkedArray[userId])) {
        if (checkedArray[userId][Number(key)].name === name) {
          return true;
        }
      }
    }
    return false;
  };
  
  // 각 유저별로 갱신된 메뉴를 userArray에 저장하고 userArray를 로컬스토리지에 다시 저장한다.
  const onClickSubmitBtn = () => {
    const allUserData = LOCAL_STORAGE.get('userData');
    let userArray = [];
    for (let i = 0; i < Object.keys(allUserData).length; i++) {
      let origin_userId = allUserData[i].userId;
      let menubar = checkedArray[origin_userId];

      userArray.push(
        userDataForm(
          origin_userId,
          allUserData[i].password,
          allUserData[i].name,
          allUserData[i].age,
          allUserData[i].creditCard.cardNumber,
          allUserData[i].creditCard.holderName,
          allUserData[i].creditCard.expired,
          allUserData[i].creditCard.CVC,
          allUserData[i].role,
          allUserData[i].address,
          menubar,
        ),
      );
    }
    LOCAL_STORAGE.set('userData', userArray);
    setIsSubmit(true);

    setTimeout(function () {
      setIsSubmit(false);
    }, 3000);
  };

  const onClickButtonLeft = () => {
    const page = pages - 1;
    if (page < 1) {
      setPages(1);
    } else {
      setPages(page);
    }
  };

  const onClickButtonRight = () => {
    const page = pages + 1;
    if (page > maxPage) {
      setPages(maxPage);
    } else {
      setPages(page);
    }
  };

  const openModal = () => {
    setShowModal(true);
    setModalStyle(!modalStyle);
  };

  const closeModal = () => {
    setShowModal(false);
    setModalStyle(!modalStyle);
  };

  return (
    <Layout>
      <TableContainer>
        <TableTitleContainer>
          <TableTitleBox>
            <TableTitle>사용자 목록</TableTitle>
            <AccountAddButton onClick={() => openModal()}>
              계정 추가
            </AccountAddButton>
          </TableTitleBox>
          <SearchContainer>
            <SearchIcon src={searchIcon} alt="search-icon" />
            <Searchbox
              type="text"
              placeholder="Search Name, ID"
              onChange={onHandleSearch}
            />
            {!isSubmit && (
              <PageAuthButton onClick={onClickSubmitBtn}>
                페이지 권한 확정하기
              </PageAuthButton>
            )}

            {isSubmit && (
              <PageAuthButton disabled={true} onClick={onClickSubmitBtn}>
                <AiOutlineCheck />
                확정되었습니다.
              </PageAuthButton>
            )}
          </SearchContainer>
        </TableTitleContainer>

        <table>
          <thead>
            <tr>
              <Cell>ID</Cell>
              <Cell>Name</Cell>
              <Cell>Age</Cell>
              <Cell>Role</Cell>
              <Cell>Address</Cell>
              <Cell>Menu</Cell>
            </tr>
          </thead>
          {data &&
            data.map((data, indexs) => (
              <tbody key={indexs}>
                <tr key={indexs}>
                  <Cell>{data.userId}</Cell>
                  <Cell>{data.name}</Cell>
                  <Cell>{data.age}</Cell>
                  <Cell>{data.role}</Cell>
                  <Cell>{data.address}</Cell>
                  <Cell>
                    {MENUS.map((property, index) => {
                      let isItemSelected = isSelected(
                        property.name,
                        data.userId,
                      );

                      return (
                        <div key={index}>
                          <Checkbox
                            type="checkbox"
                            checked={isItemSelected}
                            id={index}
                            onClick={() =>
                              onClickChckBtn(
                                property.name,
                                property.path,
                                data.userId,
                              )
                            }
                          />
                          <label>{property.name}</label>
                        </div>
                      );
                    })}
                  </Cell>
                </tr>
              </tbody>
            ))}
        </table>
        <TableFooter>
          <div>
            <AiOutlineLeftStyle
              pageend={pages === 1 ? 'true' : 'false'}
              onClick={onClickButtonLeft}
            />
            <div>{pages}</div>
            <AiOutlineRightStyle
              pageend={pages === maxPage ? 'true' : 'false'}
              onClick={onClickButtonRight}
            />
          </div>
        </TableFooter>
      </TableContainer>
      <Modal
        show={showModal}
        onClickClose={() => closeModal()}
        accountStyle={modalStyle}
      >
        <SignUp accountPlus={modalStyle} />
      </Modal>
    </Layout>
  );
}

export default Admin;

const {
  Cell,
  AccountAddButton,
  Searchbox,
  SearchContainer,
  SearchIcon,
  TableContainer,
  TableTitleBox,
  TableFooter,
  TableTitle,
  TableTitleContainer,
  PageAuthButton,
  AiOutlineLeftStyle,
  AiOutlineRightStyle,
} = style;

 

 

getUserInfo.js

더보기
import { LOCAL_STORAGE } from './constants';

export const getUserInfo = (pages, limit, searchWord) => {
  const originalData = LOCAL_STORAGE.get('userData');
  const filteredUserInfo = [];
  const nonFilteredUserInfo = [];
  const paginationInfo = [];

  {/*저장되어 있던 모든 데이터를 탐색.*/}
  originalData?.map((user) => {
    if (
      user.userId.search(searchWord) >= 0 ||
      user.name.search(searchWord) >= 0
    ) {

      {/*검색 결과와 일치하는 것이 있으면 필터 배열에 푸쉬.*/}
      filteredUserInfo.push({
        userId: user.userId,
        password: user.password,
        name: user.name,
        age: user.age,
        role: user.role,
        address: user.address,
        menubar: user.menubar,
        creditCard: {
          CVC: user.creditCard.CVC,
          cardNumber: user.creditCard.cardNumber,
          expired: user.creditCard.expired,
          holderName: user.creditCard.holderName,
        },
      });
    }

    {/*일치하지 않는 것이 있으면 논필터 배열에 푸쉬.*/}
    nonFilteredUserInfo.push({
      userId: user.userId,
      password: user.password,
      name: user.name,
      age: user.age,
      role: user.role,
      address: user.address,
      menubar: user.menubar,
      creditCard: {
        CVC: user.creditCard.CVC,
        cardNumber: user.creditCard.cardNumber,
        expired: user.creditCard.expired,
        holderName: user.creditCard.holderName,
      },
    });
    return;
  });
  
  {/*
    filter배열의 길이가 0이라면 검색되지 않은 상태인 초기 렌더링 혹은 새로고침 이므로 
    논필터링 배열이 data가 된다.
  */}
  const data =
    filteredUserInfo.length !== 0 ? filteredUserInfo : nonFilteredUserInfo;
  
  {/*올림을 이용하여 최대 페이지를 설정한다.*/}
  const maxPage =
    Math.ceil(data.length / limit) > 0 ? Math.ceil(data.length / limit) : 1;
  
  {/*설정된 데이터를 각 페이지 별로 렌더링 한다.*/}
  data.map((user, idx) => {
    if (idx >= (pages - 1) * limit && idx < pages * limit) {
      paginationInfo.push({
        userId: user.userId,
        password: user.password,
        name: user.name,
        age: user.age,
        role: user.role,
        address: user.address,
        menubar: user.menubar,
        creditCard: {
          CVC: user.creditCard.CVC,
          cardNumber: user.creditCard.cardNumber,
          expired: user.creditCard.expired,
          holderName: user.creditCard.holderName,
        },
      });
    }
    return;
  });
  return { userData: paginationInfo, maxPage: maxPage };
};

 

 

 

□ 페이지 네이션

페이지 네이션 사진 일부분

 페이지 네이션 기능은 각 페이지당 최대 10개의 데이터가 보이도록 구현됐습니다. 위의 "검색 기능의 핵심" 사진인 useEffect Hook 함수에서 초기 렌더링 될 경우에도 getUserInfo 컴포넌트를 호출하게 됩니다. 위 사진의 페이지를 호출하는 양쪽 화살표 버튼을 눌러도 useEffect 함수를 호출하게 됩니다.  

 

 getUserInfo 컴포넌트 안에서는 기존 로컬 스토리지에 저장되어 있는 데이터를 저장하는 originalData와 검색 시 필터링된 데이터 배열을 저장하는 filteredUserInfo, 필터링 되지 않는 데이터 배열을 저장하는 nonFilteredUserInfo, 페이지 별로 데이터를 저장하는 paginationInfo, 최종 데이터 배열을 저장하는 상수인 data가 존재합니다. 

 

 아무 검색 결과가 없는 초기 렌더링에는 filteredUserInfo 배열에는 어떤 데이터도 들어가 있지 않을 것이고, 모든 데이터가  nonFilteredUserInfo에 저장될 것입니다. 즉, 최종 데이터 배열을 저장하는 상수인 data에는 nonFilteredUserInfo 배열 값이 저장될 것고, paginationInfo배열을 통해서 페이지 별로 렌더링 하는 로직입니다. 

 

 검색 시에도 로직은 동일합니다. filteredUserInfo 배열과 nonFilteredUserInfo 배열에 각각 값들이 들어가 있을 것이고, data 배열에는 filteredUserInfo 배열 값이 저장될 것입니다. 

 

 

 

□ 유저 메뉴아이템 변경 기능 

 

 관리자 페이지에서 항목의 우측 부분에 각 유저의 메뉴를 수정할 수 있는 체크 버튼이 있습니다. 이 기능을 활용하여 해당 유저가 접근할 수 있는 메뉴를 Navbar에 노출시키게 됩니다. 

 

 메뉴를 선택하면 선택된 메뉴의 이름, 경로, userId를 인자로 받는 onClickChckBtn 함수를 발동합니다. 기존에 모든 메뉴가 체크 해제된 userId를 선택했다면 그냥 체크된 메뉴를 넣어줍니다. (seletedInfo의 역할은 아직 잘 모르겠습니다..) 
 

 selectedIndex는 체크 해제하면 true가 됩니다. 즉, 체크 상태인 메뉴는 false입니다. 그래서 selectedIndexfalse면 모든 배열이 체크가 된 것이므로 기존 체크 배열인 checkedArraynewSelected 배열에 넣어주고 새로 체크된 innerObj를 넣어줍니다. 

 

 selectedIndextrue면 기존 체크된 배열을 모두 newSlected에 넣어주고 체크 해제된 메뉴를 찾아서 삭제합니다. 

새로 렌더링 될 때마다 isSelected 함수에서 checked 속성을 판단합니다. 최종적으로 onClickSubmitBtn 함수를 통해 각 유저별로 갱신된 메뉴를 userArray에 저장하고 로컬 스토리지에 userArray 배열을 저장합니다. 

 

 

 


메뉴바

 

□ 관리자 페이지에서 계정별 메뉴 설정 시 해당 계정에게 선택된 메뉴만 보이는 기능

 

 관리자 페이지에서 계정별 메뉴 설정 시 해당 계정에게 선택된 메뉴만 보여지는 기능은 위의 "유저 메뉴 아이템 변경 기능"에서 이어지는 내용입니다.  

 

 Navbar.js 컴포넌트에서 해당 계정에게 선택된 메뉴만 보이도록 렌더링합니다. 로그인 상태가 아니라면 일반적으로 보이는 메뉴를, 로그인 상태면 이 유저가 관리자인지 일반 유저인지 판단하고 관리자면 관리자 전용 메뉴를 렌더링하고 일반 유저이면 그 유저에게 선택된 메뉴만 렌더링 하게 됩니다. 

 

Navbar.js

더보기
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
  NavbarContainer,
  NavbarInnerContainer,
  NavLink,
  NavMenu,
} from 'Components/Navbar/NavbarStyles';
import jaranda from 'Assets/jarandalogo.png';
import { PUBLIC_MENUS, ROUTES } from 'utils/constants';
import { getUserMenu } from 'Services/user';
import { checkIsAdmin, checkIsLoggedIn, logout } from 'Services/auth';

const Navbar = () => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [isAdmin, setIsAdmin] = useState(false);
  const [userMenu, setUserMenu] = useState([]);

  // 마운트 시 로그인 검사
  useEffect(() => {
    setIsLoggedIn(checkIsLoggedIn());
  }, []);

  // 로그인 여부와 권한에 따라 보여지는 메뉴가 다르다.
  useEffect(() => {
    if (isLoggedIn) {
      setIsAdmin(checkIsAdmin());
      setUserMenu(getUserMenu());
    } else {
      setUserMenu(PUBLIC_MENUS);
      setIsAdmin(false);
    }
  }, [isLoggedIn]);

  // 로그아웃 
  const onClickLogout = () => {
    logout();
    setIsLoggedIn(checkIsLoggedIn());
  };

  return (
    <NavbarContainer>
      <NavbarInnerContainer>
        <NavLink to={isAdmin ? ROUTES.ADMIN : ROUTES.MAIN}>
          <img src={jaranda} alt="logo" />
        </NavLink>
        <NavMenu>
          {!isAdmin &&
            userMenu.map((menu) => (
              <NavLink key={menu.path} to={menu.path}>
                {menu.name}
              </NavLink>
            ))}
          {isLoggedIn && (
            <NavLink to={ROUTES.MAIN} onClick={onClickLogout}>
              로그아웃
            </NavLink>
          )}
        </NavMenu>
      </NavbarInnerContainer>
    </NavbarContainer>
  );
};

export default Navbar;

Navbar.propTypes = {
  isLoggedIn: PropTypes.bool,
  userMenu: PropTypes.array,
  isAdmin: PropTypes.bool,
  onClickLogout: PropTypes.func,
};

 

GIF를 첨부하겠습니다. 

 

1. 일반 유저인 user1 계정으로 로그인하여 보여지는 메뉴 화면

초기 user1 계정에서 보여지는 메뉴 

 

2. 관리자 계정인 admin1 계정으로 로그인하여 user1 계정의 메뉴를 설정하는 화면 

관리자가 user1 계정의 메뉴를 설정한다.

 

3. 일반 유저인 user1 계정의 메뉴가 변경되는 화면 

user1의 메뉴가 변경됐다.

 

 

 


접근 제한

 

□ 권한별 접근 가능한 페이지 이동

 

 권한별 접근 가능한 페이지 이동은 라우터를 이용한 접근 제한으로 구현하였습니다. 사용자의 로그인 상태와 권한에 따라 이동할 수 있는 경로를 다르게 구성하였습니다. 

 

 PrivateRoute는 로그인된 관리자 유저만 이동할 수 있는 경로입니다.

PublicRoute는 모든 유저가 이동할 수 있지만 로그인 상태에서 굳이 갈 필요 없는 로그인, 회원 가입 페이지는 restricted 속성을 true로 설정하여 접근을 제한 하였습니다. 

 

 App.js 컴포넌트에서 라우트 경로를 지정해줬고, routes.js에서 PrivateRoutePublicRoute에 관한 상세 설정을 했습니다. 

 

 

App.js

더보기
import React from 'react';
import { Switch } from 'react-router-dom';
import Landing from 'Pages/Landing';
import Support from 'Pages/Support';
import Help from 'Pages/Help';
import Login from 'Pages/SignIn';
import SignUp from 'Pages/SignUp';
import Watch from 'Pages/Watch';
import Form from 'Pages/Form';
import History from 'Pages/History';
import Schedule from 'Pages/Schedule';
import Log from 'Pages/Log';
import Admin from 'Pages/Admin';
import NotFound from 'Pages/NotFound';
import userData from 'utils/userData.json';
import { ROUTES, LOCAL_STORAGE } from 'utils/constants';
import { PrivateRoute, PublicRoute } from 'routes';

if (!LOCAL_STORAGE.get('userData')) {
  LOCAL_STORAGE.set('userData', userData);
}

function App() {
  return (
    <Switch>
      <PublicRoute exact path={ROUTES.MAIN} restricted={false}>
        <Landing />
      </PublicRoute>
      <PublicRoute path={ROUTES.SUPPORT} restricted={false}>
        <Support />
      </PublicRoute>
      <PublicRoute path={ROUTES.HELP} restricted={false}>
        <Help />
      </PublicRoute>
      <PublicRoute path={ROUTES.SIGN_IN} restricted={true}>
        <Login />
      </PublicRoute>
      <PublicRoute path={ROUTES.SIGN_UP} restricted={true}>
        <SignUp />
      </PublicRoute>
      <PrivateRoute path={ROUTES.WATCH}>
        <Watch />
      </PrivateRoute>
      <PrivateRoute path={ROUTES.FORM}>
        <Form />
      </PrivateRoute>
      <PrivateRoute path={ROUTES.HISTORY}>
        <History />
      </PrivateRoute>
      <PrivateRoute path={ROUTES.SCHEDULE}>
        <Schedule />
      </PrivateRoute>
      <PrivateRoute path={ROUTES.LOG}>
        <Log />
      </PrivateRoute>
      <PrivateRoute path={ROUTES.ADMIN}>
        <Admin />
      </PrivateRoute>
      <PublicRoute path={ROUTES.NOTFOUND} restricted={false}>
        <NotFound />
      </PublicRoute>
    </Switch>
  );
}

export default App;

 

 

routes.js

더보기
import { React } from 'react';
import { Redirect, Route } from 'react-router-dom';
import PropTypes from 'prop-types';
import { ROUTES } from './utils/constants';
import { checkIsAdmin, checkIsLoggedIn } from 'Services/auth';
import { isUserMenu } from 'Services/user';

export const PublicRoute = ({ restricted, children, ...rest }) => {
  return (
    <Route {...rest}>
      {checkIsLoggedIn() && restricted ? (
        <>
          {checkIsAdmin() ? (
            <Redirect to={ROUTES.ADMIN} />
          ) : (
            <Redirect to={ROUTES.MAIN} />
          )}
        </>
      ) : (
        children
      )}
    </Route>
  );
};

PublicRoute.propTypes = {
  children: PropTypes.element,
  restricted: PropTypes.bool,
};

export const PrivateRoute = ({ children, path, ...rest }) => {
  if (!checkIsLoggedIn()) {
    return <Redirect to={ROUTES.MAIN} />;
  }

  if (checkIsAdmin()) {
    return <Route {...rest}>{children}</Route>;
  }

  return (
    <Route {...rest}>
      {isUserMenu(path) ? children : <Redirect to={ROUTES.MAIN} />}
    </Route>
  );
};

PrivateRoute.propTypes = {
  children: PropTypes.element,
  path: PropTypes.string,
};

 

 

 

 


3편은 여기서 마무리하도록 하겠습니다. 더 좋은 생각이나 의견이 있다면 댓글로 피드백 주시면 좋겠습니다.

 

긴 글 읽어주셔서 감사합니다.  

 

 

반응형