본문 바로가기
JavaScript/React

React) 리액트 라우터로 SPA 개발하기 - useNavigate, NavLink, NotFound, Navigate)

by 박채니 2022. 11. 23.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

리액트 라우터로 SPA 개발하기

 

리액트 라우터 부가 기능

 

useNavigate

 

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

Layout.js

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

// 헤더 레이아웃
const Layout = () => {
    const navigate = useNavigate();

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

    const goArticles = () => {
        // 게시글 목록으로 이동
        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;

뒤로 가기 버튼을 누르면 이전 페이지로 이동, 게시글 목록 버튼을 누르면 게시글 목록 페이지로 이동하는 것을 확인할 수 있습니다.

만일 2페이지 전으로 이동을 원한다면 'navigate(-2)', 앞 페이지로 이동을 원한다면 'navigate(1)'로 해줄 수 있습니다.

 

이 중 replace 옵션이 있는데, 페이지를 이동할 때 현재 페이지를 페이지 기록에 남기지 않습니다.

 

Layout - goArticles

    const goArticles = () => {
        // 게시글 목록으로 이동
        navigate('/articles', { replace: true});
    }

영상을 확인해보면 뒤로 가기 버튼 클릭 시 직전 페이지인 About이 나와야할 것 같지만, Home 페이지가 나온 것을 확인할 수 있습니다.

 

NavLink

 

  • 링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 또는 CSS 클래스 적용
  • style과 className은 { isActive: boolean }을 파라미터로 받는 함수 타입의 값을 전달해줌

예시

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

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

 

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;

{isActive: false, isPending: false}을 반환하고 이 중 isActive를 이용해 경로가 일치하는 경우를 찾아 스타일을 적용하였습니다.

 

하지만 위 코드를 보면 중복이 많아 이를 리팩토링 하여 사용하는 것이 적합할 것 같습니다.

 

Article.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={`/articles/${id}`} style={({ isActive }) => isActive ? activeStyle : undefined}>
                게시글 {id}
            </NavLink>
        </li>
    );
};

export default Articles;

 

NotFound 페이지 만들기

 

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

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;

 

App.js

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

function 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 문자로 아무 텍스트나 매칭한다는 뜻을 가지고 있습니다.

해당 라우트 엘리먼트의 상단에 위치하는 라우트들을 모두 확인 후 일치하는 라우트가 없다면 해당 라우트가 화면에 나타나게 됩니다.

 

Navigate 컴포넌트

 

  • 컴포넌트를 화면에 보여주는 순간 다른 페이지로 이동하고 싶을 때 사용 (리다이렉트 처리)
  • 예) 로그인 필요한 페이지에 로그인을 하지 않고 접근 시 로그인 페이지로 리다이렉트 처리할 때 사용

Login.js

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

export default Login;

 

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 값을 변경해주어 로그인 여부에 따라 페이지 렌더링을 달리 해줄 수 있습니다.

로그인을 하지 않고 mypage에 접속한다면 이를 검사하여 Navigate 컴포넌트를 통해 login 페이지로 리다이렉트 시켜줍니다.

또한 replace 옵션을 이용하여 두 페이지 전의 페이지로 이동하도록 하였습니다.

(왜냐하면 Home - MyPage → Login 리다이렉트 처리가 되었으므로 Login 페이지에서 뒤로 가기 버튼을 눌렀을 때 MyPage로 이동하게 됨 - 그럼 MyPage와 Login 리다이렉트가 반복 처리 되므로 사용자 불편! 따라서 그 전전 페이지인 Home으로 이동하게끔 replace 옵션 사용!)