[개인 프로젝트] CardLog 제작기(3) - 싱글톤 패턴 응용 Routes 모듈화 처리

건둔덕 ·2023년 3월 13일
2

CardLog 제작기

목록 보기
3/7
post-thumbnail

이전 작업에서 디자인까지 마무리를 지었다.

지금은 프론트 부분의 전반적인 초기 세팅을 전부 해주고 있는데 이번 프로젝트에서는 전체적으로 재사용성이 높은 코드들로 제작을 해보려고 한다.

처음에는 많은 부분을 생각하고 코드를 짜고 모듈화가 잘 되었다고 생각했는데, 결국 예상치 못한 부분이 게속 생겼었다.

내가 이번에 가장 많이 시간을 뺏긴 React-Router-Dom의 Route 모듈화와 초기 세팅에 대해 글을 작성해보려고 한다.

일단 나는 완벽하게 잘 다룬다고 생각하지 않기 때문에 혹시 더 좋은 방법이 있다면 댓글 부탁드립니다!

Route 모듈화

지금 현재 내가 작업하고 있는 프로젝트 이전에 작업했었던 팀 프로젝트 에서는 각 URL의 휴먼 에러를 방지하기 위해 path 들을 모아두는 파일을 하나 따로 생성해서 사용했었다.

const Path = {
  HOME: "/",
  LOGIN: "/login",
  SIGNUP: "/signup",
  MYINFO: "/myinfo",
  WRITE: "/write",
  BOARD: "/board",
};

Object.freeze(Path);

export default Path;

위의 예시와 같은 방식으로 사용을 했기 때문에 다른 프로젝트에서는 URL을 항상 써주지 않고 Path import 해와서 항상 사용 했었다.

솔직히 위의 방법으로 url의 휴먼 에러 자체를 방지할 수 있어서 굉장히 편리하게 사용한 방법이다. 근데 이번에는 좀 더 재사용성을 높게 처리해 보고 싶었다.

마침 이와 관련된 강의도 듣게 되었고 그 강의에서 레이아웃 컴포넌트 관련된 강의를 듣게 되었다.
근데 강의 내용에서의 상황은 좀 더 간단한 프로젝트에서 처리를 해주는 예시 이다보니 내가 지금 진행하고 있는 프로젝트에서는 적용하기가 좀 힘들었다.

그래서 혼자 열심히 연구해서 짜봤다.

일단 내가 만들 때 고려한 상황은 다음과 같다.

  • Path
    Path를 모듈화 하게되면 확실히 편리하고 휴먼 에러를 방지할 수 있어서 무조건 해야겠다고 생각했었다.

  • Route
    Route 부분은 Path 모듈화와 함께 꼭 해보고 싶어서 고려한 부분인데 좀 더 구체적으로 설명하자면, Route 처리 중 로그인을 안해도 누구나 볼 수 있는 라우트와 로그인을 하지 않으면 볼 수 없는 페이지 처리를 해주고 싶었고, Nav 부분이 나오는 라우트와 나오지 않는 라우트 처리도 해주고 싶었다.

위의 상황을 고려해서 작업을 하고 싶었다.



RouterInfo

여러 시행 착오 끝에 RouterInfo라는 파일을 만들고 아래와 같이 코드를 작성했다.

import Home from "../../pages/Home";
import Login from "../../pages/Login";
import MyInfo from "../../pages/MyInfo";
import PostDetail from "../../pages/PostDetail";

export interface RouterItem {
  path: string;
  element: JSX.Element;
  isLoggedIn: boolean;
  isWithNav: boolean;
  isNavIn: boolean;
  label: string;
}

type RouterItemType = {
  [key: string]: RouterItem;
};

const RouterInfo: RouterItemType = {
  HOME: {
    path: "/",
    element: <Home />,
    label: "홈",
    isLoggedIn: false, // 로그인이 필요한 페이지인지
    isWithNav: true, // Nav와 함께 나올 페이지인지
    isNavIn: true, // Nav안에 보여줄 페이지인지
  },
  LOGIN: {
    path: "/login",
    element: <Login />,
    label: "로그인",
    isLoggedIn: false,
    isWithNav: false,
    isNavIn: true,
  },
  POST: {
    path: "/posts/:id",
    element: <PostDetail />,
    label: "게시물 상세페이지",
    isLoggedIn: false,
    isWithNav: true,
    isNavIn: true,
  },
  MYINFO: {
    path: "/my",
    element: <MyInfo />,
    label: "내 정보",
    isLoggedIn: true,
    isWithNav: false,
    isNavIn: true,
  },
};

