Front-End

리액트 라우터 본문

리액트

리액트 라우터

jeongsso 2023. 3. 18. 01:30

라우팅!!!!!!!!!이란 ?

라우팅이라는 개념은 사용자가 요청한 URL에 따라 알맞은 페이지를 보여주는 것을 의미한다.

프로젝트를 하나의 페이지로 구성할 수도 있고, 여러 페이지로 구성할 수도 있다.

 

여러 페이지로 구성된 웹 애플리케이션을 만들 때

페이지 별로 컴포넌트들을 분리해가면서 프로젝트를 관리하기 위해 필요한 것이 바로 라우팅 시스템입니다.

 

리액트에서 라우트 시스템을 구축하기 위해 사용할 수 있는 선택지는 크게 두 가지가 있다.

리액트 라우터(react Router)
리액트의 라우팅 관련 라이브러리들 중에서 가장 오래됐고, 가장 많이 사용되고 있습니다.

컴포넌트 기반으로 라우팅 시스템을 설정할 수 있다.

 

Next.js

리액트 프로젝트의 프레임워크입니다.

Create React App처럼 리액트 프로젝트 설정을 하는 기능, 라우팅 시스템, 최적화,

다국어 시스템 지원, 서버 사이드 렌더링 등 다양한 기능을 제공한다.

파일 경로 기반으로 작동한다.

리액트 라우터의 대안으로 많이 사용되고 있다.

 

이 외에도 react-location, rakkas 등의 프로젝트들이 있다.

 

 

일단!!

리액트 라우터를 사용하여 라우팅 시스템을 배워보자.

이 라이브러리를 선택한 이유는 가장 인기 있고, 라우팅 기능에만 집중한 라이브러리이기 때문에

리액트 프로젝트에서의 라우팅 시스템 개념을 익히기에 적합하기 때문이다.

 

오랫동안 개발된 프로젝트인 만큼 다양하고 편리한 라우팅 기능을 제공하며 프로덕션에서 사용하기에 안정적입니다.

 

 

리액트 라우터를 사용하면 손쉽게 리액트 라우터로 

싱글 페이지 애플리케이션(SPA)를 개발할 수 있습니다.

 

 

 

 

1. 싱글 페이지 애플리케이션이란??

Single Page Application

하나의 페이지로 이루어진 애플리케이션이라는 의미다.

 

리액트 라우터를 사용하여 여러 페이지로 구성된 프로젝트를 만들 수 있다고 했는데

왜 싱글 페이지 애플리케이션이라고 불리는지 의문이 들 수 있는데,

 

이를 이해하기 위해서는 멀티 페이지 애플리케이션이 어떻게 작동하는지 알아야한다!

 

멀티 페이지 애플리케이션

멀티 페이지 애플리케이션에서는 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아오고,

페이지를 로딩할 때마다 서버에서 CSS, JS, 이미지 파일 등의 리소스를 전달받아 브라우저 화면에 보여줍니다.

 

각 페이지마다 다른 html 파일을 만들어서 제공을 하거나,

데이터에 따라 유동적인 html을 생성해 주는 템플릿 엔진을 사용하기도 한다.

 

사용자 인터랙션이 별로 없는 정적인 페이지들은 기존의 방식이 적합하지만,

사용자 인터랙션이 많고 다양한 정보를 제공하는 모던 웹 애플리케이션은 이 방식이 접합하지 않다!

 

새로운 페이지를 보여줘야 할 때마다 서버 측에서 모든 준비를 한다면 그만큼 서버의 자원을 사용하는 거라,

트래픽도 더 많이 나올 수 있기 때문이다.

 


 

그래서~

리액트 같은 라이브러리를 사용해서 뷰 렌더링을 사용자의 브라우저가 담당하도록 하고,

우선 웹 애플리케이션을 브라우저에 불러와서 실행시킨 후에 사용자와 인터랙션이 발생하면

