본문 바로가기
개발공부_Blog/Project

React Router + Zod: 안전한 params 검증과 useLoaderData 활용법

by 소팡팡 2025. 9. 22.

React Router에서 Zod로 안전한 params 검증하기

토이 프로젝트를 하다 보면 useParam()을 사용해 URL 파라미터(:id, :category)를 그대로 사용하는 경우가 많았습니다.

실제 서비스라면 사용자가 잘못된 URL을 입력하거나 예상하지 못한 값이 들어올 수도 있을텐데 말입니다!!!

그래서 런타임에서 파라미터를 검증하는 로직이 있어야 한다고 생각했고 검색을 하기 시작했습니다.

  • param을 검증할 수 있는 라이브러리인 zod
  • 검증된 param을 컴포넌트에 전달해 사용할 수 있게 하는 React-router-dom의 loader

이번 글에서는 Zod + React Router의 loader를 활용해 params를 검증하고, 검증된 데이터를 컴포넌트에서 안전하게 사용하는 패턴을 정리해 보겠습니다.

 

 

공부한 내용

  • Zod: 런타임 스키마 검증 라이브러리. 타입스크립트와 잘 맞아서, params 같은 입력값을 안전하게 다룰 수 있음.
  • React Router의 loader: 라우트가 렌더링되기 전에 실행되는 함수.
    • 데이터를 미리 불러오거나
    • 잘못된 경우 예외를 던져서 라우팅 단계에서 차단할 수 있음.

 

1. Zod 스키마로 params 정의하기

예를 들어, 상세 페이지(/detail/:id)의 id는 20~40자의 영문/숫자/언더스코어/하이픈으로 제한한다고 가정합니다.

Zod로 스키마를 선언하면 다음과 같이 됩니다:

import { z } from "zod";

export const IdSchema = z
  .string()
  .regex(/^[A-Za-z0-9_-]{20,40}$/, "Invalid id");

// /detail/:id , /detail/:id/edit
export const ParamDetailSchema = z.object({ id: IdSchema });

 

 

2. loader로 params 검증하기

React Router의 loader는 라우트가 렌더링 되기 전에 실행되는 함수입니다.

주요 특징은 다음과 같습니다:

  • 데이터 프리패치: 컴포넌트 렌더링 전에 데이터를 불러와 준비할 수 있습니다.
  • 예외 처리: 잘못된 경우 throw new Response()로 404 같은 에러를 발생시켜 라우팅 단계에서 차단합니다.
  • 데이터 전달: loader가 반환한 값은 컴포넌트에서 useLoaderData()로 꺼내 쓸 수 있습니다.

현재 예제에서는 데이터를 미리 불러오지 않고, params 검증 전용으로 loader를 활용했습니다.

export const notFound = () => {
  throw new Response("페이지를 찾을 수 없습니다", { status: 404 });
};

export const mustParse = <T>(schema: {parse: (v: unknown) =>T},value: unknown) => {
  try {
    return schema.parse(value);
  } catch {
    notFound();
  }
};

const loadDetail = ({ params }: { params: unknown }) => {
  return mustParse(ParamDetailSchema, params); // 검증 성공 시 반환
};

 

 

3. 라우터 설정에 loader 적용하기

라우터에 loader를 연결하면, 해당 경로 진입 시 params 검증 → 실패 시 NotFound 처리가 자동으로 수행됩니다.

여기서 errorElement로 지정한 <NotFound/> 컴포넌트는 loader나 렌더링 중 에러가 발생했을 때 보여줄 화면입니다.

⇒ 올바르지 않은 URL로 들어오면 404가 뜨게 됩니다!

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    errorElement: <NotFound />,
    children: [
      { index: true, element: <Navigate to={ROUTES.HOME} replace /> },
      { path: ROUTES.DETAIL, loader: loadDetail, element: <Detail /> },
      // ...
    ]
  }
]);

 

 

4. 컴포넌트에서 검증된 params 사용하기

이제 컴포넌트에서는 useParams()로 문자열을 직접 꺼내 쓰지 않고,

loader가 반환한 검증된 params를 useLoaderData()로 받습니다.

import { useLoaderData, useNavigate } from "react-router-dom";

export const Detail = () => {
  const navigate = useNavigate();
  const { id } = useLoaderData() as { id: string }; // ✅ 검증된 값
  console.log("검증된 id:", id);
  // ...
};

 

 

지금까지는 useParams()로 문자열을 그대로 받아 썼는데,

zod로 검증된 params을 useLoaderData로 받아와 컴포넌트에서 쓰니 코드의 신뢰성이 높아지는 느낌이었습니다.

 

아직은 작은 개인 프로젝트지만,

분명!!!! 실무에서도 URL 파라미터가 중요한 경우가 많을 것이라는 생각이 듭니다.

그렇다면 이런 검증 패턴이 많이 쓰이겠지요 ㅎㅎㅎ 이번 개발은 실무를 떠올려보는 좋은 연습시간이었습니다.

 

다음은 React Query 같은 데이터 패칭과 함께 loader를 어떻게 조합할지 고민해보고 연습하는 시간을 가져야겠습니다.

 

댓글