Object.freeze(RouterInfo);

export default RouterInfo;

위의 코드를 만들 때 처음에 RouterInfo는 배열이였고 그 안에 객체들로 구성을 했었었다. 하지만 그 방식으로 만들어보니 처음에 고려하려고 했었던 path를 편하게 사용할 수가 없었다. 그래서 생각을 해낸 방식이 위의 방식이다.

배열을 사용하는게 아닌 아예 객체로 데이터를 만들고 Path를 사용할 때는 key값 안의 path를 불러오게 했다. 이 부분은 formData를 불러오는 방식에서 해답을 찾았던 것 같다.



PrivateRoute

이제는 RouterInfo 데이터를 가지고 라우트를 만들 때 로그인이 되어야만 이동할 수 있는 라우트를 처리하기 위해서 PrivateRoute라는 파일을 만들어주고 아래와 같이 만들어 줬다.

import { ReactNode } from "react";
import { Navigate } from "react-router-dom";
import { useAuth } from "../../hooks/useAuth";

interface PrivateRouteProps {
  children: ReactNode;
}

const PrivateRoute: React.FC<PrivateRouteProps> = ({ children }) => {
  const auth = useAuth();

  return auth ? <>{children}</> : <Navigate to="/login" />;
};

export default PrivateRoute;

위에서 PrivateRouteuseAuth로 로그인이 되어있는지를 판단하고 로그인이 되어있으면 자식 컴포넌트(Route)를 반환하고 로그인 되어있지 않다면 login페이지로 이동시켜버리는 함수다.

참고로 useAuth는 로그인이 되어있는 상태인지를 판단하고 boolean 값으로 리턴해주는 리액트 커스텀 훅을 내가 직접 만들었당 👏



Routes 처리

처음에는 Routes를 App.tsx에 만들어 두고 그 안에서 map을 이용해서 처리할 생각이었다.
하지만 게속 에러가 났다.. 열심히 검색하고 게속 찾고.. 여러번 시도한 끝에 새로운 사실들을 알았다.

React-Route-Dom은 v6부터 Switch말고 Routes를 사용하는데 이 Routes의 자식으로는 무조건 Route만 올 수 있고, Routes 안에는 다른 컴포넌트가 있으면 바로 에러가 났다.

많은 시간을 쏟은 후 나는 깔끔하게 처리할 수 있었다!

import { Route, Routes } from "react-router-dom";
import RouterInfo, { RouterItem } from "./RouterInfo";
import PrivateRoute from "./PrivateRouter";
import React from "react";

const RoutesObject = (): JSX.Element => {
  return (
    <Routes>
      {Object.entries(RouterInfo).map(([key, item]: [string, RouterItem]) => {
        return (
          <Route
            key={item.path}
            path={item.path}
            element={
              item.isLoggedIn ? (
                <PrivateRoute>{item.element}</PrivateRoute>
              ) : (
                <>{item.element}</>
              )
            }
          />
        );
      })}
    </Routes>
  );
};

export default RoutesObject;

위와 같은 방식으로 처리를 해주고 나니 굉장히 뿌듯했다. 완벽하다고 볼 수는 없지만 나름 만족스러웠다. 저렇게 처리를 해주고 App.tsx에서 RoutesObject를 내가 사용할 레이아웃 부분에 살포시 얹어 주기만 하면 아주~ 실행이 잘됐다.



라우트 처리 하나에만 굉장히 많은 시간을 보낸 것 같지만 굉장히 값진 시간이었다.

참고로 RouterInfo데이터는 Nav에서도 그대로 사용하고 있고 데이터 통신 시 URL 부분에도 사용하고 있다. 아주 만족스럽다.

참고
GitHub

profile
건데브

0개의 댓글