필요한 부분만 자바스크립트를 사용하여 업데이트하는 방식을 사용하게 됐다.

 

만약 새로운 데이터가 필요하다면 서버 API를 호출하여 필요한 데이터만 새로 불러와 애플리케이션에서 사용할 수 있다.

 

싱글 페이지 애플리케이션

이렇게 html은 한번만 받아와서 웹 애플리케이션을 실행시킨 후,

이후에는 필요한 데이터만 받아와서 화면에 업데이트하는 것이 싱글 페이지 애플리케이션입니다.

 

싱글 페이지 애플리케이션은 기술적으로는 한 페이지만 존재하지만,

사용자가 경험하기에는 여러 페이지가 존재하는 것처럼 느낄 수 있다.

 

리액트 라우터 같은 라우팅 시스템은 사용자의 브라우저 주소창의 경로에 따라 알맞은 페이지를 보여주는데,

이후 링크를 눌러서 다른 페이지로 이동할 때 서버에 다른 페이지의 html을 새로 요청하는 것이 아니라!

브라우저의 History API를 사용하여 브라우저 주소창의 값만 변경하고

기존 페이지에 띄웠던 웹 애플리케이션을 그대로 유지하면서 라우팅 설정에 따라 또 다른 페이지를 보여준다!!

 

 

 

 

  리액트 라우터 설치 

리액트 라우터는 yarn을 사용하여 react-router-dom 이라는 라이브러리를 설치하면 된다.

$yarn add react-router-dom
// 요로코롬!

 

 

  프로젝트에 라우터 적용  

리액트 라우터를 적용할 때는 src/index.js 파일에서 react-router-dom에 내장되어 있는

BrowserRouter라는 컴포넌트를 사용하여 감싸면 됩니다.

 

이 컴포넌트는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로 불러오지 않고도 주소를 변경하고

현재 주소의 경로에 관련된 정보를 리액트 컴포넌트에서 사용할 수 있도록 해 준다.

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.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App/>
  </BrowserRouter>
);

요런 식으로! 감싸면 됩니다!!

 

 

 

  Route 컴포넌트로 특정 경로에 원하는 컴포넌트 보여주기  

사용자의 브라우저 주소 경로에 따라 우리가 원하는 컴포넌트를 보여주려면 Route라는 컴포넌트를 통해 설정해야한다.

<Route path='주소규칙' elements={보여 줄 컴포넌트 JSX} />

요런 식으로 보여주면 됩니당.

 

Route 컴포넌트는 Routes 컴포넌트 내부에서 사용되어야 합니다.

// App.js
import {Route, Routes} from 'react-route-dom';
import About from './pages/About';
import Home from './pages/Home';

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

export default App;

이제 터미널에 yarn start 를 입력해서 개발 서버를 열면 됩니다!

 

 

 

 

  Link 컴포넌트를 사용하여 다른 페이지로 이동하는 링크 보여주기  

Link 컴포넌트를 사용하여 다른 페이지로 이동하는 링크를 보여주는 방법을 봐보자!

웹 페이지에서는 원래 링크를 보여줄 때 a 태그를 쓰는데,

리액트 라우터를 사용하는 프로젝트에서는 a태그를 바로 사용하면 안된다..!

 

왜냐하면 a 태그를 클릭하여 페이지를 이동할 때 브라우저에서는 페이지를 새로 불러오기 때문이다.

 

Link 컴포넌트 역시 a 태그를 사용하기는 하지만,

페이지를 새로 불러오는 것을 막고 History API를 통해 브라우저 주소의 경로만 바꾸는 기능이 내장되어 있다.

<Link to = "경로"> 링크 이름 </Link>

 

 

 

 

 

 


2. URL 파라미터와 쿼리스트링

페이지 주소를 정의할 때 가끔은 유동적인 값을 사용해야할 때도 있다.

URL 파라미터 예시 : /profile/jeongseo

