본문 바로가기

React

[React] router

멀티페이지 어플리케이션 단점 

> 항상 새 콘텐츠를 가져와야 함

> 새로운 HTTP 요청을 전송하고 새로운 응답을 받는 과정에서 사용자의 흐름이 중단될 수도 있음 

> 지연이 발생할 수 있음 

> 사용자 경험 저하

> 복잡한 사용자 인터페이스 구축 시 싱글 페이지 어플리케이션 사용

 

SPA 

> 최초 HTML 요청 하나만 전송

> HTML 파일과 추가적인 JS 다운로드 되고 클라이언트에서 실행되는 추가 JS 코드는 사용자가 화면에서 보는 것들을 실제로 조절 

 

SPA에서 라우팅사용 

> 클라이언트 측에 리액트 코드 추가 가능하고 이것이 사용중이 URL을 감시 URL 이 변경될 떄마다 작동하여 URL 이 변경되면 화면에 다른 콘텐츠를 표시하게 됨

> 백엔드로부터 새 HTML 파일을 로딩하지 않고 약간의 클라이언트 측 코드를 추가하여 간단히 URL을 감시하다가 URL 이 변경되면 다른 리액트 컴포넌트를 로딩

 

라우터 주소 등록 방법

 

import { RouterProvider, createBrowserRouter } from "react-router-dom";
import HomePage from "./pages/Home.js";
import ProductsPage from "./pages/Products.js";
import RootLayout from "./pages/Root.js";
import ErrorPage from "./pages/Error.js";
import ProductDetailPage from "./pages/ProductDetail.js";

const router = createBrowserRouter([
 
  {
    path: "/",
    element: <RootLayout />,
    errorElement: <ErrorPage />,
    children: [
      { index: true, element: <HomePage /> },
      { path: "products", element: <ProductsPage /> },
      { path: "products/:productId", element: <ProductDetailPage /> },
    ],
  },
]);

function App() {
 // 위에서 생성한 router를 RouterProvider에 넣어준다 
  return <RouterProvider router={router} />;
}

export default App;

path : 도메인 뒤에 붙는 경로 ex) https://test.com 가 도메인이라고 치고 https://test.com/sample 이라는 주소를 만들고 싶으면 path : "/sample"을 입력 하면 됨

 

element : 관련한 컴포넌트를 import 해오면 됨

errorElement : 페이지에 오류났을 때 띄울 페이지 

children : 자녀 라우트 정의, path에 상대경로를 작성했는데 래퍼 라우트 경로 뒤에 첨부되는 것을 의미

index:true  부모 라우트가 현재 활성이면 표시되어야 하는 기본 라우트를 표시할 때 사용

ex) path:'products' 의 경우 https://test.com/products 

 

동적 path : /:를 붙임

 { path: "products/:productId", element: <ProductDetailPage /> },

ex) path:"products/:productId" 라고 작성을 하면 

 https://test.com/products/1 URL이 입력되었을 때

ProductDetailPage로 넘어감

 

 

 

 

래핑 라우트

import { Outlet } from "react-router-dom";
import MainNavigation from "../components/MainNavigation";

function RootLayout() {
  return (
    <>
      <MainNavigation />
      <main>
        <Outlet />
      </main>
    </>
  );
}
export default RootLayout;

Outlet 위치에는 자녀 컴포넌트를 띄우게 되고 

그걸 감싸는 RootLayout은 항상 자녀 컴포넌트의 컴포넌트 요소로 감싼 형태로 나타나게 된다.

이 컴포넌트에서 module.css적용시 자녀 컴포넌트에도 적용가능

 

 

 

다른 Path로 이동 방법

 

잘못된 방법

<a href="/">HOME</a>

 

이 웹사이트를 지원하는 서버에 새로운 요청을 하게 됨

그 서버는 SPA를 구성하는 싱글 HTML 을 제공 > 모든 JS 코드를 로딩 > 리액트 애플리케이션을 재시작

사이트 성능에 피해를 줌 > SPA의 장점을 잃게 됨

 

올바른 방법 > Link 사용

요청을 전송하는 기본 설정을 막고 리액트 라우터가 새 URL 을 알고 새 URL에 맞는 적절한 요소를 로딩하도록 해야함

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

