ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React.js(2)_React Router
    SPA/React.js 2022. 9. 28. 11:47

    해당 게시글은 2022.04.28에 깃허브로 작성되었습니다.

     


     

    1. Router란?

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

     

    예를 들어 블로그를 생각해봤을 때

    블로그 내에는 [글쓰기, 포스트 목록, 포스트 읽기 페이지...] 등 하나의 애플리케이션이 여러 페이지로 구성되어 있다.

     

    이렇게 여러 페이지로 구성된 웹 애플리케이션을 만들 때 페이지별로 Component들을 분리해가면서 프로젝트를 관리하기 위해 필요한 것이 바로 Routing 시스템이다.

     

    React에서 Route 시스템을 구축하기 위해 사용할 수 있는 선택지는 아래와 같이 크게 두가지가 있다.

    (1) React-Router
    React의 Routing 관련 라이브러리들 중 가장 오래됐고 가장 많이 사용된다.
    Component 기반으로 Routing 시스템을 설정할 수 있다.

    (2) Next.js
    React 프로젝트의 프레임워크다.
    React 프로젝트 설정을 하는 기능, Routing 시스템, 최적화, 다국어 시스템 지원, 서버 사이드 렌더링 등 다양한 기능을 제공한다.

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

     

    Next.js에 대한 글은 아래에서 볼 수 있다.

     

    [Next.js] 01. Next.js 시작하기

    1. Next.js란? Next.js는 React로 만드는 서버 사이드 렌더링(SSR) 프레임 워크 이다. 그렇다면 기존 React만을 사용하는 것보다 Next.js를 사용할 때 얻는 이득은 무엇일까? 그 이득을 이해하기 위해 우선 CS

    chaeng03.tistory.com

     

    우선 React Router를 사용하면 쉽게 싱글 페이지 애플리케이션(Single Page Application)을 만들 수 있다.

     


     

    2. Single Page Application이란?

    Single Page Application(이하 SPA) 이란 말 그대로 한 개의 페이지로 이루어진 애플리케이션을 의미한다.

    이를 명확히 이해하기 위해서는 SPA가 생기기 전에 사용되던 Multi Page Application(이하 MPA)의 작동 원리에 대해 알아볼 필요가 있다.

     

    MPA에서는 사용자가 다른 페이지로 이동할 때마다 새로운 HTML을 받아오고

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

     

    이런 방식은 사용자 인터랙션이 별로 없는 정적인 페이지에서는 적합하지만

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

    새로운 페이지를 보여줄 때마다 서버측에서 모든 준비를 한다면 그만큼 서버의 자원을 사용하는 것이고 트래픽도 더 많이 나올 수 있기 때문이다.

    그래서 사용자와의 인터랙션이 발생하면 필요한 부분만 Javascript를 사용하여 업데이트하는 방식을 사용하게 됐는데

    이렇게 HTML은 한번만 받아와서 웹 애플리케이션을 실행시키고 그 이후에는 필요한 데이터만 받아와서 화면에 업데이트해주는 것을 SPA라고 한다.

     

    SPA는 기술적으로는 한 페이지만 존재하지만 사용자가 경험하기에는 여러 페이지가 존재하는 것처럼 느낄 수 있는데

    사용자가 링크를 눌러서 다른 페이지로 이동할 때마다 다른 페이지의 HTML을 요청하는 것이 아니라

    브라우저의 History API를 사용하여 브라우저의 주소창 값만 변경하고 해당 주소창 값에 적합한 페이지를 보여주는 방식 이기 때문이다.

     


     

    3. React Router 시작하기

    (1) 기본 설정

    우선 라이브러리를 설치해야 하는데

    React Router 코딩을 진행할 폴더를 생성해주고 본인이 사용하는 코드 에디터에 해당 폴더를 열어준다.

    그 후, 터미널을 열어 npx create-react-app . 명령을 실행함으로써 React 프로젝트를 새로 생성해보자.

     

    그리고 터미널에 아래 명령어를 입력하여 React Router 라이브러리를 설치해준다.

    npm install react-router-dom

     

    프로젝트에 React Router를 적용할 때에는 index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter라는 Component를 사용하여 감싸면 된다.

     

    이 Component는 웹 애플리케이션에 HTML5의 History API를 사용하여 페이지를 새로 불러오지 않고도 주소를 변경하고 현재 주소의 경로에 관련된 정보를 React Component에서 사용할 수 있도록 해 준다.

    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import App from './App';
    import { BrowserRouter } from 'react-router-dom';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
        <BrowserRouter>
          <App />
        </BrowserRouter>
    );

     

    이제 본격적으로 코딩을 시작해보자.

     

    우선 각 페이지에서 사용할 Component를 만들 차례인데

    사용자가 웹 사이트에 들어오게 됐을 때 가장 먼저 보여지게 될 <Home> 페이지 Component와 웹 사이트를 소개하는 <About> 페이지 Component를 만들어보자.

     

    src 디렉토리에 components 폴더를 생성하고 그 안에 다음 파일들을 생성한다.

    // src/components/Home.js
    import React, { Component } from 'react'
    
    export default class Home extends Component {
      render() {
        return (
          <div>
            <h1>Home</h1>
            <p>가장 먼저 보여지는 페이지입니다.</p>
          </div>
        )
      }
    }
    // src/components/About.js
    import React, { Component } from 'react'
    
    export default class About extends Component {
      render() {
        return (
          <div>
            <h1>About</h1>
            <p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
          </div>
        )
      }
    }

     

    (2) Route

    각 페이지에서 사용할 Component들이 준비되었다.

    이제 핵심인 사용자의 브라우저 주소 경로에 따라 우리가 원하는 Component를 보여주기 위해서

    Route라는 Component를 통해 Route 설정을 해주면 된다.

     

    Route Component는 아래와 같은 형식으로 사용한다.

    <Route path="주소" element={보여줄 Component.js}/>

    그리고 Route Component는 Routes Component 내부에서 사용 되어야 한다.

    [여기서 <Route/>에 대해]
     Component 태그 작성 시 <Route></Route> 이와 같이 열고 닫는 형태로 작성을 하는데
     이를 함축하는 방식이 <Route/> 이다.

     

    그럼 이어서 App Component를 다음과 같이 Route Component를 사용하여 Route 설정을 해보자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    
    function App() {
      return (
        <div className="App">
          <Routes>
            <Route path="/" element={<Home/>}/>
            <Route path="/about" element={<About/>}/>
          </Routes>
        </div>
      );
    }
    
    export default App;

     

    (3) Link

    이제 Route 기본 설정은 끝났으니

    Link Component를 사용해 다른 페이지로 이동하는 링크를 보여주는 방법을 알아보자.

     

    원래 웹 페이지에서 링크를 보여줄 때 a 태그를 사용했으나

    React를 사용하는 프로젝트에서는 a 태그 사용 시, 페이지를 새로 불러오며 이동하기 때문에 a 태그를 바로 사용하면 안 된다.

    Link Component 역시 a 태그를 사용하지만, 페이지를 새로 불러오는 것을 막고 History API를 통해 브라우저 주소의 경로만 바꾸는 방식으로 한다.

     

    Link Component는 아래와 같은 형식으로 사용하며

    react-router-dom 모듈의 기능이므로 react-router-dom에서 import하여 사용해야 한다.

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

     

    그럼 <Home> 페이지에서 <About> 페이지로 이동할 수 있도록 Link Component를 Home Component에서 사용해보자.

    // src/components/Home.js
    import React, { Component } from 'react'
    import { Link } from 'react-router-dom'
    
    export default class Home extends Component { 
      render() {
        return (
          <div>
            <h1>Home</h1>
            <p>가장 먼저 보여지는 페이지입니다.</p>
            <Link to="/about">소개</Link>
          </div>
        )
      }
    }
    

     

    (4) URL 파라미터와 쿼리스트링

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

    - URL 파라미터
     /profile/velopert

    - 쿼리스트링
     /articles?**page=1&keyword=react

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

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

     

    (4-1) URL 파라미터

    우선 URL 파라미터를 사용하는 방법을 알아보자.

    이를 위해 components 폴더에 Profile Component를 생성하여 아래와 같이 작성해보자.

    // src/component/Profile.js
    import { useParams } from 'react-router-dom';
    
    const data = {
      dong: {
        name: '강동원',
        description: '잘생긴 사람',
      },
      gil: {
        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 파라미터는 useParams라는 Hook을 사용하여 객체 형태로 조회 할 수 있다.

    URL 파라미터의 이름은 Route 설정을 할 때 Route Component의 path props를 통하여 설정한다.

    [여기서 Hook이란?]
     Hooks는 리액트 v16.8에 새로 도입된 기능으로, 함수형 Component에서 할 수 없었던 다양한 작업을 할 수 있게 해준다.
     대표적인 Hook에는 useState, useEffect, useContext 등이 있다.

     

    그 후 App Component를 아래와 같이 수정함으로써 링크에 따라 Profile Component가 보여지도록 하자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    
    function App() {
      return (
        <div className="App">
          <Routes>
            <Route path="/" element={<Home/>}/>
            <Route path="/about" element={<About/>}/>
            <Route path='/profiles/:username' element={<Profile/>}/>
          </Routes>
        </div>
      );
    }
    
    export default App;

    URL 파라미터는 /profiles/:username와 같이 경로에 :을 사용하여 설정한다.

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

     

    이제 <Profile> 페이지로 이동할 수 있도록 Home Component에 Link를 더 만들어보자.

    // src/component/Home.js
    import React, { Component } from 'react'
    import { Link } from 'react-router-dom'
    
    export default class Home extends Component {
      render() {
        return (
          <div>
            <h1>Home</h1>
            <p>가장 먼저 보여지는 페이지입니다.</p>
            <ul>
              <li>
                <Link to="/about">소개</Link>
              </li>
              <li>
                <Link to="/profiles/dong">강동원의 프로필</Link>
              </li>
              <li>
                <Link to="/profiles/gil">홍길동의 프로필</Link>
              </li>
              <li>
                <Link to="/profiles/void">존재하지 않는 프로필</Link>
              </li>
            </ul>
          </div>
        )
      }
    }

     

    (4-2) 쿼리스트링

    이번에는 쿼리스트링을 사용해보자.

    About Component를 아래와 같이 수정하자.

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

    위 Component에서 useLocation이라는 Hook을 사용했는데

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

     

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

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

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

    - hash
     주소의 # 문자열 뒤의 값
     (주소 History API가 지원되지 않는 구형 브라우저에서 클라이언트 Routing을 사용할 때 쓰는 Hash Router에서 사용한다.)

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

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

     

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

    직접 확인해보며 이해하기 위해 주소창에 http://localhost:3000/about?detail=true&mode=1를 입력해 결과를 확인해보자.

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

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

     

    원래는 번거로운 작업이나 다행히 React Router v6부터 useSearchParams라는 Hook을 통해 쿼리스트링을 쉽게 다룰 수 있게 되었다.

    // src/components/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>mode: {mode}</p>
          <button onClick={onToggleDetail}>Toggle detail</button>
          <button onClick={onIncreaseMode}>mode + 1</button>
        </div>
      );
    };
    
    export default About;

    useSearchParams는 배열 타입의 값을 반환하며, 첫번째 원소는 쿼리파라미터를 조회하거나 수정하는 메소드들이 담긴 객체를 반환한다.

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

     

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

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

     

    (5) 중첩된 Route-Nested Routing

    이번에는 React Router에서 중첩된 Route를 다룰 때 어떻게 해야 하는지 배워보자.

     

    게시글 목록을 보여주는 페이지와 게시글을 읽는 페이지를 만들어본다.

    components 디렉토리에 Articles, Article Component를 만들고 코드를 작성하자.

    // src/components/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/components/Article.js
    import { useParams } from "react-router-dom";
    
    const Article = () => {
      const {id} = useParams();
      return (
        <div>
          <h2>게시글 {id}</h2>
        </div>
      );
    };
    
    export default Article;

     

    이렇게 두 Component를 만들었다면

    해당 페이지들의 Route를 App Component에서 설정하자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    import Articles from './components/Articles';
    import Article from './components/Article';
    
    function App() {
      return (
        <div className="App">
          <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>
        </div>
      );
    }
    
    export default App;

    이제 http://localhost:3000/articles 주소로 접속하여 게시글 목록 페이지와 각 게시글 읽기 페이지도 잘 나타나는지 확인해보자.

     

    (5-1) 공통 레이아웃 Component

    게시글을 열었을 때, 게시글의 하단에 목록을 보여줘야 한다면 어떻게 해야 할까?

    만약 기존 방식으로 구현을 한다면 다음과 같이 ArticleList Component를 따로 만들어서 각 페이지 Component에서 사용해야 됐을 것이다.

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

     

    그러나 만약 중첩된 Route를 사용한다면 좀 더 나은 방식으로 구현할 수 있다.

    App Component를 아래와 같이 수정해보자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    import Articles from './components/Articles';
    import Article from './components/Article';
    
    function App() {
      return (
        <div className="App">
          <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>
        </div>
      );
    }
    
    export default App;

    그 다음에는 Articles Component에서 React Router에서 제공하는 Outlet이라는 Component를 사용해줘야 한다.

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

     

    Articles Component를 아래와 같이 수정하자.

    // src/components/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 Component가 사용된 자리에 중첩된 Route가 보여지게 된다.

    /articles/1 경로로 들어가면

    위와 같이 보여지게 된다.

     

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

    예를 들어서 <Home><About><Profile> 페이지에서 상단에 <header>를 보여줘야 하는 상황이라면

    아마 Header Component를 따로 만들어두고 각 페이지에서 재사용을 하는 방법을 제일 먼저 떠올릴텐데

    방금 배운 중첩된 Route와 Outlet을 활용하여 구현할 수 있다.

     

    예제를 만들어보며 이해해보자.

    우선 공통 레이아웃을 위한 Layout Component를 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;

    페이지 Component가 보여져야 하는 부분에 Outlet Component를 사용해준다.

     

    이제는 App Component를 아래와 같이 수정하자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    import Articles from './components/Articles';
    import Article from './components/Article';
    import Layout from './Layout';
    
    function App() {
      return (
        <div className="App">
          <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>
        </div>
      );
    }
    
    export default App;

    그럼 아래와 같은 화면으로 <Home><Profile> 페이지 상단에 header가 나타나게 된다.

    Outlet을 사용한 Component를 Route의 element로 사용함으로써 자식 Route 요소들에 공통으로 Layout Component가 보여지도록 한다.

     

    (5-2) index props

    Route Component에는 index라는 props가 있는데 이 props는 path="/"와 동일한 의미를 가진다.

    App Component를 아래와 같이 수정해보자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    import Articles from './components/Articles';
    import Article from './components/Article';
    import Layout from './Layout';
    
    function App() {
      return (
        <div className="App">
          <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>
        </div>
      );
    }
    
    export default App;

    / 경로로 들어갔을 때 여전히 <Home> 페이지가 잘 나오고 있다.

     

    index props는 상위 Route의 경로와 일치하지만, 그 이후에 경로가 주어지지 않았을 때 보여지는 Route를 설정할 때 사용한다.

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

     

    (6) React Router 부가기능

    이 외에도 React Router에는 웹 애플리케이션에서 Routing에 관련된 작업을 할 때 사용할 수 있는 유용한 API를 제공한다.

    그 중 자주 사용되는 것들에 대해 알아보자.

     

    (6-1) useNavigate

    useNavigateLink Component를 사용하지 않고 다른 페이지로 이동을 해야 하는 상황에 사용하는 Hook이다.

     

    Layout Component를 아래와 같이 수정해보자.

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

    그럼 Header에 버튼 두개가 생기며 누를 때 이전 페이지로 가거나 게시글 목록 페이지로 이동이 된다.

     

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

    navigate(-2)는 뒤로 두번 가고 navigate(1)은 앞으로 한번 간다.
    물론 앞으로 가는 건 뒤로가기를 한 상태여야 작동한다.

     

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

    방금 작성했던 goArticles 함수를 아래와 같이 수정해보자.

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

    그 다음에 / 경로로 들어가 <Home> 페이지를 띄우고 소개 링크를 눌러 <About> 페이지로 이동한다.

    그 후 상단의 게시글 목록 페이지를 눌러 이동 후, 브라우저의 뒤로가기 버튼을 눌러 이전 페이지로 이동을 해보면 소개 페이지로 갈 것 같지만 <Home> 페이지로 간다.

     

    (6-2) NavLink

    NavLink Component는 링크에서 사용하는 경로가 현재 Route의 경로와 일치하는 경우, 특정 스타일 또는 CSS 클래스를 적용하는 Component이다.

     

    이 Component를 사용할 때 style 또는 className을 설정할 때

    {isActive: boolean}을 파라미터로 전달받는 함수 타입의 값을 전달한다.

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

     

    그럼 Articles Component를 아래와 같이 수정해보자.

    // src/component/Articles.js
    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={`/article/${id}`}
            style={({isActive}) =>  (isActive ? activeStyle: undefined)}>
              게시글 {id}
          </NavLink>
        </li>
      );
    };
    
    export default Articles;

    게시글 목록에서 현재 보여지고 있는 게시글의 리스트가 초록색으로 되며 글씨 크기가 더 커지는 걸 볼 수 있다.

     

    (6-3) NotFound 페이지 만들기

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

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

     

    우선 components 디렉토리에 NotFound.js 파일을 생성하고 아래와 같이 작성해주자.

    // src/components/NotFound.js
    import React, { Component } from 'react'
    
    export default class NotFound extends Component {
      render() {
        return (
          <div style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            fontSize: 64,
            position: 'absolute',
            width: '100%',
            height: '100%'
          }}>
            404
          </div>
        )
      }
    }

     

    그 다음에 App Component를 아래와 같이 수정해보자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    import Articles from './components/Articles';
    import Article from './components/Article';
    import Layout from './Layout';
    import NotFound from './components/NotFound';
    
    function App() {
      return (
        <div className="App">
          <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>
        </div>
      );
    }
    
    export default App;

    여기서 *는 wildcard 문자인데 이는 아무 텍스트나 매칭한다는 뜻이다.

     

    해당 Route 엘리먼트의 상단에 위치하는 Route들의 규칙을 모두 확인하고 일치하는 Route가 없으면 이 Route가 화면에 나타나게 된다.

    그 후 주소창에 이제껏 정의하지 않은 경로를 입력하면 위와 같이 404 페이지가 뜨게 된다.

     

    (6-4) Navigate

    해당 Component는 Component를 화면에 보여주는 순간 다른 페이지로 이동하고 싶을 때 사용하는 Component이다.

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

     

    예를 들어 사용자의 로그인이 필요한 페이지인데 로그인을 안했다면 로그인 페이지를 보여줘야 할텐데 그런 상황에서 사용할 수 있다.

     

    우선 아래 두 Component를 components 디렉토리에서 생성 후 작성하자.

    // src/components/Login.js
    import React, { Component } from 'react'
    
    export default class Login extends Component {
      render() {
        return (
          <div>로그인 페이지</div>
        )
      }
    }
    // src/components/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를 가리킨다고 가정해보자.

     

    위 Component에서 만약 이 값이 false라면 Navigate Component를 통해 /login 경로로 이동한다.

    여기서 replace props는 useNavigate에서 설명한 것과 동일하게 페이지 이동 시 현재 페이지를 기록에 남기지 않아 뒤로가기를 눌렀을 때 2페이지 전의 페이지로 이동한다.

     

    이제 연결시키기 위해 App Component를 아래와 같이 수정하자.

    // src/App.js
    import { Route, Routes } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    import Profile from './components/Profile';
    import Articles from './components/Articles';
    import Article from './components/Article';
    import Layout from './Layout';
    import NotFound from './components/NotFound';
    import Login from './components/Login';
    import MyPage from './components/MyPage';
    
    function App() {
      return (
        <div className="App">
          <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>
        </div>
      );
    }
    
    export default App;

    그 다음 /mypage 경로로 이동해보면 페이지가 로딩되는 순간 바로 Login 페이지로 이동된다.

     


     

    이렇게 React Router에 대해 기본적인 개념과 사용방법, 그리고 유용한 API와 Hook에 대해서 알아봤다.

    다음번에는 Router와도 관련있는 Ajax에 대해 알아볼까 한다.

     

    'SPA > React.js' 카테고리의 다른 글

    [React] Firebase로 회원가입/로그인, 로그아웃  (0) 2022.10.26
    React.js(5)_React Redux  (0) 2022.09.28
    React.js(4)_React Ajax  (0) 2022.09.28
    React.js(3)_Ajax  (0) 2022.09.28
    React.js(1)_React란?  (0) 2022.09.28
Designed by Tistory.