React Hook #4 react router dom v6

Duboo·2022년 7월 5일
0

REACT HOOK

목록 보기
5/16
post-thumbnail

Router

SPA인 리액트(브라우저)에서 페이지를 전환하는 방법
v6 이상 Router 사용방법


대부분의 경우 App.js를 기준으로 Router를 Render하는 용도로 사용한다.

// 각 개별적인 용도를 하는 컴포넌트들을 별도의 폴더안에 정리
- components
 - FileNum01
 - FileNum02
 - FileNum03

// 개별적인 용도의 컴포넌트들을 모아 하나의 화면을 구성하는 대표 페이지들을 별도의 폴더로 정리
- routes
 - Home
 - PageNum01
 - PageNum02

컴포넌트별 분할해서 개발이 가능한 장점을 살려서 컴포넌트를 하나의 페이지로 사용이 가능하며 이를 route 폴더를 만들어서 분류가 가능하고 routes에 들어가는 각각의 컴포넌트들을 components 폴더를 만들어서 분류하는게 좋다.


설치

npm i react-router-dom or yarn add react-router-dom

*router의 사용방법은 변동될 수 있으며, v6 이상을 기준으로 작성함

npx create-react-app my-app으로 리액트를 설치한 뒤 package.json에 보면 react-router-dom은 포함되어 있지 않기 때문에 추가로 설치해줍니다.


기본 구조 및 사용

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";

const App = () => {
  return (
    <Router>
      <Routes>
        <Route />
      </Routes>
    </Router>
  )
}

export default App;
  • BrowserRouter : route를 가능하게 만들어주는 메인
  • Routes : 다수의 Route를 감싸 핸들링하는 태그
    • *v5에서 Switch로 사용되었지만 변경
  • Route : 실제 하나의 페이지로 사용하는 컴포넌트
    • Route는 실제 컴포넌트와 연결되기 때문에 url을 확인하는 path컴포넌트인 element가 필요

App

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Contact from "./routes/Contact";


const App = () => {
  return (
    <Router>
      <Routes>
        <Route path="/" element={Home}/>
        //<Route index element={Home}/>        
        <Route path="contact" element={Contact}/>
        <Route path="about" element={About}/>
      </Routes>
    </Router>
  )
}

export default App;

상위 라우트의 경로로 path="/" 보다 명시적으로 사용할 수 있는 Route의 props으로 index가 있습니다.

  • <Route path="/" element={Home}/>
  • <Route index element={Home}/>
    위 두가지 케이스는 동일한 의미로 사용됩니다.
<Route path="/" element={Home}/>
  • path : url을 확인하며 "/"는 root를 뜻함
  • element : 연결되어 있는 컴포넌트를 뜻함
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Contact from "./routes/Contact";


const App = () => {
  return (
    <>
      <Router>
        <Routes>
          <Route path="/" element={<Home />}/>
          <Route path="contact" element={<Contact />}/>
          <Route path="about" element={<About />}/>
        </Routes>
      </Router>
    </>
  )
}

export default App;
import React from "react";
import { Link } from "react-router-dom";

const Home = () => {
  return (
    <>
        <h1>Home</h1>
        <nav>
            <Link to="/">Home</Link>
            <Link to="/about">About</Link>
            <Link to="/contact">Contact</Link>
        </nav>
    </>
  )
}

export default Home;

Link는 html의 <a>와 같은 역할을 하지만 <a>와는 다르게 페이지가 새로고침되지 않으며 사용방법은 아래와 같다.

<Link to="/url path">Text</Link>
  • to : 원하는 url 경로

이렇게 링크에 경로를 적어주면 App.js에서 작성한 경로(path)에 해당하는 컴포넌트(element)로 새로고침 없이 이동이 가능하다.

Link의 기능 및 스타일을 편하게 적용하기 위해서 사용하면 좋은 기능

import React from "react";
import { NavLink } from "react-router-dom";

const Home = () => {
  return (
    <>
        <h1>Home</h1>
        <nav>
            <NavLink to="/">Home</NavLink>
            <NavLink to="about">About</NavLink>
            <NavLink to="contact">Contact</NavLink>
        </nav>
    </>
  )
}

export default Home;

<Link>와 동일한 기능을 하지만 다른 점은 클릭시 active가 된다는 점이다.

위처럼 class="active"가 자동으로 생성되기 때문에 기능 및 스타일을 적용하기가 매우 유용합니다.

유동적인 URL 경로

유동적인 주소를 정의할 때 사용하는 방법으로 useParams가 있으며, 주로 ID 및 특정한 데이터를 조회할 때 사용합니다.

유저의 프로필을 몇개 만들어서 유저별로 다른 페이지를 불러오는 임시 페이지를 만들어봅니다.

useParams 사용방법

import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Contact from "./routes/Contact";

const App = () => {
  return (
    <>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact/:id" element={<Contact />} />
        </Routes>
      </Router>
    </>
  );
};

