Router 정리하기

이강혁·2024년 5월 7일
0

막힘

목록 보기
10/11


커머스 프로젝트를 진행하면서 구현을 끝낸 시점 리팩토링을 시작했다. 가장 먼저 손봐야겠다고 생각한 곳은 라우터였는데 사진과 같았다. 권한 구분도 안 되어 있었고 보기에도 어렵고 그래서 권한에 따라 라우터 구분하는 김에 라우터도 이쁘게 만들어봐야겠다 결심했다.


라우터 권한 구분하기


보통 권한구분 하는 것처럼 이렇게 PrivateRoute를 만들어서 해당하는 라우터만 태그 안에 넣는 방식을 사용했는데 지금은 판매자, 구매자 밖에 없지만 나중에 권한이 더 생기거나 권한에 따라서 공유하는 페이지도 있고 그러면 어떡하지라는 걱정이 생겼다.


유연하게 대응가능한 라우터

펭구스님 코드
동민님 코드
펭구스님이 가르쳐주신 방법과 동민님 코드를 참고해서 라우터를 재구성하였다.

이렇게 페이지에 대한 정보를 상수로 만들어줬다. route속성에는 보여줘야는 컴포넌트의 이름을 적어줬다.

그렇게 하고나서 Aggregate컴포넌트를 반환할 withAggregate 컴포넌트를 만들고

이제 페이지와 권한별로 필터링해줄 AggregateComponent라는 컴포넌트를 따로 만들었다.


전역상태라이브러리 사용하지 않기로 마음먹었기에 firebase에서 사용자의 role을 가져오고. useRef로 저장해주었다.


상태 가져오는게 끝나면 페이지와 권한에 따라 메인으로 보낼 지 그대로 보여줄지 결정해줬다.


마지막으로 라우터 상수를 가지고와서 각각 import 해주고 Route 컴포넌트로 만들어줌으로써 페이지를 사용할 수 있게 되었다.


하다가 막혔다가 풀렸음


중간에 몇몇 페이지에서 이런 에러를 보이면서 화면이 나오지 않는 문제가 발생하기도 했다.

Uncaught TypeError: Cannot convert object to primitive value

그냥 단순한 타입에러인가 싶었지만 제일 첫 사진에 나온 것처럼 만들었을 때는 잘 작동하다가 aggregate로 하니까 안 되길래 일단 내부 코드를 다 살펴봤는데 이상한 점을 찾을 수 없었고 구글링 했을 때도 객체 타입 오류라는 내용의 글만 나왔다. 비슷한 방식을 사용한 다른 팀원분께 여쭤봤는데 컴포넌트를 export 하는 방식 때문에 발생한 문제였다.

컴포넌트 이름 잘못 쓰는 것을 방지하기 위해 해당 페이지들에서 named export를 사용했는데

이런식으로 import 했으니 타입 에러가 나는 거였다. 그래서 export default로 고쳐주니까 정상적으로 import가 되고 화면도 잘 나왔다.


해결되지 못한 의문이었다가 해결함


aggregate 파일에서 이런식으로 AggregateComponent를 내보냈는데 굳이 이렇게 해야하나 싶었다.

그래서 useEffect 안에 콘솔을 넣어서 테스트를 했는데 사진과 같이 중복으로 사용했을 때는

이렇게 한 번만 출력이 됐지만
AggregateComponent를 직접 사용했을 때는

이렇게 모든 페이지가 렌더링 되는 문제가 발생했다. 페이지가 몇 개 없어서 성능 상에 문제는 없는 거 같지만 그래도 찝찝해서 일단 중복으로 사용하는 방식으로 결정을 하긴 했는데 왜 이런 문제가 발생하는 지는 아직 밝혀내지 못했다. 차차 알아봐야겠다.

라고 하고 GPT 선생님과 멘토님께 여쭤봤다.

GPT는

import React, { lazy } from "react";
import { Route, Routes } from "react-router-dom";
import { ROUTES } from "./route.constant"
import { Suspense } from "react";
import { AggregateComponent, withAggregate } from "./withAggregate";
import Layout from "component/layout/Layout";

const LazyRouteComponent = ({ route }: { route: string }) => {
  const LazyComponent = lazy(() => import(`@/pages/${route}.tsx`));

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <AggregateComponent page={route}>
        <LazyComponent />
      </AggregateComponent>
    </Suspense>
  )
}


export const AppRoute = () => {
  return (
    <Routes>
      <Route element={<Layout />}>
        {Object.entries(ROUTES).map(([key, { path, route }]) => (
          <Route key={key} path={path} element={<LazyRouteComponent route={route} />} />
        ))}
      </Route>
    </Routes>
  )
}

다음과 같이 withAggregate를 사용하는 대신 LazyComponent를 따로 빼서 사용하기를 권장했고 이렇게 했더니 원하는 페이지만 렌더링 되었다.

멘토님은


export const AppRoute = () => {
  return (
    <Routes>
      <Route element={<Layout />}>
        {Object.entries(ROUTES).map(([key, { path, route }]) => {
          const LazyComponent = React.lazy(
            () => import(`@/pages/${route}.tsx`)
          );
          return (
            <Route
              key={key}
              path={path}
              element={
                <Suspense fallback={<div>Loading...</div>}>
                  <AggregateComponent children={<LazyComponent />} page={route} />
                </Suspense>
              }
            />
          )
        })}
      </Route>
    </Routes>
  )
}

이렇게 기존에 대괄호로 묶어놓았던 AggregateComponent를 HTML 태그처럼 바꿔서 사용해보라고 하셨고 원하는 페이지만 렌더링 되었다.
이 현상에 대해서 jsx 파일을 babel이 변환하는 과정에서 문제가 발생한 것으로 추정한다고 말씀해주셨다.

GPT는 Suspense 구성요소는 map 루프 안의 개별 경로 구성요소 즉 페이지별 AggregateComponent를 래핑해서 react에서 모든 요소를 렌더링 하게 되었지만 태그로 바꾸면서 Suspense 배치가 변경되면서 현재 경로를 기반으로 구성요소가 느리게 로드되어 성능이 향상되고 비횔성 구성요소의 불필요한 레더링이 방지된다고 했는데 무슨 말인지 모르겠다. Lazy 로딩에 대해서 따로 공부해봐야겠다.

어쨌든 AggregateComponent 호출하는 방식을 바꿈으로써 불필요한 코드의 중복과 렌더링을 방지할 수 있게 되었다.

profile
사용자불량

0개의 댓글