쿼리스트링 예시 : /develop?page=1&keyword=react

 

URL 파라미터주소의 경로에 유동적인 값을 넣는 형태,

쿼리스트링주소의 뒷부분에 ?문자열 이후 key=value로 값을 정의하며 &로 구분하는 형태다.

 

 

 

URL 파라미터

URL 파라미터는 useParams라는 Hook을 사용하여 객체 형태로 조회할 수 있다.

URL 파라미터의 이름은 라우트 설정할 때 Route 컴포넌트의 path props를 통해 결정한다.

 

URL 파라미터는 /profile/:username과 같이 경로에 : 를 사용하여 설정한다.

만약 URL 파라미터가 여러 개인 경우 /profile/:username/:field와 같은 형태로 설정할 수 있다.

 

쿼리스트링

쿼리스트링을 사용할 때는 URl파라미터와 달리 Route 컴포넌트를 사용할 때 별도로 설정하는 것이 없다.

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

const About = () => {
  const location = useLocation();
  return (
    <div>
      <h1>소개</h1>
      <p> 리액트 라우터를 사용해 보는 프로젝트입니다.</p>
      <p>쿼리스트링: {location.search}</p>
    </div>
  );
};

export default About;

위 컴포넌트에서는 useLocation이라는 Hook을 사용했습니다.

이 Hook은 location 객체를 반환하는데, 

이 객체는 현재 사용자가 보고 있는 페이지의 정보를 지니고 있다.

즉, 이 객체에는 다음과 같은 값들이 있다.

- pathname : 현재 주소 경로( 쿼리스트링 제외)

- serch : 맨 앞의 ? 문자를 포함한 쿼리스트링 값

- hash : 주소의 # 문자열 뒤의 값

- state : 페이지로 이동할 때 임의로 넣을 수 있는 상태 값

- key : location 객체의 고유값, 초기에는 default 이며, 페이지가 변경될 때마다 고유의 값이 생성됨.

 

쿼리스트링은 location.search 값을 통해 조회할 수 있다.

 

쿼리스트링 값이 현재 ?detail=true&mode=1로 표시되는데,

이 문자열에서 앞에 있는 ?를 지우고, & 문자열로 분리한 뒤 key와 value를 파싱하는 작업을 해야하는데,

이 작업은 보통 npm에서 qs 또는 querystring 패키지를 설치해 처리할 수 있다.

 

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

const About = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const detail = searchParams.get('detail');
  const mode = searchParams.get('mode');
  
  const onToggleDetail = () => {
    setsearchParams({ mode, detail: detail === 'true' ? false : true });
  };
  
  const onIncreaseMode = () => {
    const nextMode = mode === null ? 1 : parseInt (mode) + 1;
    setsearchParams ({ mode: nextMode, detail });
  };
  
  return (
    <div>
      <h1>소개</h1>
      <p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
      <p>detail: {detail} </p>
      <p>mode: {mode}</p>
      <button onClick={onToggleDetail}>Toggle detail</button>
      <button onClick={onIncreaseMode}>mode + 1</button>
    </div>
  );
};

export default About;

useSearchParams는 배열 타입의 값을 반환하며,

첫 번째 원소는 쿼리파라미터를 조회하거나 수정하는 메서드들이 담긴 객체를 반환한다.

get 메서드를 통해 특정 쿼리파라미터를 조회할 수 있고,

set 메서드를 통해 특정 쿼리파라미터를 업데이트할 수 있다.

만약 조회 시 쿼리파라미터가 존재하지 않는다면 null로 조회된다.

 

두 번째 원소는 쿼리 파라미터를 객체 형태로 업데이트 할 수 있는 함수를 반환한다.

 

 

쿼리파라미터를 사용할 때 주의할 점조회할 때 값은 무조건 문자열 타입이라는 점이다.

