줄줄이 소시지 코드에 눈이 아파온다.
하드코딩된 값이 많아지고 반복된 작업이 눈이 밟혀 리팩터링을 진행했다.
우선, route와 관련된 값들을 하나의 객체로 빼내어, 상수로 관리하고자 하였다.
route(key)값에 대한 value들을 하나하나 Object.freeze 할 경우, 소프트 코딩 및 TS의 장점을 살리지 못한다고 판단하여, RouteMap type을 선언하는 과정에서 Readonly를 RouteConfig(value)에 먹여줬다.
굳굳! 깔끔해졌다!
현재 ROUTE_MAP의 HAS_NAV field의 값에 따라, Nav 컴포넌트를 optional하게 rendering시킬 계획이었다.
허나, 한 가지 문제점을 직면하게 되었다.
동적라우팅을 사용하는 경우
useLocation의 결과 값은 '/task/1'이기 때문에'/task/:id'와 값이 다르다는 문제가 발생
했다.
어떻게 넘길 것인가!
useRoutes, useMatch 등등 여러 해결책을 시도해보았지만 생각보다 해당 issue를 해결하는 것이 쉽지 않았다.
결국, 어렵지만 근본적인 해결책인 정규표현식을 채택
하였다.
ROUTE_MAP 상수의 key값에서 동적 라우팅된 부분(:id)를 실제 useLocation의 id의 type으로 변환시켜 match가 되는 값이 있는지 확인하면 해결될 문제였다.
function App() {
const { pathname } = useLocation();
const navArr = Object.entries(ROUTE_MAP)
.filter(([, value]) => value.HAS_NAV)
.map(([path]) => path);
const hasNav = navArr.some((path: string) => {
const pathRegex = new RegExp(`^${path.replace(/:\w+/g, '\\d+')}$`);
return pathRegex.test(pathname);
});
return (
<>
{hasNav && <Nav />}
<ContentWrapper>
<Router />
</ContentWrapper>
</>
);
}
key값의 route에 동적라우팅(/:id)부분을 실제 id가 들어올 수 있는 값으로 변경해주었다.
new RegExp(
^${path.replace(/:\w+/g, '\\d+')}$
)
replace의 결과가 되는 부분에서 w를 사용해 string도 받는 것이 일반적이겠지만, BE에서 task나 project가 Create될 때, id를 autoIncrease를 관리하고 있기 때문에 number만 받는 것이 안전하다고 판단했다.
해당 로직은 두 군데에서 사용된다.
- Nav 컴포넌트 렌더링 여부를 결정하는 App 컴포넌트
- Nav 컴포넌트 유무에 따라 자동적으로 Content가 들어가는 컴포넌트의 크기와 위치를 결정하는 ContentWrapper 컴포넌트.
두 군데에서 반복사용되는 로직이기 때문에 함수 형태로 빼내주었다.
굳 👍