프론트엔드/리액트를 다루는 기술

리액트를 다루는 기술, 리액트 라우터로 SPA 개발하기

Hyeon_E 2023. 7. 10. 21:37

정리하기에 앞서 현재 쓰이는 것은 react-router-v6인데 책에서는 react-router-v5를 다루고 있어 많은 차이가 있음 검색하면 안쓰이는 것이랑 없어진것 등 나름 정리했지만 현재 쓰이는 것이랑 차이가 있을 수 있음

 

[ 13.1 SPA란 ]

SPA는 Single Page Application(싱글 페이지 애플리케이션)의 약어 말 그대로 한 개의 페이지로 이루어진 애플리케이션

 

전통적인 웹페이지는 여러 페이지로 구성되어 있음 기존에는 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아오고 페이지를 로딩할 때마다 서버에서 리소스를 전달받아 해석한 뒤 화면에 보여줌

사용자에게 보이는 화면은 서버 측에서 준비함 사전에 html 파일을 만들어서 제공하거나 데이터에 따라 유동적인 html을 생성해 주는 템플릿 엔진을 사용하기도 함 요즘은 웹에서 제공되는 정보가 정말 많기 때문에 새로운 화면을 보여 주어야 할 때마다 서버 측에서 모든 뷰를 준비한다면 성능상에 문제가 발생할 수 있음 속도와 트래픽 측면에서는 캐싱과 압축을 해서 서비스를 제공하면 어느정도 최적화 될 수도 있지만 사용자와의 인터랙션이 자주 발생하는 모던 웹 애플리케이션에는 적당하지 않을 수 있음 또한 화면전환이 일어날때마다 html을 계속 서버에 새로 요청하면 사용자의 인터페이스에서 사용하고 있던 상태를 유지하는 것도 번거롭고 바뀌지 않는 부분까지 새로 불러와서 보여주어야 하기 때문에 불필요한 로딩이 있어서 비효율적

 

그래서 리액트 같은 라이브러리 혹은 프레임워크를 사용하여 뷰 렌더링을 사용자의 브라우저가 담당하도록 하고 애플리케이션을 브라우저에 불러와 실행시킨 후 사용자와 인터렉션이 발생하면 필요한 부분은 JS를 사용하여 업데이트 해줌

 

SPA의 경우 서버에서 사용자에게 제공하는 페이지는 한종류이지만 해당 페이지에서 로딩된 JS와 현재 사용자 브라우저의 주소 상태에 따라 다양한 화면을 보여줄 수 있음 다른 주소에 다른 화면을 보여 주는 것을 라우팅이라고 함

 

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

  • 리액트 라우터(React Router): 리액트의 라우팅 관련 라이브러리들 중에서 가장 오래됐고 가장 많이 사용되고 있음 이 라이브러리는 컴포넌트 기반으로 라우팅 시스템을 설정할 수 있음
  • Next.js: 리액트 프로젝트의 프레임워크 이 프레임워크는 Create React App처럼 리액트 프로젝트 설정을 하는 기능, 라우팅 시스템, 최적화, 다국어 시스템 지원, 서버 사이드 렌더링 등 다양한 기능들을 제공함 이 프레임워크의 라우팅 시스템은 파일 경로 기반으로 작동함 이 프레임워크는 리액트 라우터의 대안으로 많이 사용되고 있음

 

▶ 13.1.1 SPA 단점

앱의 규모가 커지면 JS 파일이 너무 커진다는 것 페이지 로딩시 사용자가 실제로 방문하지 않을 수도 있는 페이지의 JS도 불러오기 때문 리액트 라우터처럼 브라우저에서 JS를 사용하여 라우팅을 관리하는 것은 JS를 실행하지 않은 일반 크롤러에서는 페이지의 정보를 제대로 수집해 가지 못한다는 잠재적인 단점이 따름 즉 검색 결과에 페이지가 잘 나타나지 않을 수 있음 또한 JS가 실행될때까지 페이지가 비어 있기 때문에 JS 파일이 로딩되어 실행되는 짧은 시간동안 흰 페이지가 나타날 수 있다는 단점이 있음

 

[ 13.2 프로젝트 준비 및 기본적인 사용법 ]

