Javascript/React

리액트 라우터

kyoulho 2024. 8. 1. 11:35

react-router는 React 애플리케이션에서 클라이언트 사이드 라우팅을 처리할 수 있게 도와주는 라이브러리이다.

React Router의 주요 기능

  1. 경로 기반 라우팅: URL 경로에 따라 다른 컴포넌트를 렌더링 한다.
  2. 네스티드 라우팅: 라우트 안에 라우트를 중첩하여 복잡한 내비게이션을 구현한다.
  3. 동적 라우팅: URL 매개변수를 사용하여 동적인 콘텐츠를 처리한다.
  4. 프로그램적 내비게이션: 코드에서 직접 내비게이션을 제어할 수 있다.
  5. 라우트 보호: 인증 및 권한 관리를 통해 특정 라우트 접근을 제어한다.

설치

npm i react-router

 

Routes, Route, Link


Routes

Routes는 React Router v6에서 도입된 새로운 컴포넌트로, 라우트의 집합을 정의하는 역할을 한다. 이전 버전에서는 Switch를 사용했지만, v6에서는 Routes가 이를 대체한다. Routes는 하위에 있는 Route 컴포넌트들의 경로를 기반으로 가장 적합한 컴포넌트를 렌더링 한다.

Route

Route는 특정 URL 경로에 대해 어떤 컴포넌트를 렌더링 할지 정의하는 컴포넌트이다. 단독으로 사용할 수 없고 Routes 안에서 사용되어야 한다. 각 Route는 path와 element를 속성으로 가진다.

라우트를 설정할 때 라우트 경로는 다음처럼 콜론을 붙일 수 있는데, 이처럼 콜론을 앞에 붙인 심벌을 라우트 변수라고 한다.

<Route path="/board/card/:cardId" element={<Card />} />

 

Link

Link는 사용자가 클릭할 수 있는 링크를 생성하여 페이지 간의 내비게이션을 도와준다. HTML의 <a> 태그와 유사하지만, 페이지를 새로고침하지 않고 클라이언트 사이드에서 내비게이션을 처리한다.

예제

NoMatch.tsx

import {useNavigate} from "react-router-dom";
import {useCallback} from "react";
import {Button} from "../theme/daisyui";

export default function NoMatch() {
    const navigate = useNavigate();

    const goBack = useCallback(() => {
        navigate(-1)
    }, []);

    return (
        <div className={"flex flex-col p-4"}>
            <p className={"text-xml text-center p-4 alert alert-error"}>Oops! No page found!</p>
            <div className={"flex justify-center mt-4"}>
                <Button className={"ml-4 btn-primary btn-xs"} onClick={goBack}>
                    GO BACK
                </Button>
            </div>
        </div>
    )
}

Home.tsx

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

type HomeProps = {
    title?: string
}

const Home: FC<HomeProps> = ({title}) => {
    return (
        <div>
            <div className={"flex bg-gray-200 p-4"}>
                <Link to={"/"}>Home</Link>
                <Link to={"welcome"} className={"ml-4"}>
                    Welcome
                </Link>
                <Link to={"/board"} className={"ml-4"}>
                    Board
                </Link>
            </div>
            <p className={"text-bold text-center text-xl"}>{title ?? 'Home'}</p>
        </div>
    )
}

export default Home

RoutesSetup.tsx

import {Route, Routes} from "react-router-dom";
import NoMatch from "./NoMatch";
import Home from "./Home";
import Board from "../pages/Board";

export default function RoutesSetup() {
    return (
        <Routes>
            <Route path={"/"} element={<Home/>}/>
            <Route path={"/welcome"} element={<Home title={"Welcome to our site"}/>}/>
            <Route path={"/board"} element={<Board/>}/>
            <Route path={"*"} element={<NoMatch/>}/>
        </Routes>
    )
}

App.tsx

