회사 프로젝트를 Vue3로 진행하는데 부분 프론트 개발자로 협업하면서, 몇 달 전에 짰던 React router를 개선할 여지가 있다고 판단하여 구조적인 개선을 해보았다.
function App() {
return (
<>
<NavBar />
<Router />
<Footer />
</>
);
}
기존에도 동일하게 react-router-dom
라이브러리를 사용했고,
공통인 네비게이션 바와 푸터는 전역에 적용되도록 위와, 같이 Router 태그 바깥에 넣었다.
import { BrowserRouter, Routes, Route } from "react-router-dom";
import BlogList from "./post/BlogList";
import CmsList from ".//cmsList/CmsList";
import MainPage from ".//main/MainPage";
import SignInPage from ".//signIn/page";
import MyInquiryList from ".//bbs/inquiry/InquiryList";
import MyCmsList from "./myPage/myCms/MyCmsList";
import InquiryForm from ".//bbs/inquiry/InquiryForm";
import ApplyCms from "./apply/ApplyForm";
import ReviewPage from ".//review/page";
import SigninKakaoLoading from "./signIn/loading/SigninKakaoLoading";
import SignOutLoading from ".//signIn/signOut";
import NotFound from "../error/notFound";
import NoticeDetail from ".//bbs/notice/NoticeDetail";
import InquiryDetail from ".//bbs/inquiry/InquiryDetail";
import MyReviewList from "./review/MyReviewList";
import SigninTwitterLoading from "./signIn/loading/SigninTwitterLoading";
import TossSuccess from "./payment/tossSuccess";
import SigninNaverLoading from "./signIn/loading/SigninNaverLoading";
import MyCmsDetail from "./myPage/myCms/MyCmsDetail";
import TossPayment from "./payment/TossPayment";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/posts" element={<BlogList />} />
<Route path="/commissions" element={<CmsList />} />
<Route path="/reviews" element={<ReviewPage />} />
<Route path="/sign/in" element={<SignInPage />} />
<Route path="/apply/:cmsId" element={<ApplyCms />} />
<Route path="/mypage/cms" element={<MyCmsList />} />
<Route path="/mypage/cms/:cmsApplyId" element={<MyCmsDetail />} />
<Route path="/mypage/inquiry" element={<MyInquiryList />} />
<Route path="/mypage/inquiry/form" element={<InquiryForm />} />
<Route path="/mypage/inquiry/:inqId" element={<InquiryDetail />} />
<Route path="/mypage/reviews" element={<MyReviewList />} />
<Route path="/notice/:noticeId" element={<NoticeDetail />} />
<Route path="/login/kakao" element={<SigninKakaoLoading />} />
<Route path="/login/naver" element={<SigninNaverLoading />} />
<Route path="/login/twitter" element={<SigninTwitterLoading />} />
<Route path="/sign/out" element={<SignOutLoading />} />
<Route path="/toss/success" element={<TossSuccess />} />
<Route path="/toss/payment" element={<TossPayment />} />
{/* 404 error page */}
<Route path="/*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
이것이 기존 라우터 컴포넌트다.
Route 태그는 path
와 element
속성(attribute)을 가진다.
내 코드를 프로젝트 코드와 보면서 내것은 상당히 날것(raw)같고 뭉쳐있다는 생각이 들었다.
내가 생각했던 문제점
../../
의 상대경로로 했지? 이러면 위치 옮길 때 골치아픈데, 게다가 위치 파악이 제대로 안됨...오늘 짠 코드는 나와 신이 알지만, 어제 짠 코드는 신만이 안다는 말이 떠올랐다.
일단 상대 경로부터 바꿨다. @
를 붙인 alias를 등록해서 src를 기준으로 한다.
저번에 포스팅을 하긴 했지만 간략히 다시하겠다.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@router': '/src/router',
'@views': '/src/views',
'@components': '/src/components'
}
},
css: {
preprocessorOptions: {
sass: {
additionalData: '@import "@/assets/scss/_variables";',
},
},
},
})
테스트로 view, components를 등록해 보았다.
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": false,
"baseUrl": ".",
"paths": {
"@router/*": [
"src/router/*"
],
"@views/*": [
"src/views/*"
],
"@components/*":[
"src/components/*"
]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
baseUrl
설정을 꼭 해야 한다. .
으로 설정하더라고.
보통 도메인별로 domain.routes.ts
를 별도 ts 파일로 작성한다.
내 토이 프로젝트는 라우팅이 그렇게 많지 않아서 테스트로 1개의 파일에 작성해 보도록 하겠다.
나는 routes.ts
파일을 src/router에 작성했다.
import React, { lazy } from 'react';
import { RouteObject } from 'react-router-dom';
/**
* 진입페이지
*/
const entryRoutes: RouteObject[] = [
{path: "/", Component: lazy(() => import('@views/EntryPage')),}
];
const statsRoutes: RouteObject[] = [
{path: "/stats", Component: lazy(() => import('@views/MonthlyStats'))}
]
/**
* 모든 라우팅
*/
const pageRoutes = [
...entryRoutes,
...statsRoutes
]
export {
pageRoutes,
}
lazy()
를 써서 지연 로딩을 구현하고 동시에 기존 라우터처럼 길어지는 page import문을 줄일 수 있었다.
타입으로는 RouteObject
를 사용했다. path, Component를 사용했다. (에러 시 ts가 검출)
전체적으로 pageRoutes에서 기존 라우팅 배열들을 합쳐서 export해준다.
이 방법은 mypage는 mypage별로, application은 application별로 그룹핑을 한 효과가 있다.
import { Suspense } from "react";
import { Outlet } from "react-router-dom";
import NavBar from "@components/navbar/index";
import Footer from "@components/footer/Footer";
import CommonLoading from "@components/loading";
function Layout () {
return <>
<NavBar />
<Suspense fallback={<CommonLoading />}>
<Outlet />
</Suspense>
<Footer/>
</>
}
export default Layout;
(지금 보니 CommonLoading 별로다. 이름 나은 것 없나)
Outlet
컴포넌트는 react-router-dom
의 내장 컴포넌트다.
Suspense
컴포넌트로 감싸서 불러오기 중에는 로딩 컴포넌트를 띄우다가 lazy()
를 썼던 routes를 화면에 띄운다.
그러면 저 Outlet
컴포넌트에는 뭐가 들어가나?
import { BrowserRouter as Router, Routes, Route, createBrowserRouter, RouterProvider } from 'react-router-dom';
import { pageRoutes } from "./routers/routes";
import Layout from "@components/global/Layout";
import NotFound from '@views/NotFound';
function App () {
const router = createBrowserRouter([
{
element: <Layout />,
errorElement: <NotFound />,
children: pageRoutes
},
])
return (
<RouterProvider router={router} />
)
}
export default App;
정답은 pageRoutes의 컴포넌트가 Outlet
에 들어가는 것이 된다.
errorElement는 지정되지 않은 router 경로에 접근할 경우 발생한다. 그래서 404 페이지 컴포넌트를 추가해주면 된다.
컴포넌트만 최소한으로 만들어서 테스트해볼 수 있다고 생각한다.
아래 블로그 추천
https://semaphoreci.com/blog/routing-layer-react
여기에 통계 페이지를 더해서 커미션 정산 액셀 파일을 통계를 내서 보여주고 싶은데 고민중이다.