export default App;

useParamspath="/contact/:id와 같이 :id로 사용할 수 있습니다.

  • :(콜론) : useParams를 사용하겠다는 의미
  • id : 변수와 같이 생각할 수 있으며 유동적인 url 값

<Route path="/contact/:id" element={<Contact />} />와 같이 contact 경로 뒤에 추가로 오는 경로의 값을 설정해두면 변경되는 값을 useParmas**로 확인할 수 있습니다.

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

const Home = () => {
  return (
    <>
      <h1>Home</h1>
      <ul>
        <li>
          <Link to="/about">/about으로 가는 링크</Link>
        </li>
        <li>
          <Link to="/contact/user1">user1으로 가는 링크</Link>
        </li>
        <li>
          <Link to="/contact/user2">user2으로 가는 링크</Link>
        </li>
        <li>
          <Link to="/contact/user3">유저 정보가 없는 페이지로 가는 링크</Link>
        </li>
      </ul>
    </>
  );
};

export default Home;

App 컴포넌트에서 넣어준 :id의 값으로 <Link to="/contact/user1">user1으로 가는 링크</Link>와 같이 user1 혹은 user2가 올 수 있습니다.

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

const Contact = () => {
  const users = {
    user1: {
      name: "DUDU",
      age: 16,
    },
    user2: {
      name: "POPO",
      age: 33,
    },
  };

  const params = useParams();
  const userInfo = users[params.id];

  return (
    <>
      {userInfo ? (
        <div>
          <h1>유저 정보</h1>
          <div>
            이름: {userInfo.name} 나이: {userInfo.age}
          </div>
        </div>
      ) : (
        <h1>유저 정보가 없습니다</h1>
      )}
    </>
  );
};

export default Contact;

결과를 확인하기 위해 콘솔을 찍어보면 아래와 같은 데이터를 받을 수 있습니다.

Home 화면user1으로 가는 링크 클릭
user2으로 가는 링크 클릭유저 정보가 없는 링크 클릭

위의 url을 살펴보면 App에서 넘겨준 id값Contact의 useParams로 받아 사용할 수 있습니다.


중첩된 라우트 사용

말 그대로 라우트를 중첩되게 사용해야할 때가 있을텐데 컴포넌트를 추가로 만들지 않아도 리액트에서 제공하는 Outlet을 사용하면 쉽게 사용할 수 있습니다.

먼저 블로그들의 리스트를 보여주는 컴포넌트를 만들어줍니다.
Posts

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

const Posts = () => {
  return (
    <ul>
      <li>
        <Link to="/posts/1">블로그 리스트 1</Link>
      </li>
      <li>
        <Link to="/posts/2">블로그 리스트 2</Link>
      </li>
      <li>
        <Link to="/posts/3">블로그 리스트 3</Link>
      </li>
    </ul>
  );
};

export default Posts;

Post

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

const Post = () => {
  const { blog } = useParams();
  return (
    <div>
      <h1>블로그 내용 {blog}</h1>
    </div>
  );
};

export default Post;

App.js

import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Contact from "./routes/Contact";
import Posts from "./routes/Posts";
import Post from "./routes/Post";

const App = () => {
  return (
    <>
      <Router>
        <Routes>
          <Route index element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact/:id" element={<Contact />} />
          <Route path="/posts" element={<Posts />} />
          <Route path="/posts/:blog" element={<Post />} />
        </Routes>
      </Router>
    </>
  );
};

export default App;

위와 같은 상황에 블로그 내용이 들어있는 페이지 안에 다시 블로그 리스트를 불러주기 위해서는

const Post = () => {
  const { blog } = useParams();
  return (
    <div>
      <h1>블로그 내용 {blog}</h1>
      <Posts />
    </div>
  );
};

위와 같이 다시 컴포넌트를 불러와야하지만 리액트에서 제공하는중첩된 라우트 Outlet을 사용하면 보다 쉽게 구현할 수 있습니다.

Outlet

Outlet 컴포넌트는 <Route>의 자식으로 들어가는 JSX 엘리먼트를 불러오는 역할을 합니다.

const App = () => {
  return (
    <>
      <Router>
        <Routes>
          <Route index element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact/:id" element={<Contact />} />
          <Route path="/posts" element={<Posts />}>
            <Route path=":blog" element={<Post />} />
          </Route>
        </Routes>
      </Router>
    </>
  );
};

위처럼 중첩된 라우팅을 사용할 컴포넌트를 감싸주고 부모 (여기서는 Posts)컴포넌트에서 Outlet 컴포넌트를 불러오면 됩니다.

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

const Posts = () => {
  return (
    <div>
      <Outlet />
      <ul>
        <li>
          <Link to="/posts/1">블로그 리스트 1</Link>
        </li>
        <li>
          <Link to="/posts/2">블로그 리스트 2</Link>
        </li>
        <li>
          <Link to="/posts/3">블로그 리스트 3</Link>
        </li>
      </ul>
    </div>
  );
};

