[React] React-Router

Main·2023년 11월 16일
0

React

목록 보기
9/31
post-thumbnail

React Router?

React Router은 React에서 웹 애플리케이션의 라우팅을 관리하기 위한 라이브러리입니다.

웹 페이지에서 라우팅이란 사용자가 서로 다른 경로나 URL을 방문할 때 해당 경로에 맞는 적절한 뷰 혹은 컴포넌트를 보여주는 기능을 의미합니다.

사용자가 웹 사이트의 '홈페이지', '서비스 소개', '연락처' 등 다양한 페이지를 방문할 때마다 적절한 화면을 보여주는 것이 라우팅이고, 이를 React에서 구현할 수 있도록 도와주는 것이 React Router입니다.

React Router를 사용하는 이유

  1. 싱글 페이지 애플리케이션(SPA) 구현: React Router를 사용하면, 페이지를 새로 로딩하지 않고도 뷰를 바꿀 수 있습니다. 이는 사용자 경험을 향상시키며, 애플리케이션의 퍼포먼스를 개선하는 데 도움이 됩니다.
  2. 코드 분리 및 지연 로딩: React Router를 사용하면, 각 라우트에 대응하는 컴포넌트를 분리하여 코드를 관리할 수 있습니다. 또한, 필요한 라우트의 컴포넌트만 로딩하여 애플리케이션의 초기 로딩 시간을 줄일 수 있습니다.
  3. URL을 통한 상태 관리: URL을 통해 애플리케이션의 상태를 관리할 수 있습니다. 이를 통해 사용자가 브라우저의 뒤로 가기, 앞으로 가기 버튼을 사용하거나 즐겨찾기를 통해 특정 상태로 쉽게 돌아갈 수 있습니다.
  4. 프레임워크에 종속되지 않은 라우팅: React Router는 React에 종속되지 않은 독립적인 라우팅 라이브러리입니다. 이는 다른 라이브러리나 프레임워크와 함께 사용할 수 있음을 의미합니다.

따라서, React Router는 복잡한 애플리케이션의 라우팅을 효과적으로 관리하고, 사용자 경험을 향상시키는 데 필요한 도구입니다.

Router의 종류

1 ) BrowserRouter

웹 애플리케이션에서 가장 일반적으로 사용되는 라우터입니다. HTML5의 History API를 사용하여 UI를 업데이트합니다.

BrowserRouter는 HTML5의 History API를 사용하여 클린한 URL을 제공합니다. 즉, URL에 해시(#)가 포함되지 않습니다. 이는 사용자에게 보다 친숙하고, SEO에도 유리합니다.

사용자가 직접 URL을 입력하거나 새로고침을 했을 때도 기대하는 페이지를 보여줄 수 있습니다.

BrowserRouter를 사용할 때는 서버에서 모든 요청을 애플리케이션의 진입점으로 리다이렉트하는 설정이 필요합니다. 그렇지 않으면, 사용자가 직접 URL을 입력하거나 새로고침을 했을 때 404 에러가 발생할 수 있습니다.

2 ) HashRoter

Hash-based URL을 사용하는 라우터입니다.

서버 설정 없이 간편하게 사용할 수 있습니다. 해시(#) 부분은 브라우저만이 관리하므로, 서버는 항상 루트 페이지만을 제공하면 됩니다.

History API를 지원하지 않는 브라우저에서도 잘 동작합니다.

URL에 해시(#)가 포함되어 있습니다. 이는 URL이 다소 어색하게 보일 수 있으며, SEO에도 불리할 수 있습니다.

2. React Router Dom

React를 위한 라우팅 라이브러리인 react-router의 웹 버전이라고 할 수 있습니다. 웹 애플리케이션에서 사용되는 라우팅 컴포넌트들을 제공합니다.

React Router Dom 라우팅

1 ) 최상단 <BrowserRouter> 컴포넌트로 감싸기

react-router-dom에 내장되어 있는 BrowserRouter라는 컴포넌트를 사용하여 감싸면 됩니다.

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

2 )  , 컴포넌트로 감싸기

