Route Folder

Remix

목록 보기
3/5

개요

Remix의 모듈에 대해서 작성을 하려고했는데 어쩌다보니 Route에 관련해서 테스트를 진행해보았고, 그결과 Remix의 Route Folder 구조에 대해서 작성하고자 한다.

Remix의 공식홈페이지에 있는 글을 기반으로 번역하고 실습하였다.

그럼! 먼저 Remix의 Route 구조는 기본적으로 다음과 같다.

Root Basic

app/

├── routes/
│   ├── _index.tsx
│   └── about.tsx
└── root.tsx

root.tsx

먼저 root.tsx파일을 살펴보면 프로젝트의 루트 레이아웃을 역할을 하며, 다른 모든 경로는 <Outlet/>안에서 렌더링이 된다.

// root.tsx

import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

export default function Root() {
  return (
    <html lang="en">
      <head>
        <Links />
        <Meta />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

폴더 구조를 URL로 예를 들자면 아래 표와같이 동작을 한다고 생각하면 된다.

URL파일 경로
/app/routes/_index.tsx
/aboutapp/routes/about.tsx

Dot Delimiters

하나의 Route파일 이름에 .을 추가를 할 수 있는데 추가해보자

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.salt-lake-city.tsx
│   └── concerts.san-diego.tsx
└── root.tsx
URL파일 경로
/app/routes/_index.tsx
/aboutapp/routes/about.tsx
/concerts/trendingapp/routes/concerts.trending.tsx
/concerts/salt-lake-cityapp/routes/concerts.salt-lake-city.tsx
/concerts/san-diegoapp/routes/concerts.san-diego.tsx

Dynamic Segments

정적인 URL이 아닌 URL에 /about/1234와 같이 동적 값이 들어가는 경우

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.$city.tsx
│   └── concerts.trending.tsx
└── root.tsx
URL파일 경로
/app/routes/_index.tsx
/aboutapp/routes/about.tsx
/concerts/trendingapp/routes/concerts.trending.tsx
/concerts/12345app/routes/concerts.$id.tsx
/concerts/idtestapp/routes/concerts.$id.tsx

이때 동적으로 들어온 값은 loader함수의 params매개변수를 이용하여 가져올 수 있다.
자세한 내용은 참고하자. 나중에 추가

Nested Routes

개발을 하다보면 레이아웃도 사용하고 중첩 라우팅을 많이 사용하게 될텐데 그때의 구조는 다음과 같다

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts._index.tsx
│   ├── concerts.$city.tsx
│   ├── concerts.trending.tsx
│   └── concerts.tsx
└── root.tsx
URL파일 경로레이아웃
/app/routes/_index.tsxapp/root.tsx
/aboutapp/routes/about.tsxapp/root.tsx
/concertsapp/routes/concerts._index.tsxapp/routes/concerts.tsx
/concerts/trendingapp/routes/concerts.trending.tsxapp/routes/concerts.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsxapp/routes/concerts.tsx

레이아웃이 해제된 Nested Routes

URL은 중복이 되지만 레이아웃은 적용하지 싶지않을때 부모 세그먼트 끝에 _를 붙이는 다음과 같은 구조를 사용한다.

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.$city.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.tsx
│   └── concerts_.mine.tsx
└── root.tsx
URL파일 경로레이아웃
/app/routes/_index.tsxapp/root.tsx
/aboutapp/routes/about.tsxapp/root.tsx
/concerts/mineapp/routes/concerts_.mine.tsxapp/root.tsx
/concerts/trendingapp/routes/concerts.trending.tsxapp/routes/concerts.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsxapp/routes/concerts.tsx

Pathless Routes

중첩 URL이 없는 중첩 레이아웃이라고 하는데, 이게 무슨소리냐면 예로 /auth/login/auth/register처럼 사용하지 않고 /login/register만 사용하여 공통된 레이아웃을 공유하고 싶은 경우를 의미한다.

 app/
├── routes/
│   ├── _auth.login.tsx
│   ├── _auth.register.tsx
│   ├── _auth.tsx
│   ├── _index.tsx
│   ├── concerts.$city.tsx
│   └── concerts.tsx
└── root.tsx
URL파일 경로레이아웃
/app/routes/_index.tsxapp/root.tsx
/loginapp/routes/_auth.login.tsxapp/routes/_auth.tsx
/registerapp/routes/_auth.register.tsxapp/routes/_auth.tsx
/concertsapp/routes/concerts.tsxapp/root.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsxapp/routes/concerts.tsx

Optional Segments

url의 /kr/about/en/about과 같이 url의 경로가 선택적(동적)으로 바뀌는 경우

 app/
├── routes/
│   ├── ($lang)._index.tsx
│   ├── ($lang).$productId.tsx
│   └── ($lang).categories.tsx
└── root.tsx
URL파일 경로
/app/routes/($lang)._index.tsx
/categoriesapp/routes/($lang).categories.tsx
/en/categoriesapp/routes/($lang).categories.tsx
/fr/categoriesapp/routes/($lang).categories.tsx
/american-flag-speedoapp/routes/($lang)._index.tsx
/en/american-flag-speedoapp/routes/($lang). $productId.tsx
/fr/american-flag-speedoapp/routes/($lang). $productId.tsx

Splate Routes

슬래시를 포한한 URL의 나머지 부분과 일치할때

 app/
├── routes/
│   ├── _index.tsx
│   ├── $.tsx
│   ├── about.tsx
│   └── files.$.tsx
└── root.tsx
URL파일 경로
/app/routes/_index.tsx
/aboutapp/routes/about.tsx
/beef/and/cheeseapp/routes/$.tsx
/filesapp/routes/files.$.tsx
/files/talks/remix-conf_old.pdfapp/routes/files.$.tsx
/files/talks/remix-conf_final.pdfapp/routes/files.$.tsx
/files/talks/remix-conf-FINAL-MAY_2022.pdfapp/routes/files.$.tsx

폴더형식

app/
├── routes/
│   ├── _landing._index.tsx
│   ├── _landing.about.tsx
│   ├── _landing.tsx
│   ├── app._index.tsx
│   ├── app.projects.tsx
│   ├── app.tsx
│   └── app_.projects.$id.roadmap.tsx
└── root.tsx

위의 구조를 가진 파일형식의 파일구조를 폴더형식으로 변환하면 다음과 같은 구조로 변환된다.

혹시 route.tsx

app/
├── routes/
│   ├── _landing._index/
│   │   └── route.tsx
│   ├── _landing.about/
│   │   └── route.tsx
│   ├── _landing/
│   │   ├── footer.tsx
│   │   ├── header.tsx
│   │   └── route.tsx
│   ├── app._index/
│   │   └── route.tsx
│   ├── app.projects/
│   │   └── route.tsx
│   ├── app/
│   │   ├── footer.tsx
│   │   ├── primary-nav.tsx
│   │   └── route.tsx
│   └── app_.projects.$id.roadmap/
│        └── route.tsx
└── root.tsx

추가로 아래의 같은 경우에는 동일한 라우터로 인식을 한다고한다.

# these are the same route:
app/routes/app.tsx
app/routes/app/route.tsx

# as are these
app/routes/app._index.tsx
app/routes/app._index/route.tsx

⚠️ 이때 주의할점은 폴더 형식으로 Nested Routes 구성하는 경우 바로 routes폴더 바로 하위에 있는 폴더만 경로로 등록된다고 한다. 예로 app/routes/about/header/route.tsx는 무시되고 app/routes/about/route.tsx까지만 경로로 등록된다고 한다. 문서 안보고 이것저것 해보다가 개고생함

폴더 형식 실습

폴더로 변환하는 형식이 이해가 안가서 실습을 진행해 보았는데 중첩으로 라우터를 사용하는 URL들이 독립적으로 동작하는 것과 레이아웃을 포함하는 라우터를 실습해보았다.

독립적 동작

폴더구조는 다음과 같다.

app/
├── routes/
│   ├── loader-and-action._index/
│   │   └── route.tsx
│   └── loader-and-action.$id/
│       └── route.tsx
└── root.tsx

/loader-and-action경로로 들어 갔을때는 독립적인 loader-and-action만 표출하고, /loadfer-and-action/1 경로만 표출한다.

loader-and-action._index/route.tsx

// app/rotues/loader-and-action.$id/route.tsx
export default function LoaderAndAction() {
    return <div>단독 페이지 입니다. </div>
}

loader-and-action.$id/route.tsx

// app/routes/loader-and-action/$id.tsx
import { LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export async function loader({ params }: LoaderFunctionArgs) {
  console.log("params:", params); // 디버깅용
  
  return {
    id: params.id
  };
}

export default function IdPage() {
  const { id } = useLoaderData<typeof loader>();
  
  return (
    <div>
      <h1>ID: {id}</h1>
    </div>
  );
}

결과

/loader-and-actionloader-and-action/1

레이아웃 포함

app/
├── routes/
│   ├── root-outlet/
│   │   └── route.tsx
│   └── root-outlet.$id/
│       └── route.tsx
└── root.tsx

/root-outlet경로로 들어 갔을때는 부모 레이아웃을 표출하고, /root-outlet/1 경로로 들어갔을때는 부모와 자식 모두 표출한다.

root-outlet/route.tsx

// app/rotues/root-outlet/route.tsx
import { Outlet } from "@remix-run/react";

export default function Page() {
    return (
      <div>
        루트입니다.
        <Outlet/>
      </div>
    );
  }

root-outlet.$id/route.tsx

// app/rotues/root-outlet.$id/route.tsx
export default function Page() {
    return (
      <div>
        루트의 자식입니다.
      </div>
    );
  }

결과

/root-outlet/root-outlet/1

이때 route.tsx대신 index.tsx를 사용해도 정상 동작을 했는데, Claude에게 물어봤더니 차이점이 있다고 한다. 실제로 사용하면서 차이점을 알아야 할것같다.

- route.tsx
1. 레이아웃 컴포넌트 역할
2. 자식 라우트들의 공통 레이아웃 제공
3. 반드시 <Outlet />을 포함해야 함

- index.tsx 또는 _index.tsx
1. 인덱스 라우트 (기본 페이지)
2. 해당 경로의 메인 페이지 역할

코드만 보았을때는 <Outlet/>때문에 부모가 자식을 포함하는것처럼 보이는데 loader-and-action._index/route.tsx에서 <Outlet/>을 사용해도 자식이 표출이 안된다. 폴더명을 자세히보자

폴더 형식 실습 추가(2025. 07. 30)

Nested Route

중첩 라우터를 만들때는 어떻게 폴더 구조를 가져가야하는지 실습해보았다.

app/
├── routes/
│   ├── nested/
│   │   └── index.tsx
│   └── nested.depth1/
│       └── route.tsx
│   └── nested.depth1.depth2/
│       └── route.tsx
└── root.tsx

이렇게 폴더를 만들어서 구성을 해주어야 함

Pathless Route

pathless의 경우에는 아래와 같은 폴더 구조로 만들어줘야 한다.

app/
├── routes/
│   ├── _auth.signup/
│   │   └── route.tsx
│   └── _auth.login/
│       └── route.tsx
└── root.tsx

Remix v1에서는 pathless route를 __auth 형태로 설정해주었지만 v2에서는 _auth형태로 언더바가 하나 빠진 형태로 폴더구조를 구성해주면 된다.

Dynamic Route

Dynamic Route의 경우에 는 다음과 같은 구조로 만들어주면 된다.

app/
├── routes/
│   ├── dynamic/
│   │   └── route.tsx
│   └── dynamic.$id/
│   │   └── route.tsx
│   └── dynamic.$id.$code/
│       └── route.tsx
└── root.tsx

https://remix.run/docs/en/main/file-conventions/routes#folders-for-organization

profile
업무하면서 쌓인 노하우를 정리하는 블로그🚀 풀스택 개발자를 지향하고 있습니다👻

0개의 댓글