티스토리 뷰

기록

@react-router/dev의 자동 타입 추론

als982001 2025. 6. 22. 18:46

 최근, 개인적인 일 때문에 오랜만에 노마드코더 강의를 들었다. 강의를 듣다 보니, 기존에 놓쳤던 타입 추론 방식의 편리함을 다시금 깨달아서 간단히 정리하려 한다.

 

1. 상황

// app.routes.ts

import {
  type RouteConfig,
  index,
  prefix,
  route,
} from "@react-router/dev/routes";

export default [
  index("common/pages/home-page.tsx"),
  ...prefix("products", [
    index("features/products/pages/products-page.tsx"),
    ...prefix("leaderboards", [
      index("features/products/pages/leaderboard-page.tsx"),
      route(
        "/weekly/:year/:week",
        "features/products/pages/weekly-leaderboard-page.tsx"
      ),
      // 생략
    ]),
    ...prefix("categories", [
      index("features/products/pages/categories-page.tsx"),
      route("/:category", "features/products/pages/category-page.tsx"),
    ]),
    route("/search", "features/products/pages/search-page.tsx"),
    route("/submit", "features/products/pages/submit-page.tsx"),
    route("/promote", "features/products/pages/promote-page.tsx"),
  ]),
] satisfies RouteConfig;

 

우선 routes.ts가 위처럼 작성되어 있다.

 

// weekly-leaderboard-page.tsx

import { DateTime } from "luxon";
import type { Route } from "./+types/weekly-leaderboard-page";
import { data, isRouteErrorResponse, Link } from "react-router";
import { z } from "zod";
import { Hero } from "~/common/components/hero";
import { ProductCard } from "../components/product-card";
import { Button } from "~/common/components/ui/button";
import ProductPagination from "~/common/components/product-pagination";

const paramsSchema = z.object({
  year: z.coerce.number(),
  week: z.coerce.number(),
});

export const meta: Route.MetaFunction = ({ params }) => {
  // 생략
};

export const loader = ({ params }: Route.LoaderArgs) => {
  const { success, data: parsedData } = paramsSchema.safeParse(params);

	// 생략

  return {
    ...parsedData,
  };
};

export default function WeeklyLeaderboardPage({
  loaderData,
}: Route.ComponentProps) {
  // 생략
}

 

그리고 /weekly/:year/:week 경로에 대응하는 weekly-leaderboard-page.tsx 파일의 코드는 위와 같다.

 

 

이 때, loader의 params에 마우스를 올려보면 자동으로 위의 이미지처럼 타입이 뜬다.

 

 

그리고 WeeklyLeaderboardPage의 loaderData에 마우스를 올려보면 위의 이미지처럼 타입이 보인다. 하지만 나는 별도로 직접 타입을 지정하지 않았는데, 어떻게 알아서 타입이 지정되어 있는지 궁금했다. (혹은 까먹었다...)

 

2. loader의 params의 경우

route(
  "/weekly/:year/:week/:hahaha",
  "features/products/pages/weekly-leaderboard-page.tsx"
),

 

 실험적으로 routes.ts의 경로를 위처럼 바꿔보았다.

 

 

그 결과, loader 함수의 params에 자동으로 hahaha가 반영되었다. 즉, routes.ts에서 경로를 정의하면 params의 타입도 자동으로 추론된다는 것을 알 수 있다.

 

3. 컴포넌트의 loaderData의 경우

 

 

 하지만 loaderData에는 hahaha가 없는데, 이는 loader 함수 안에서 유효성 검사를 다음과 같이 했기 때문이다.

 

const paramsSchema = z.object({
  year: z.coerce.number(),
  week: z.coerce.number(),
});

...

export const loader = ({ params }: Route.LoaderArgs) => {
  const { success, data: parsedData } = paramsSchema.safeParse(params);

	// 생략

  return {
    ...parsedData,
  };
};

 

즉, params 전체에서 year, week만 파싱했고, loader의 return도 위처럼 구성했기 때문이다.

 

const paramsSchema = z.object({
  year: z.coerce.number(),
  week: z.coerce.number(),
  hahaha: z.string().optional(),
});

 

 

만약 위처럼 유효성 검사하는 부분을 수정하면, 제대로 반영되는 것을 확인할 수 있다.

 

4. 누가 자동 타입 추론을 해주었는가

 이 자동 타입 추론은 @react-router/dev 패키지 덕분에 가능한 것이었다.

라이브러리 자동 타입 추론 설명
react-router-dom ❌ 불가능 수동 타입 정의 필요
@react-router/dev ✅ 가능 routes.ts 기반으로 타입 자동 추론

 

내가 작성한 경로 선언(route(), prefix() 등)을 바탕으로 params, loaderData, Route.ComponentProps, Route.LoaderArgs 같은 모든 타입이 자동으로 유추된다.

 

5. 요약

  • @react-router/dev를 사용하면 라우팅 경로로부터 타입이 자동으로 생성된다.
  • routes.ts에서 경로를 수정하면, params 타입도 자동으로 반영된다.
  • loaderData는 loader 함수에서 리턴하는 값의 구조를 따르므로, 필요 데이터만 골라 넣으면 된다.
  • react-router-dom만 사용했다면 이런 자동 추론은 직접 구현하거나 수동 타입 지정이 필요했을 것이다.

여담으로, loaderData라는 이름은 react-rotuer에서 자동으로 주입하는 props로, 해당 라우트에서 export const loader로 반환된 데이터를 의미한다고 한다.

 


참고 문서

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함