<Routes> 컴포넌트는 여러 Route를 감싸서 그 중 일치하는 라우트 단 하나만을 렌더링 시켜주는 역할을 합니다.

<Route>는 path속성에 경로, element속성에는 컴포넌트를 넣어 줍니다. 

<Route> 를 이용하여 중첩 라우팅을 할 수 있습니다.

<Route></Route> 사이에 하위 라우터를 넣으면됩니다. 하위 라우터의 컴포넌트는 상위 라우터에서 <Outlet/> 컴포넌트를 통해 가져올 수 있습니다.

부모 라우트의 경로와 정확히 일치하는 경로에서 렌더링되는 컴포넌트에는 ****index 속성을 넣어주면 됩니다. 즉, Main 컴포넌트는 URL이 / 일 때 렌더링 됩니다.

URL 파라미터별로 페이지를 동적으로 생성할 때에는 path을 /product/:productId 과같이 사용하며, :productId 부분은 실제로는 사용자가 입력하는 값으로 대체됩니다. productId 값은 Product 컴포넌트 내에서 useParam()를 사용하여 가져올 수 있습니다.

어떤 경로와 일치하지않는 페이지를 설정할 때는 마지막 <Route>의 path=""를 넣어서 설정합니다. 위의 라우터의 경로와 일치하는 라우터가 없는 경우 (모든 경로)를 NotFound로 처리한다는 의미입니다.

app.js

import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import Main from './Main';
import Product from './Product';

function App() {
	const [isLogin, setIsLogin] = useState(false);
	const LoginContext = createContext(); 
	
  return (
    <Routes>
      <Route path='/' element={<Layout />} >
        <Route index element={<Main />} />
        <Route path='/product/:productId' element={<Product />} />
				<Route path="*" element={<NotFound />}></Route>
      </Route>
    </Routes>
  );
}
export default App;

Layout.js

<Outlet/> 컴포넌트는 하위 라우터의 컴포넌트를 의미합니다.

아래 에서는 <header> <footer> 사이에 Layout 컴포넌트 라우터의 하위 라우터 컴포넌트가 렌더링 됩니다.

<Link/> : 사용자가 클릭하면 다른 위치로 이동하는 컴포넌트입니다. 페이지를 새로고침하지 않고 애플리케이션의 일부분만 업데이트합니다.

💡 <a> 태그와 <Link/> 컴포넌트 차이점

<a> 태그는 페이지를 이동시켜주는 역할을 합니다.

하지만 <a> 태그는 페이지를 새로고침하기 때문에 모든 리소스 들을 다시 불러오는 것과 같습니다.

<Link/> 컴포넌트는 이와 다르게 해당되는 페이지 이동시 해당 라우터로 이동시켜주어 해당 라우터에 해당되는 컴포넌트를 렌더링 시켜주는 방식으로 페이지를 새로고침 하지 않고 동적으로 컴포넌트를 불러옵니다.

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

const navStyles = {
  display: "flex",
  gap: "20px",
  padding: "5px",
  border: "1px solid black"
}

const footerStyles = {
  fontSize: "30px",
  fontWeight:"bold"
}

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

  const logout = () => {
    navigate("/")
  }

  return (
    <div>
      <header>
        <h1>Header</h1>
      </header>
      <nav style={navStyles}>
        <Link to="/">Home</Link>
        <Link to="/product/1">Prodcut1</Link>
        <Link to="/product/2">Prodcut2</Link>
        <Link to="/product/3">Prodcut3</Link>
      </nav>
      <Outlet />
      <footer style={footerStyles}>footer</footer>
    </div>
  );
}

Main.js

function Main() {
	return (
    <div>
	     Main Contents
    </div>
  );
}

export default Main;

Product.js

productId를 받아오기 위해 useParams()을 사용하였습니다.

useParams() : 현재 라우트의 URL 매개변수에 접근할 수 있게 해주는 훅입니다.

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

function Product() {
const { productId } = useParams();

	return (
    <div>
	     Product {productId} page.
    </div>
  );
}