즉, true 또는 false 값을 넣는다면 값을 비교할 때 꼭 'true'와 같이 따옴표로 감싸서 비교하고,

숫자를 다룬다면 parseInt를 사용하여 숫자 타입으로 변환을 해야한다.

 

 

 

 

 


3.  중첩된 라우트

이번에는 리액트 라우터에서 중첩된 라우트를 다룰 때 어떻게 해야 하는지 보자.

 

Articles.js 와 Aticle.js를
App.js에서 불러게 되면,

<Route path = "/articles" element={<Articles/>}/>
<Route path = "/articles/:id" element={<Article/>}/>

요런식으로 불러오고,

 

Home 컴포넌트에서는 게시글 목록 페이지로 가는 링크를 추가하면?!

<li>
  <Link to = "/articles"> 게시글 목록 </Link>
</li>

요로코롬!

 


 

근데 중첩된 라우트를 이용하면 좀 더 나은 식으로 구현할 수 있다.

일단 App.js에서 불러오는 방식을 보자.

<Route path="/articles" element={<Articles/>}>
  <Route path=":id" element={<Article/>}/>
</Route>

이렇게 안아줍니다!

 

다음으로 Articles 컴포넌트에 리액트 라우터에서 제공하는 Outlet이라는 컴포넌트를 사용해야한다.

이 컴포넌트는 Route의 children으로 들어가는 JSX 엘리먼트를 보여주는 역할을 한다.

<Route path=":id" element={<Article/>}/>

Articles 컴포넌트에서 수정해야하는 점이 있는데,

// 원래는 이랬다면,
import { Link } from 'react-router-dom';

const Articles = () => {
  return (
    <ul>
      <li>
        <Link to="/articles/1">게시글 1</Link>
      </li>
      <li>
        <Link to="/articles/2">게시글 2</Link>
      </li>
      <li>
        <Link to="/articles/3">게시글 3</Link>      
      </li>
    </ul>
  );
};

export default Articles;

// 이런 식으로 바꿔주면 됩니다.
import { Link, Outlet } from 'react-router-dom';

const Articles = () => {
  return (
    <div>
      <Outlet/>
      <ul>
        <li>
          <Link to="/articles/1">게시글 1</Link>
        </li>
        <li>
          <Link to="/articles/2">게시글 2</Link>
        </li>
        <li>
          <Link to="/articles/3">게시글 3</Link>
        </li>
      </ul>
    </div>
  );
};

export default Articles;

 

 

 

 

 

  공통 레이아웃 컴포넌트  

중첩된 라우트와 Outlet은 페이지끼지 공통적으로 보여줘야 하는 레이아웃이 있을 때도 유용하게 사용할 수 있다.

 

예를 들어 Home, About, Profile 페이지 상단에 헤더를 보여줘야 하는 상황을 가정해봅시다.


가장 먼저 Header 컴포넌트를 따로 만들어두고 각 페이지 컴포넌트에서 재사용하는 방법을 생각 할 것입니다.

물론 이 방법이 틀린 것은 아니지만,

방금 배운 중첩된 라우트와 Outlet을 활용하여 구현할 수도 있습니다.

중첩된 라우트를 사용하는 방식을 사용하면 컴포넌트를 한번만 사용해도 된다는 장점이 있죠.

상황에 따라 그리고 여러분의 취향에 따라 구현하면 됩니다.


이번에는 중첩된 라우트를 통해 공통 레이아웃 컴포넌트를 사용해봅시다.


우선 공통 레이아웃을 위한 Iayout 컴포넌트를 src 디렉터리에 만드세요.

// src/Layout.js
import { Outlet } from 'react-router-dom';