터미널에 라이브러리 다운

 

yarn add react-router-dom

 

▶ 13.2.2 프로젝트에 라우터 적용

프로젝트에 리액트 라우터를 적용할때는 src/inex.js 파일에서 react-router-dom에 내장되어이 있는 BrowerRouter라는 컴포넌트를 사용하여 감싸면 됨 이 컴포넌트는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경하고 현재 주소에 관련된 정보를 props로 쉽게 조회하거나 사용할 수 있게 해줌 

 

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

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

 

▶ 13.2.3 Route 컴포넌트로 특정 주소에 컴포넌트 연결

Route 컴포넌트를 사용하여 어떤 규칙을 가진 경로에 어떤 컴포넌트를 보여줄지 정의할 수 있음

Route 컴포넌트는 Routes 컴포넌트 내부에서 사용되어야 함

 

<Route path="주소규칙" element={보여 줄 컴포넌트} />

 

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

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

export default App;

 

구글을 확인하다보면 element를 component로 되어있는것을 볼수도 있는데 React Router v6가 정식 릴리즈 되면서 Route에 children이나 component가 사라졌고 대신 element를 사용해야함

또한 exact 옵션이 삭제되어 하위경로에 여러 라우팅을 매칭시키고 싶다면 URL 뒤에 *을 사용하여 일치시킴

 

[ 13.3 Link 컴포넌트를 사용하여 다른 주소로 이동하기 ]

Link 컴포넌트는 클릭하여 다른 주소로 이동시켜 주는 컴포넌트 일반 웹 애플리케이션에서는 a 태그를 사용하여 페이지를 이동하는데 리액트 라우터를 사용할때는 a 태그를 직접 사용하면 안됨 a 태그는 페이지를 전환하는 과정에서 페이지를 새로 불러오기 때문에 에플리케이션이 들고 있는 상태들을 모두 날려 버리게 됨 렌더링된 컴포넌트들도 다 사라지고 다시 처음부터 렌더링하게 된는 것

Link 컴포넌트를 사용하여 페이지를 전환하면 페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한 상태에서 HTML5 History API를 사용하여 페이지의 주소만 변경해 줌 Link 컴포넌트 자체는 a 태그로 이루어져 있지만 페이지 전환을 방지하는 기능이 내장되어 있음

 

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

 

import { Route, Routes, Link } from "react-router-dom";
import Home from "./Home";
import About from "./About";

const App = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">홈</Link>
        </li>
        <li>
          <Link to="/about">소개</Link>
        </li>
      </ul>
      <Routes>
        <Route path="/" element={<Home />}></Route>
        <Route path="/about" element={<About />}></Route>
      </Routes>
    </div>
  );
};

 

[ 13.4 URL 파라미터와 쿼리스트링 ]

페이지 주소를 정의할 때 가끔은 유동적인 값을 전달해야 할 때도 있음

이는 파라미터와 쿼리스트링으로 나눌 수 있음

  • 파라미터: 주소의 경로에 유동적인 값을 넣는 형태
    • ex) /propfile/velopert
  • 쿼리스트링: 주소의 뒷부분에 ? 문자열 이후에 key=value로 값을 정의하며 &로 구분하는 형태
    • ex) /articles?**page=1&keyword=react

 

주로 URL 파라미터는 ID 또는 이름을 사용하여 특정 데이터를 조회할 때 사용하고 쿼리스트링(Querystring)은 키워드 검색, 페이지네이션, 정렬 방식 등 데이터 조회에 필요한 옵션을 전달할 때 사용

 

▶ 13.4.1 URL 파라미터

 

// src/pages/Profile.js
import { useParams } from 'react-router-dom';

const data = {
  velopert: {
    name: '김민준',
    description: '리액트를 좋아하는 개발자',
  },
  gildong: {
    name: '홍길동',
    description: '고전 소설 홍길동전의 주인공',
  },
};

const Profile = () => {
  const params = useParams();
  const profile = data[params.username];

  return (
    <div>
      <h1>사용자 프로필</h1>
      {profile ? (
        <div>
          <h2>{profile.name}</h2>
          <p>{profile.description}</p>
        </div>
      ) : (
        <p>존재하지 않는 프로필입니다.</p>
      )}
    </div>
  );
};