export default Posts;

공통된 컴포넌트 사용

상단의 네비게이션과 같은 모든 화면에 보여져야하는 컴포넌트의 경우에도 모든 컴포넌트에서 상단 네비게이션 컴포넌트를 사용하지 않아도Outlet을 사용하면 쉽게 구현이 가능합니다.

Header

import { Outlet } from 'react-router-dom';

const Header = () => {
  return (
    <div>
      <header style={{ background: 'skyblue', padding: "2rem" }}>
        상단 네비게이션 컴포넌트
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
};

export default Header;

App

import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Contact from "./routes/Contact";
import Posts from "./routes/Posts";
import Post from "./routes/Post";
import Header from "./routes/Header";

const App = () => {
  return (
    <>
      <Router>
        <Routes>
          <Route element={<Header />}>
            <Route index element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/contact/:id" element={<Contact />} />
            <Route path="/posts" element={<Posts />}>
              <Route path=":blog" element={<Post />} />
            </Route>
          </Route>
        </Routes>
      </Router>
    </>
  );
};

export default App;

상단에 들어갈 네비게이션 컴포넌트를 생성하고 App.js에서 위와 같이 사용합니다.


useNavigate

Link 컴포넌트를 사용하지 않고 페이지 전환할 때 사용할 수 있는 기능입니다.

Header

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

const Header = () => {
  const navigate = useNavigate();

  const goBack = () => {
    navigate(-1);
  };
  const goPosts = () => {
    navigate("/posts");
  }
  return (
    <div>
      <header style={{ background: 'skyblue', padding: "2rem" }}>
        상단 네비게이션 컴포넌트<br/>
        <button onClick={goBack}>뒤로가기</button>
        <button onClick={goPosts}>블로그 리스트 바로가기</button>
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
};

export default Header;

상단 네비게이션 컴포넌트에 위와 같이 useNavigate를 사용했습니다.

  • navigate() 인자로 숫자 타입을 사용하면 숫자 만큼 페이지 전환을 + or - 할 수 있고 인자로 경로를 사용하면 해당 경로로 바로 이동합니다.
  const goPosts = () => {
    navigate("/posts", { replace: true });
  };
  • navigate("/posts", { replace: true }) 두번째 인자로 넘겨줄 수 있는 replace를 이용해서 페이지 기록을 남기지 않을 수 있습니다.
    • 기본값은 { replace: false }이기 때문에 기록을 남기지 않기 위해서는 true로 사용해야함

홈에서 블로그 리스트 바로가기를 클릭하고 뒤로가기를 클릭하면 홈의 기록이 남지 않아 뒤로가기를 할 수 없습니다.


확인할 수 없는 경로

NotFound

const NotFound = () => {
  return (
    <h1 style={{fontSize: "4rem", color: "red"}}>404 NotFound</h1>
  );
};

export default NotFound;

App

      <Router>
        <Routes>
          <Route element={<Header />}>
            <Route index element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/contact/:id" element={<Contact />} />
            <Route path="/posts" element={<Posts />}>
              <Route path=":blog" element={<Post />} />
            </Route>
          </Route>
          <Route path="*" element={<NotFound />}/>
        </Routes>
      </Router>

<Route path="*" element={NotFound}/>에서 *는 css의 * 와 같다고 생각할 수 있는데 모든 경로에 적용한다는 의미로 만약 모든 경로를 확인했지만 일치하는게 없다면 해당 라우트로 전환됩니다.


회원에게만 보여지는 페이지를 사용자가 로그인을 하지 않은 상태로 들어갔을 때, 로그인 페이지로 넘어가지는 것을 경험해봤을 겁니다.

위와 같은 케이스에 사용할 수 있는 기능으로 리액트에서 제공하는 Navigate가 있습니다.

Login

import React from "react";

const Login = () => {
  return (
    <>
      <h1>로그인 페이지</h1>
    </>
  );
};

export default Login;

MyPage

import React from "react";
import { Navigate } from "react-router-dom";

const MyPage = () => {
  const isLogin = false;
  return (
    <>
      <div>
        {!isLogin ? (
          <Navigate to={"/login"} />
        ) : (
          <h1>로그인 확인 회원 전용 페이지</h1>
        )}
      </div>
    </>
  );
};

export default MyPage;

isLogin 변수에 담긴 값이 로그인의 유무에 따라서 true or false로 변경된다고 가정했을때 로그인이 되지 않은 상황에서는 위와 같이 MyPage 컴포넌트가 아닌 Login 컴포넌트로 전환됩니다.

이때 <Navigate to={"/Login"} replace={ true } />와 같이 replace={ true }를 넘겨주면 사용자가 로그인을 완료한 상태에서 뒤로가기를 눌러도 로그인 페이지는 기록되지 않습니다.

profile
둡둡

0개의 댓글