const Layout = () => {
  return (
    <div>
      <header style={{ background:'lightgray', padding: 16, fontsize: 24 }}>
        Header
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
}; 
 
export default Layout;

각 페이지 컴포트가 보여져야 하는 부분에 Outlet 컴포넌트를 사용해주었습니다.

 컴포넌트를 다 작성했으면 App 컴포넌트를 다음과 같이 수정해주세요.

// src/App.js
import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route element={<Layout>} />
        <Route path="/" element={<Home />} />
        <Route path="/about" element= {<About />} />
        <Route path="/profiles/:username" element={<Profile />} />
      </Route>
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
    </Routes>
  );
};

export default App;

 

 

 

 

 

  index props  

Route 컴포넌트에는 index라는 props가 있다.

이 props는 path="/"와 동일한 의미를 가진다.

<Route index element={<About/>}/>

path="/"와 동일한 역할을 하며 이를 좀 더 명시적으로 표현하는 방법이다.

 

 

 

 

 


4.  리액트 라우터 부가기능

  useNavigate 

Link 컴포넌트를 사용하지 않고 다른 페이지로 이동해야 하는 상황에 사용하는 Hook이다.

const goArticles = () => {
  // articles 경로로 이동
  navigate('/article');
};

navigate 함수를 사용할 때 파라미터가 숫자 타입이라면 앞으로 가거나, 뒤로 간다.

예를 들어 navigate(-1)을 하면 뒤로 한 번 가고,

navigate(-2)를 하면 뒤로 두 번 간다.

 

반대로 navigate(1)을 하면 앞으로 한 번 간다. 물론~ 뒤로가기를 한번 한 상태여야 한다.

 

 

다른 페이지로 이동할 때 replace라는 옵션이 있는데,

이 옵션을 사용하면 페이지를 이동할 때 현재 페이지를 페이지 기록에 남기지 않는다.

 

만약 {replace : true} 설정이 없었다면 직전에 봤던 페이지로 가겠지만,

이 옵션이 활성화되어 있다면 그 전의 페이지로 가게 된다.

 

그러니까 ! 음...

1 → 2 → 3(현재)라면 replace : true라면 뒤로가기 했을 때 1로 가버린다고 생각하면 된다.

 

 

 

 

 

  NavLink  

NavLink 컴포넌트는 링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우

특정 스타일 또는 CSS 클래스를 적용하는 컴포넌트다.

 

이 컴포넌트의 style과 className은 {isActive:boolean}을 파라미터로 전달받는 함수 타입의 값을 전달한다.

<NavLink
  style={({isActive}) => isActive ? activeStyle : undefined}
/>

<NavLink
  style={({isActive}) => isActive ? 'active' : undefined}
/>

 

 

 

 

 

  NotFound 페이지 만들기  

이 페이지는 사전에 정의되지 않은 경로에 사용자가 진입했을 때 보여주는 페이지다.

즉, 페이지를 찾을 수 없을 때 나타나는 페이지입니다.

<Route path="*" element={<NotFound/>} />

여기서 *는 wildcard 문자인데,

아무 텍스트나 매칭한다는 뜻이다.

 

이 라우트 엘리먼트의 상단에 위치하는 라우트들의 규칙을 모두 확인하고,

일치하는 라우트가 없다면 이 라우트가 화면에 나타나게 됩니다.

 

 

 

 

  Navigate 컴포넌트  

컴포넌트를 화면에 보여주는 순간 다른 페이지로 이동을 하고 싶을 때 사용하는 컴포넌트다.

즉, 페이지를 리다이렉트하고 싶을 때 사용한다.

 

예를 들어 사용자의 로그인이 필요한 페이지인데 로그인을 안했다면 로그인 페이지를 보여주는 듯이!

<Navigate to = "./login" replace={true}/>

 

 

 

 

 

 

'리액트' 카테고리의 다른 글

리덕스 / Context API  (2) 2023.06.02
되짚어 가기  (0) 2023.06.01
immer 불변성 유지하기  (1) 2023.03.14
컴포넌트 성승 최적화  (1) 2023.03.10
컴포넌트 스타일링  (1) 2023.03.06
Comments