export default Profile;

 

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

URL 파라미터의 이름 라우트를 설정을 할 때 Route 컴포넌트의 path porps를 통하여 설정

data 객체에 예시 프로필 정보들을 key-value 형태로 담아두었음

Profile  컴포넌트에서는 username URL 파라미터를 통하여 프로필을 조회한 뒤에 프로필이 존재하지 않으면 ‘존재하지 않는 프로필입니다.’ 라는 문구를 보여주고 존재한다면 프로필 정보를 보여주도록 로직을 작성

 

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

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

export default App;

 

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

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

 

// src/pages/Home.js
import { Link } from 'react-router-dom';

const Home = () => {
  return (
    <div>
      <h1>홈</h1>
      <p>가장 먼저 보여지는 페이지입니다.</p>
      <ul>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profiles/velopert">velopert의 프로필</Link>
        </li>
        <li>
          <Link to="/profiles/gildong">gildong의 프로필</Link>
        </li>
        <li>
          <Link to="/profiles/void">존재하지 않는 프로필</Link>
        </li>
      </ul>
    </div>
  );
};

export default Home;

 

▶ 13.4.2 쿼리스트링

쿼리스트링을 사용할 때는 URL 파라미터와 달리 Route 컴포넌트를 사용할 때 별도로 설정해야되는 것은 없음

 

// src/pages/About.js
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은 location 객체를 반환하며 현재 사용자가 보고 있는 페이지의 정보를 지니고 있음

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

  • pathname: 현재 주소의 경로(쿼리스트링 제외)
  • search: 맨 앞의 ? 문자 포함한 쿼리스트링 값
  • hash: 주소의 # 문자열 뒤의 값
    • 주로 History API 가 지원되지 않는 구형 브라우저에서 클라이언트 라우팅을 사용할 때 쓰는 해시 라우터에서 사용
  • state: 페이지로 이동할때 임의로 넣을 수 있는 상태 값
  • key: location 객체의 고유 값 초기에는 default 이며 페이지가 변경될때마다 고유의 값이 생성됨

위에 코드를 사용해 주소창에 http://localhost:3000/about?detail=true&mode=1 라고 입력하면

쿼리스트링 값이 현재 ?detail=true&mode=1 으로 표시됨

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

쿼리스트링을 따로 파싱까지 해야된다면 번거로울수도 있는데 다행히도 리액트 라우터에서는 v6 부터 useSearchParams 라는 Hook을 통해서 쿼리스트링을 더욱 쉽게 다룰 수 있게 되었음

 

// src/pages/About.js
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를 사용하여 숫자 타입으로 변환을 해야 함

 

[ 13.5 중첩된 라우트 ]

// src/pages/Articles.js
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;

 

// src/pages/Article.js
import { useParams } from 'react-router-dom';

const Article = () => {
  const { id } = useParams();
  return (
    <div>
      <h2>게시글 {id}</h2>
    </div>
  );
};

export default Article;

 

// src/App.js
import { Route, Routes } from 'react-router-dom';
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 path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/profiles/:username" element={<Profile />} />
      <Route path="/articles" element={<Articles />} />
      <Route path="/articles/:id" element={<Article />} />
    </Routes>
  );
};

export default App;

 

만약에 따로 페이지를 보여주는 것이 아니라 게시글 하단에 목록을 보여주어야 한다면 

기존 방식으로 구현하면 각 컴포넌트를 따로 만들어서 각 페이지 컴포넌트에서 사용을 해야됨

 

<div>
  <h2>게시글 {id}</h2>
  <ArticleList />
</div>

 

하지만 중첩된 라우트를 사용한다면 좀 더 나은 방식으로 구현 할 수 있음

 

// src/App.js
import { Route, Routes } from 'react-router-dom';
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 path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/profiles/:username" element={<Profile />} />
      <Route path="/articles" element={<Articles />}>
        <Route path=":id" element={<Article />} />
      </Route>
    </Routes>
  );
};

export default App;

 

Articles 컴포넌트에서 리액트 라우터에서 제공하는 Outlet 이라는 컴포넌트를 사용해주어야 함

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

 

