Problem
React Router를 사용하면서 <Link>나 navigate에 경로를 직접 하드코딩해 작성해왔다.
초반에는 단순히 /detail/:id와 같은 문자열을 그대로 작성하는 방식이 빠르고 편리해 보였다.
하지만 서비스가 커지면서 카테고리와 페이지 수가 늘어나고, 특정 경로를 수정해야 할 때마다
코드 전반에서 <Link>나 navigate를 하나 하나 찾아 수정해야 하는 문제가 발생했다.
특히 오타 같은 휴먼 에러로 인한 불필요한 버그가 자주 발생하면서,
라우팅 방식을 더 안정적으로 관리할 필요성을 느꼈다.
즉, “경로를 안전하게 일관성 있게 관리할 수 있는 방법”을 찾는 것이 문제의 핵심이었다.
Solution
이 문제를 해결하기 위해 라우트 경로를 하드코딩하지 않고 중앙에서 관리할 수 있도록 리팩토링을 진행했다.
1. 경로 상수화 (Single Source of Truth)
- 모든 라우트 문자열을 ROUTES 객체에 상수로 선언하고, 필요한 곳에서는 import 하여 사용하도록 변경했다.
export const ROUTES = {
ABOUT: "/aboutMe",
DETAIL: "/detail/:id",
DETAIL_EDIT: "/detail/:id/edit",
HOME: "/home",
MENU: "/menu/:category",
MYPAGE: "/mypage/:userId",
SEARCH: "/search",
WRITE: "/write"
} as const;
2. 빌더 함수 도입 (paths 객체)
- URL의 파라미터나 쿼리를 직접 문자열로 조합하지 않고, 빌더 함수 패턴을 활용해 안전하게 경로를 생성하도록 개선했다.
- 예를 들어 paths.detail({ id })처럼 호출하면, 올바른 형태의 URL이 자동으로 생성된다.
- 이렇게 하면 복잡한 URL 조합 과정을 캡슐화하고, 컴포넌트에서는 단순히 함수를 호출해 사용할 수 있게 된다.
export const paths = {
about: () => ROUTES.ABOUT,
detail: ({ id }: { id: string }) => `/detail/${id}`,
menu: ({ category }: { category: Category }) => `/menu/${category}`,
};
- `home()` → 그냥 상수 반환 (문자열 하드코딩 방지).
- `detail({id})` → 파라미터를 안전하게 넣어주는 URL 빌더.
- `menu({category})` → Category 유니온 타입을 강제 → 잘못된 값 넣으면 컴파일 에러.
3. 컴포넌트 적용
- router.ts파일에서의 라우터 상수 적용.
// routers > index.ts
import { ROUTES } from "./paths";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <Navigate to={ROUTES.HOME} replace /> },
{ path: ROUTES.ABOUT, element: <AboutMe /> },
{ path: ROUTES.DETAIL, element: <Detail /> },
{ path: ROUTES.HOME, element: <Home /> }, ...
]
}
]);
export default function Router() {
return <RouterProvider router={router} />;
}
- 실제 컴포넌트에서 Link 컴포넌트와 navigate에 적용한 코드
// 이전
<Link to={`/detail/${post.id}`}>{post.title}</Link>
<Link to="/">DEV.SoYoung</Link>
// 리팩토링
<Link to={paths.detail({ id: post.id })}>{post.title}</Link>
<Link to={paths.home()}>DEV.SoYoung</Link>
리팩토링 후 장점
Routers와 paths.ts를 통해 모든 경로를 한 곳에서 정의하고, 다른 곳에서는 import하여 사용할 수 있기 때문에 오타를 제거할 수 있다는 장점이 있었다. 그리고 라우터의 중앙 집중 관리를 통해 수정 및 업데이트 시 유지보수 및 리팩토링을 하는 부분에 있어서 확실히 편해졌다. 라우터를 적용하는 각 페이지를 찾아가서 라우터를 수정하는 것이 아니라 Roters와 paths.ts파일에서 주된 수정이 이루어졌기 때문이다.
요약해보면 아래와 같다.
하드코딩 된 라우트 경로 => 중앙 집중 관리 + 빌더 함수화 => 일관성, 안전성, 유지보수성, 리팩토링 내성 강화
- 중앙 집중 관리 (SSOT) : 모든 경로를 한 곳에서 정의 → 오타·불일치 제거
- 타입 안정성 확보 : 빌더 함수에서 Category, userId 등 파라미터 타입 강제
- 가독성/재사용성↑ : paths.xxx() 형태로 코드 의도가 명확
- 리팩토링 내성↑ : 경로 정책 변경 시 한 파일만 수정하면 전체 반영
Next Step
이번 리팩토링으로 라우트 관리 문제는 해결했지만, 아직 보완할 점이 있다.
특히 URL 파라미터나 쿼리에 잘못된 값이 들어오면 화면이 깨지거나 오류가 발생할 수 있다.
다음 단계는 아래와 같은 내용을 진행할 예정이다.
- Zod 스키마를 활용해 param과 query를 검증
- 잘못된 값이 들어올 경우 404 페이지나 리다이렉트 처리
여기까지 정리된 내용은 실제 서비스 규모가 커질수록 필수적으로 적용해야 하는 리팩토링 패턴이라고 생각한다.
다음 글에서는 Zod와 React Router를 조합한 파라미터 검증 방식을 소개해보려 한다.
'개발공부_Blog > Project' 카테고리의 다른 글
| Home 컴포넌트 ⇒ 중복 데이터 로딩, 단일 상태 관리로 해결하기 (1) | 2025.09.18 |
|---|---|
| 하드코딩 된 카테고리, 중앙 관리로 리팩토링[2] (0) | 2025.09.18 |
| Vercel로 [ Vite + React + Firebase ] 프로젝트 배포하기 (4) | 2025.08.17 |
| Firebase Authentication으로 로그인 구현하기 (6) | 2025.08.17 |
| React를 사용해서 만든 서비스를 어플로 다운 받을 수 있다고? ( PWA로 프로젝트 하기 ) (0) | 2024.12.11 |
댓글