# Routes

## 라우터란?

> 라우팅을 구현하는 도구
>
> URL과 컴포넌트 간의 매핑을 관리하고, URL 변경에 따라 컴포넌트를 렌더링 시키는 역할을 한다.

## React Router

> React에서 라우팅을 구현하는데 사용되는 라이브러리

### Install

```bash
npm i react-router-dom
```

### Browser Router

> 브라우저에 내장된 히스토리를 탐색 및 제어하기 위해 내부적으로 History API를 사용하여 CSR 라우팅을 구현

* URL과 매핑되는 페이지 컴포넌트를 렌더링
* history 객체를 사용해서 뒤로가기, 앞으로가기, 새로고침등의 동작을 처리

```tsx
declare function BrowserRouter(
  props: BrowserRouterProps
): React.ReactElement;

interface BrowserRouterProps {
  basename?: string;
  children?: React.ReactNode;
  window?: Window;
}
```

{% hint style="info" %}
테스트 환경에서는 window 객체에 접근할 수 없기 때문에, BrowserRouter를 사용하는 경우 테스트가 실패할 수 있다. 따라서 페이지 외부(main.js 등)에서 라우팅 처리를 해야 한다.
{% endhint %}

```tsx
// main.js
root.render(
    <StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </StrictMode>,
  );
};
```

### Memory Router

> Test 환경에서 `BrowserRouter` 대신 `MemoryRouter`를 사용하여 라우팅 환경 구축한다.
>
> 말 그대로 메모리 내에서 라우팅

```tsx
declare function MemoryRouter(
  props: MemoryRouterProps
): React.ReactElement;

interface MemoryRouterProps {
  basename?: string;
  children?: React.ReactNode;
  initialEntries?: InitialEntry[];
  initialIndex?: number;
}
```

* **initialEntries** → 초기 엔트리 목록 설정, 즉 테스트할 페이지의 첫 렌더링 경로 설정
  * 배열인 이유는 브라우저 히스토리 스택과 동일, (뒤록가기, 앞으로가기 테스트를 하기 위함)
* **initialIndex** → 초기 엔트리 목록\[배열] 중 인덱스를 통해 경로를 선택

```tsx
import { MemoryRouter } from 'react-router-dom';

describe('App', () => {
  it('render correctly', () => {
    render(
      <MemoryRouter>
        <Routes>
          <Route path="/" element={<App />} />
          <Route path="/dashboard" element={<DashBoard />} />
        </Routes>
      </MemoryRouter>,
    );

    expect(screen.getByText(/이다잉/)).toBeInTheDocument();
  });
});
```

#### 함수로 추출해서 재사용성을 높혀보자

```tsx
export function withRouter(routes, initialEntries = '/') {
  return (
    <MemoryRouter initialEntries={[initialEntries]}>
      <Routes>
        {routes}
      </Routes>
    </MemoryRouter>
  )
}

describe('App', () => {
  it('render correctly', () => {
    render(withRouter(
    <>
      <Route path="/" element={<App />} />
      <Route path="/dashboard" element={<DashBoard />} />
    </>
    ));

    expect(screen.getByText(/이다잉/)).toBeInTheDocument();
  });
});
```

### Routes

> 여러 경로의 라우팅 규칙을 처리하는 컨테이너
>
> 자식으로 여러 `<Route />` 컴포넌트를 갖는다.

```tsx
interface RoutesProps {
  children?: React.ReactNode;
  location?: Partial<Location> | string;
}

<Routes location>
  <Route />
</Routes>;
```

* **children** → 자식으로 `<Route/>` 컴포넌트만 받을 수 있다.
* **location** → 현재 위치를 명시적으로 제공, 생략시 현재 브라우저 URL 기준으로 처리

```tsx
<Routes>
  <Route path="/" element={<HomePage />} />
  <Route path="/dashboard" element={<DashboardPage />} />
  <Route path="*" element={<NotFound />} />
</Routes>
```

* location이 변경될 때마다 하위 자식들의 경로를 확인하여 가장 일치하는 경로를 찾아 해당 분기를 렌더링한다
* 매칭되는 라우팅 규칙이 없을 때 특정 컴포넌트를 렌더링 할 수 있다. (catch-all 라우팅)
* 중첩하여 라우팅 규칙을 설정할 수 있다.

### Route

> 경로와 매칭되는 컴포넌트를 선언적으로 지정

```tsx
interface RouteObject {
  path?: string;
  index?: boolean;
  children?: React.ReactNode;
  caseSensitive?: boolean;
  id?: string;
  loader?: LoaderFunction;
  action?: ActionFunction;
  element?: React.ReactNode | null;
  Component?: React.ComponentType | null;
  errorElement?: React.ReactNode | null;
  ErrorBoundary?: React.ComponentType | null;
  handle?: RouteObject["handle"];
  shouldRevalidate?: ShouldRevalidateFunction;
  lazy?: LazyRouteFunction<RouteObject>;
}
```

* **path** → 컴포넌트와 연결할 경로 지정, `:`로 시작하는 경로를 `Dynamic Segments`라 한다.
* **index** → 인덱스 경로 여부 지정
* **caseSensitive** → 경로의 대소문자를 일치시킬지 여부 (default false)
* **element** / **Component** → 경로와 연결할 컴포넌트를 지정한다.

{% hint style="info" %}
**Dynamic Segmenets**란?

동적 경로의 매개변수(params)를 나타내는 라우트 경로의 일부, `:` 기호를 사용하여 매개변수를 표현한다.

```tsx
<Route
  path="/teams/:teamId"
  element={<Team />}
/>;

// Team
export default function TeamPage() {
const params = useParams();
  // path: /teams/hotspur 인경우 params.teamId => hospur
  // path: /teams/real 인경우 params.teamId => real
  return <p>{`params: ${params.teamId}`}</p>
}
```

{% endhint %}

{% hint style="info" %}
**Index Route**란?

index 경로이기 때문에 path를 설정하지 않아도 된다.

부모 경로가 일치하지만 자식 경로중 일치하는 것이 없을 때에 렌더링되는 기본 자식 경로이다.
{% endhint %}

### Outlet

> 라우터에 매칭된 하위의 다른 라우트 컴포넌트가 위치할 영역을 지정 (Slot 개념)

일반적으로 레이아웃 컴포넌트에 활용된다.

```tsx
export default function Layout() {

  return (
    <>
      <header>Header</header>
      <main>
        <Outlet />
      </main>
      <footer>Footer</footer>
    </>
  );
};

```

### &#x20;Layout Route

> 라우트 매칭에 참여하지 않는 껍데기용 라우트

```tsx
<Routes>
  <Route element={<PageLayout />}> // Layout
    <Route path="/privacy" element={<Privacy />} />
    <Route path="/tos" element={<Tos />} />
  </Route>
  <Route path="contact-us" element={<Contact />} />
</Routes>

// '/privacy' 경로에서 렌더링된 결과물
<PageLayout>
  <Privacy />
</PageLayout>
```

{% hint style="warning" %}
LayoutRoute Component는 반드시  하위 경로 요소를 렌더링할 \<Outlet> 을 추가 해주어야 한다.

Children을 사용하면 예상대로 동작하지 않을 수 있다.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/dev_road/week-7/routes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