function HomePage() {
  const navigate = useNavigate();

  function navigateHandler() {
    navigate("/products");
  }

  return (
    <>
      <h1>My Home Page</h1>
      <p>
        Go to
        <Link to="products">the list of products</Link>
      </p>
      <p>
        <button onClick={navigateHandler}>Navigate</button>
      </p>
    </>
  );
}

export default HomePage;

Link는 배후에서 앵커 요소를 렌더링, 요소에 대한 클릭을 감시

링크를 클릭했을 때 전송하는 브라우저 기본 설정을 막아줌

라우트 정의를 확인하여 그에 맞춰 페이지를 업데이트.

적절한 콘텐츠를 로딩, URL을 바꾸지만 새로운 HTTP요청은 전송하지 않음

 

강제 라우팅 : navigate()

 

 

NavLink

import { NavLink } from "react-router-dom";
import classes from "./MainNavigation.module.css";

function MainNavigation() {
  return (
    <header className={classes.header}>
      <nav>
        <ul className={classes.list}>
          <li>
            <NavLink
              to="/"
              className={({ isActive }) =>
                isActive ? classes.active : undefined
              }
              end
            >
              Home
            </NavLink>
          </li>
          <li>
            <NavLink
              to="/products"
              className={({ isActive }) =>
                isActive ? classes.active : undefined
              }
            >
              Products
            </NavLink>
          </li>
        </ul>
      </nav>
    </header>
  );
}

export default MainNavigation;

Link와 달리 className 프로퍼티 추가시에 실제로 문자열을 받는 일반적인 className 프로퍼티가 아닌 함수를 받는 프로퍼티. 그 함수는 앵커 태그에 추가되어야 하는 css 클래스 이름을 리턴 > 그 함수는 자동적으로 객체를 받음 > 여기에 isActive 속성을 할당 할 수 있음 

isActive 는 react-router-dom 에서 제공

현재 라우트가 활성인 경우 true 비활성인 경우 false

조건부 css 활용 가능

 

NavLink뒤에 end를 붙이면 현재 활성인 라우트 URL 뒤에 이 경로로 끝나고 그 뒤에 아무런 path 가 없을 시에만  이 링크를 활성으로만 간주함

 

동적 라우팅 

ProductsPage.jsx

import { Link } from "react-router-dom";

const PRODUCTS = [
  { id: "p1", title: "Product 1" },
  { id: "p2", title: "Product 2" },
  { id: "p3", title: "Product 3" },
];

function ProductsPage() {
  return (
    <>
      <h1>Products Page</h1>
      <ul>
        {PRODUCTS.map((prod) => (
          <li key={prod.id}>
            <Link to={prod.id}>{prod.title}</Link>
          </li>
        ))}
      </ul>
    </>
  );
}

export default ProductsPage;

 

ProductsDetail.js

import { useParams } from "react-router-dom";
import { Link } from "react-router-dom";
function ProductDetailPage() {
  const params = useParams();

  return (
    <>
      <h1>Product Detail Page</h1>
      <p>{params.productId}</p>
      <p>
        <Link to=".." relative="path">
          Back
        </Link>
      </p>
    </>
  );
}

export default ProductDetailPage;

parameter 사용 

useParams를 통해 가져올 수 있음 

params.productId(productId는 라우터 path생성 당시 path에서 지정한 파라미터 이름 ex) products/:productId 라고지정했을 때 productId에 속함)

 

Link to=".." : 활성이었던 경로의 한단계 위의 라우트로 돌아가라는 의미, 부모 path로 돌아감

Link to=".."  relative="path": 활성 중인 경로에서 한 세그먼트만 제거

 

절대경로

/로 시작

path가 항상 도메인 이름 뒤에서 부터 나타남

ex) path : "/"

ex) path : "/products"

 

 

상대경로

/를 빼고 시작

컴포넌트 내에서 상대경로를 입력하면 

활성중인 경로 뒤에 경로가 추가 됨

 

 

 

'React' 카테고리의 다른 글

[React] Router , action, defer()  (0) 2024.04.30
[React] router loader, useNavigation  (0) 2024.04.24
[React] Redux  (0) 2024.04.19
[React] Form Validation check  (0) 2024.04.18
[React] Custom Hook  (0) 2024.04.17