// src/pages/Articles.js
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 컴포넌트가 사용된 자리에 중첩된 라우트가 보여지게 됨

즉 여기서는 Article 컴포넌트와 밑에 리스트가 한 화면에 보여지는 것

 

▶ 13.5.1 공통 레이아웃 컴포넌트

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

페이지에서 상단에 헤더를 보여줘야 하는 상황이라면 Header 컴포넌트를 따로 만들어두고 중첩된 라우트와 Outlet을 활용하여 구현을 할 수 있음 중첩된 라우트를 사용하는 방식을 사용하면 컴포넌트를 한번만 사용해도 된다는 장점이 있음

 

// 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 컴포넌트를 사용

 

// 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;

 

Home 페이지에 들어가면 상단에 Header가 보이는 것을 확인할 수 있음

 

▶ 13.5.2 index props 

Route 컴포넌트에는 index라는 props가 있음 이 props 는 path="/"와 동일한 의미를 가짐

 

// 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 path="/" element={<Layout />}>
        <Route index 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 prop은 상위 라우트의 경로와 일치하지만 그 이후에 경로가 주어지지 않았을때 보여지는 라우트를 설정할때 사용

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

 

[ 13.6 리액트 라우터 부가기능 ]

리액트 라우터는 웹 애플리케이션에서 라우팅에 관련된 작업을 할 때 사용할 수 있는 유용한 API를 제공

 

▶ 13.6.1 useNavigate

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

 

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

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

  const goBack = () => {
    // 이전 페이지로 이동
    navigate(-1);
  };

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

  return (
    <div>
      <header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
        <button onClick={goBack}>뒤로가기</button>
        <button onClick={goArticles}>게시글 목록</button>
      </header>
      <main>
        <Outlet />
      </main>
    </div>
  );
};

export default Layout;

 

 

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

예를 들어서 navigate(-1) 을 하면 한 번 뒤로가고 navigate(-2) 를 하면 두 번 뒤로감

반대로 navigate(1) 을 하면 앞으로 한 번 감 이때 뒤로가기를 한번 한 상태여야 함

다른 페이지로 이동을 할 때 replace 라는 옵션이 있는데 이 옵션을 사용하면 페이지를 이동할 때 현재 페이지를 페이지 기록에 남기지 않음

 

// src/Layout.js - goArticles
const goArticles = () => {
  navigate('/articles', { replace: true });
}

 

그 다음에 / 경로로 들어가서 Home 페이지를 띄운 뒤에 소개 링크를 눌러 About 페이지로 이동한 후 상단의 게시글 목록 페이지를 눌러 들아간 다음 그 상태에서 뒤로가기 버튼을 눌러 이전 페이지로 디오하면 Home 페이지가 뜸

 { replace: true } 설정으로 인해 현재 페이지를 페이지 기록으로 남기지 않았기 때문에 설정이 없었더라면 직전에 봤던 페이지인 About 페이지가 나타나야 하지만 true로 그 전의 페이지인 Home 페이지가 나타나게 됨

 

13.6.2 NavLink

NavLink 컴포넌트는 링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 또는 CSS 클래스를 적용하는 컴포넌트 이 컴포넌트를 사용할 때 style 또는 className을 설정할 때 { isActive: boolean } 을 파라미터로 전달받는 함수 타입의 값을 전달

 

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

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

 

// src/pages/Articles.js
import { NavLink, Outlet } from 'react-router-dom';

const Articles = () => {
  const activeStyle = {
    color: 'green',
    fontSize: 21,
  };

  return (
    <div>
      <Outlet />
      <ul>
        <li>
          <NavLink
            to="/articles/1"
            style={({ isActive }) => (isActive ? activeStyle : undefined)}
          >
            게시글 1
          </NavLink>
        </li>
        <li>
          <NavLink
            to="/articles/2"
            style={({ isActive }) => (isActive ? activeStyle : undefined)}
          >
            게시글 2
          </NavLink>
        </li>
        <li>
          <NavLink
            to="/articles/3"
            style={({ isActive }) => (isActive ? activeStyle : undefined)}
          >
            게시글 3
          </NavLink>
        </li>
      </ul>
    </div>
  );
};

export default Articles;

 