export default Product;

NotFound.js

useNavigate() : 라우팅을 프로그래밍 방식으로 제어할 수 있습니다. 즉, 사용자의 액션에 따라 페이지 이동을 제어하는 등의 작업을 수행할 때 유용하게 사용됩니다.

특정 이벤트가 실행됐을 때 동작하도록 하거나 추가적인 로직이 필요한 경우 useNavigate()를 사용합니다.

아래 코드에서는 button를 클릭할 경우 Main( “/” )페이지로 이동되도록 하였습니다.

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

function NotFound() {
	const navigate = useNavigate();
  const goToMain = () => {
		navigate("/");
  }

	return (
    <div>
	     404 Not Found.
    </div>
		<button onClick={ goToMain }>Home</button>
  );
}

export default Product

조건부 라우팅

Login 유무에 따른 조건부 라우팅을 해보겠습니다.

Login 상태를 전역으로 관리하기 위해 ContextAPI를 사용하였습니다.

조건부 처리를 위해 삼항 연산자를 사용하였습니다.

조건에 만족하지 않는다면 <Navigate/> 컴포넌트를 통해 페이지를 이동시켜주었습니다.

<Navigate/> : 페이지 네비게이션을 선언적으로 제어하는데 사용됩니다.

특정 조건에 따라 다른 경로로 리다이렉트하고 싶을 때 <Navigate/> 컴포넌트를 사용합니다.

to 속성을 통해 경로를 지정하면 해당 경로로 이동하게 됩니다.

replace 속성을 추가하면 해당 경로 이동을 replace로 이동하게 됩니다.

app.js

import { Navigate, Route, Routes } from "react-router-dom";
import Main from "./Main";
import Product from "./Product";
import Layout from "./Layout";
import { createContext, useState } from "react";
import Login from "./Login";
import NotFound from "./NotFound";

export const LoginContext = createContext({ isLogin: false });

export default function App() {
  const [isLogin, setIsLogin] = useState(false);

  return (
    <LoginContext.Provider value={{ isLogin, setIsLogin }}>
      <Routes>
        {/* 로그인 이전 라우팅 처리 */}
        <Route
          path="/login"
          element={!isLogin ? <Login /> : <Navigate to="/" replace />}
        />
        {/* 로그인 이후 라우팅 처리 */}
        {isLogin ? (
          <Route path="/" element={<Layout />}>
            <Route index element={<Main />} />
            <Route path="product/:productId" element={<Product />} />
            <Route path="*" element={<NotFound />} />
          </Route>
        ) : (
          <Route path="/*" element={<Navigate to="/login" replace />} />
        )}
      </Routes>
    </LoginContext.Provider>
  );
}

Login.js

import { useContext } from "react";
import { LoginContext } from "./App";

function Login() {
  const { setIsLogin } = useContext(LoginContext);

  const login = () => {
    setIsLogin(true);
  }

  return <button onClick={login}>Login</button>;
}

export default Login;

Layout.js

기존과 코드와 동일하며 logout 버튼이 추가되었습니다.

import { useContext } from "react";
import { Link, Outlet } from "react-router-dom";
import { LoginContext } from "./App";
const navStyles = {
  display: "flex",
  gap: "20px",
  padding: "5px",
  border: "1px solid black",
};

const footerStyles = {
  fontSize: "30px",
  fontWeight: "bold",
};

export default function Layout() {
  const { setIsLogin } = useContext(LoginContext);
  const logout = () => {
    setIsLogin(false);
  };

  return (
    <div>
      <header>
        <h1>Header</h1>
      </header>
      <nav style={navStyles}>
        <Link to="/">Home</Link>
        <Link to="/product/1">Prodcut1</Link>
        <Link to="/product/2">Prodcut2</Link>
        <Link to="/product/3">Prodcut3</Link>
        <button onClick={logout}>로그아웃</button>
      </nav>
      <Outlet />
      <footer style={footerStyles}>footer</footer>
    </div>
  );
}
profile
함께 개선하는 개발자

0개의 댓글