import type React from 'react'
import './App.css'
import {useStore} from "./store";
import {Provider as ReduxProvider} from 'react-redux'
import {HTML5Backend} from "react-dnd-html5-backend";
import {DndProvider} from 'react-dnd';
import {BrowserRouter} from "react-router-dom";
import RoutesSetup from "./routes/RoutesSetup";


export default function App() {
    let store = useStore();
    return (
        <ReduxProvider store={store}>
            <DndProvider backend={HTML5Backend}>
                <BrowserRouter>
                    <RoutesSetup/>
                </BrowserRouter>
            </DndProvider>
        </ReduxProvider>
    )
}

 

useNavigate


useNavigate 훅은 navigate 함수를 얻는 데 사용한다.

navigate 함수는 매개변수로 전달한 경로로 이동하게 해 준다. 또한 window.history 객체의 go 메서드처럼 -1과 같은 숫자로 '뒤로 가기' 등의 효과를 줄 수도 있다.

const navigate = useNavigate();
const cardClicked = useCallback(
    (cardId: string) => () => {
        navigate(`/board/card/${cardId}`)
    }, [navigate]);

 

useLocation


useLocation 훅은 location 객체를 얻는 데 사용한다. location 객체는 브라우저가 기본으로 제공하는 window.location과 개념적으로 비슷하나 같지는 않다.

location 객체

  • pathname: 현재 URL의 경로 (/home, /about, 등)
  • search: 쿼리 문자열 (? sort=asc&filter=active)
  • hash: URL의 해시 부분 (#section1)
  • state: 라우트 상태 객체 (선택 사항, Link 컴포넌트로 전달된 상태)
import React from 'react';
import { useLocation } from 'react-router-dom';

const LocationDisplay = () => {
  const location = useLocation();

  return (
    <div>
      <p>Pathname: {location.pathname}</p>
      <p>Search: {location.search}</p>
      <p>Hash: {location.hash}</p>
      <p>State: {JSON.stringify(location.state)}</p>
    </div>
  );
};

 

useParams


useParams 훅은 URL 경로에서 동적 매개변수(params)를 가져오는 데 사용된다. URL에 포함된 변수들을 추출할 수 있다.

아래 예제는 URL 경로가 /user/:userId일 때, userId 매개변수를 URL에서 추출하여 사용할 수 있습니다.

import React from 'react';
import { useParams } from 'react-router-dom';

const UserProfile = () => {
  const { userId } = useParams();

  return (
    <div>
      <h1>User Profile</h1>
      <p>User ID: {userId}</p>
    </div>
  );
};

 

useSearchParams


useSearchParams 훅은 URL의 쿼리 문자열을 읽고 수정하는 데 사용된다. URLSearchParams 객체를 반환하여 쿼리 파라미터를 쉽게 처리할 수 있다.

주요 메서드

  • get: 쿼리 파라미터의 값을 가져온다.
  • set: 쿼리 파라미터의 값을 설정한다.
  • delete: 쿼리 파라미터를 삭제한다.
import React from 'react';
import { useSearchParams } from 'react-router-dom';

const SearchExample = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  // 쿼리 파라미터 sort, filter 의 값을 얻을 수 있다
  const sort = searchParams.get('sort');
  const filter = searchParams.get('filter');

  const handleSortChange = () => {
    setSearchParams({ sort: 'desc', filter: filter || 'active' });
  };

  return (
    <div>
      <h1>Search Example</h1>
      <p>Sort: {sort}</p>
      <p>Filter: {filter}</p>
      <button onClick={handleSortChange}>Change Sort Order</button>
    </div>
  );
};

 

Outlet 컴포넌트와 중첩 라우팅


Outlet 컴포넌트는 React Router의 v6에서 도입된 기능으로, 중첩된 라우트를 구현하는 데 사용된다. 다른 컴포넌트들이 렌더링 되는 위치를 지정해 주는 역할을 한다.

Outlet에 보이기 원하는 컴포넌트는 Outlet을 사용하는 컴포넌트의 자식 라우터로 구성한다.