게시글에 들어갔을때 그 게시글의 링크의 텍스트가 더 커지고 글씨 색이 초록색으로 변함 위에 코드처럼 반복되는 컴포넌트가 많을 경우  현재 NavLink를 감싼 또 다른 컴포넌트를 만들어서 리팩토링 한는 것이 좋음

 

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

const Articles = () => {
  return (
    <div>
      <Outlet />
      <ul>
        <ArticleItem id={1} />
        <ArticleItem id={2} />
        <ArticleItem id={3} />
      </ul>
    </div>
  );
};

const ArticleItem = ({ id }) => {
  const activeStyle = {
    color: 'green',
    fontSize: 21,
  };
  return (
    <li>
      <NavLink
        to={`/articles/${id}`}
        style={({ isActive }) => (isActive ? activeStyle : undefined)}
      >
        게시글 {id}
      </NavLink>
    </li>
  );
};

export default Articles;

 

▶ 13.6.3 NotFound 페이지 만들기

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

즉 페이지를 찾을 수 없을 때 나타나는 페이지

 

// src/pages/NotFound.js
const NotFound = () => {
  return (
    <div
      style={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: 64,
        position: 'absolute',
        width: '100%',
        height: '100%',
      }}
    >
      404
    </div>
  );
};

export default NotFound;

 

// 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 NotFound from './pages/NotFound';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index 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>
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
};

export default App;

 

여기서 * 는 wildcard 문자 이 문자는 아무 텍스트나 매칭한다는 뜻 라우트 엘리먼트의 상단에 위치하는 라우트들의 규칙을 모두 확인하고 일치하는 라우트가 없다면 이 라우트가 화면에 나타나게 됨

 

▶ 13.6.4 Navigate 컴포넌트

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

즉 페이지를 리다이렉트 하고 싶을 때 사용 예를 들어 사용자의 로그인이 필요한 페이지인데 로그인을 안했다면 로그인 페이지를 보여주는 상황에 사용할 수 있음

 

// src/pages/Login.js
const Login = () => {
  return <div>로그인 페이지</div>;
};

export default Login;

 

// src/pages/MyPage.js
import { Navigate } from 'react-router-dom';

const MyPage = () => {
  const isLoggedIn = false;

  if (!isLoggedIn) {
    return <Navigate to="/login" replace={true} />;
  }

  return <div>마이 페이지</div>;
};

export default MyPage;

 

여기서 isLoggedIn은 현재 false라는 고정값을 가지고 있지만 이 값이 로그인 상태에 따라 true 또는 false를 가르킨다고 가정한다면 위 컴포넌트에서는 값이 false 라면 Navigate 컴포넌트를 통해 /login 경로로 이동 여기서 replace props는 useNavigate 에서 설명한 것과 동일함 페이지를 이동할 때 현재 페이지를 기록에 남기지 않기 때문에 이동 후 뒤로가기를 눌렀을 때 2 페이지 전의 페이지로 이동함

 

// 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 Login from './pages/Login';
import MyPage from './pages/MyPage';
import NotFound from './pages/NotFound';
import Profile from './pages/Profile';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index 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>
      <Route path="/login" element={<Login />} />
      <Route path="/mypage" element={<MyPage />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
};

export default App;

 

브라우저에서 /mypage 경로로 이동해보면 페이지가 로딩되는 순간 바로 Login 페이지로 이동됨

 

[ 13.7 정리 ]

리액트 라우터를 사용하면 큰 규모의 프로젝트를 진행하다 보면 한 가지 문제가 발생

웹 브라우저에서 사용할 컴포넌트, 상태 관리를 하는 로직, 그 외 여러 기능을 구현하는 함수들이 점점 쌓이면서 최종 결과물인 자바스크립트 파일의 크기가 매우 커진다는 점

위에 코드를 예로 About 페이지에 사용자가 들어왔을 때 지금 당장 필요하지 않은 Profile, Articles 등의 컴포넌트의 코드까지 함께 불러옴 이런 경우 라우트에 따라 필요한 컴포넌트만 불러오고 다른 컴포넌트는 다른 페이지로 이동하게되어 필요한 시점에 불러오면 더 효율적 이 문제는 코드 스플리팅이라는 기술로 해결을 할 수 있음