아래 예제에서 NoMatch는 Outlet의 자식 라우터로 설정되는데, 리액트 라우터에서는 이를 중첩 라우트 설정이라고 한다. 

import {Outlet} from "react-router-dom";
import NavigationBar from "./NavigationBar";
import Footer from "./Footer";

export default function Layout() {
    return (
        <>
            <NavigationBar/>
            <Outlet/>
            <Footer/>
        </>
    )
}
import {Route, Routes} from "react-router-dom";
import NoMatch from "./NoMatch";
import Layout from "./Layout";
import Board from "../pages/Board";

export default function RoutesSetup() {
    return (
        <Routes>
            <Route path={"/"} element={<Layout/>}>
                <Route path={"/board"} element={<Board/>}/>
                <Route path={"*"} element={<NoMatch/>}/>
            </Route>
        </Routes>
    )
}

 

색인라우터

Route 컴포넌트의 IndexRoute (React Router v5 이하) 또는 index (React Router v6) 속성은 부모 라우트에 대한 기본 경로를 설정하는 데 사용된다. 이를 통해, 부모의 경로로 들어온 요청을 라우팅 할 기본 컴포넌트를 지정할 수 있다.

import {Route, Routes} from "react-router-dom";
import NoMatch from "./NoMatch";
import Layout from "./Layout";
import Board from "../pages/Board";
import LandingPage from "./LandingPage";

export default function RoutesSetup() {
    return (
        <Routes>
            <Route path={"/"} element={<Layout/>}>
                <Route index element={<LandingPage/>}/>
                <Route path={"/board"} element={<Board/>}/>
                <Route path={"*"} element={<NoMatch/>}/>
            </Route>
        </Routes>
    )
}

 

useResolvedPath & useMatch


useResolvedPath

useResolvedPath 훅은 주어진 경로를 현재 위치에 상대적으로 해석(resolved)된 경로 객체를 반환한다. 이 객체에는 pathname, search, hash 등의 속성이 포함되어 있다. 내비게이션 링크나 경로 관련 로직에서 상대 경로를 절대 경로로 해석할 때 사용된다.

useMatch

useMatch 훅은 주어진 경로 패턴이 현재 URL과 일치하는지 확인하고, 일치하는 경우 일치 결과 객체를 반환gks다. 이 객체에는 일치된 경로와 매개변수(param) 정보가 포함되어 있다. 특정 경로 패턴과 현재 URL의 일치 여부를 확인하여 조건부 렌더링이나 경로 기반 로직을 처리할 때 사용된다.

import type {FC} from "react";
import type {LinkProps as RRLinkProps} from "react-router-dom";
import {Link as RRLink, useMatch, useResolvedPath} from "react-router-dom";

export type LinkProps = RRLinkProps & {}

export const Link: FC<LinkProps> = ({className: _className, to, ...props}) => {
    // useResolvedPath 훅을 사용해 'to' 경로를 현재 경로를 기준으로 해석
    const resolved = useResolvedPath(to);

    // useMatch 훅을 사용해 현재 경로와 해석된 경로가 일치하는지 확인
    // 'end: true'는 정확히 일치해야 함을 의미
    const match = useMatch({path: resolved.pathname, end: true});

    // className을 결정, 경로가 일치하면 'btn-active' 클래스를 추가
    const className = [_className, match ? 'btn-active' : ''].join(' ');

    // react-router-dom의 Link 컴포넌트를 반환, className과 기타 props를 전달
    return <RRLink to={to} className={className} {...props}/>
};

'Javascript > React' 카테고리의 다른 글

공개 라우트와 비공개 라우트  (0) 2024.08.01
React Beautiful DnD 라이브러리  (0) 2024.07.31
React DnD 라이브러리  (0) 2024.07.31
리덕스 미들웨어  (0) 2024.07.29
리듀서 활용하기  (0) 2024.07.22