[React] 리액트 라우터 v6.0 사용하기

soyeon·2022년 2월 21일
1
post-thumbnail

🐊 들어가며

<리액트를 다루는 기술> 13장 리액트 라우터로 SPA 개발하기를 공부하면서 정리한 내용입니다📚
중간에 모르는 것들은 따로 서치를 해서 공부했습니다.

🐊SPA란?

🍧SPA: 싱글 페이지 애플리케이션, 하나의 페이지로 이루어진 애플리케이션

멀티 페이지 애플리케이션

  • 싱글 페이지 애플리케이션이 생기기 이전, 멀티 페이지 애플리케이션을 사용했습니다.
  • 멀티 페이지 애플리케이션은 다른 페이지로 이동할 때마다 새로운 HTML를 받아옵니다.
  • 정적인 페이지에는 적합하지만 사용자 인터랙션이 많고 다양한 정보를 제공하는 현대의 웹에서는 적합하지 않습니다.

싱글 페이지 애플리케이션

  • 사용자와의 인터랙션이 발생하면 필요한 부분만 JS를 사용하여 업데이트하는 방식
  • 새로운 데이터가 필요할 경우 서버 API를 호출하여 필요한 데이터만 새로 불러옵니다.
  • HTML은 한번만 받아오고 이후에는 필요한 데이터만 받아와서 화면만 업데이트하기 때문에 현대의 웹에 적합합니다.

🐊리액트 라우터 기본 사용법

🍧라우팅 : 사용자가 요청한 URL에 따라 알맞은 페이지를 보여주는 것

설치하기

npm install react-router-dom

프로젝트에 라우터 적용

index.js 파일에 BrouserRouter 컴포넌트로 감싸면 됩니다.
index.js

import {BrowserRouter} from 'react-router-dom'

ReactDOM.render(
    <BrowserRouter>
    <App />
    </BrowserRouter>,
  document.getElementById('root')
);

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

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

❗ 컴포넌트는 <Routes> 안에 있어야합니다.
❗예시에서는 App 컴포넌트에 라우트를 작성했습니다.

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

Link를 사용하는 이유

  • 리액트 라우터를 사용하는 프로젝트에서는 a 태그를 사용하면 안됩니다.

    • a 태그를 클릭하여 이동하는 경우 브라우저에서는 페이지를 새로 불러오게 되기 때문입니다.
  • Link 컴포넌트에는 페이지를 새로 불러오는 것을 막고 History API를 통해 주소만 바꿔주는 기능이 내장되어 있습니다.

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

예)

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

function Home() {
return (
  <div>
    <h1></h1>
    <p>가장 먼저 보여지는 페이지입니다.</p>
    <ul>
      <li>
        <Link to="/about">소개</Link>
      </li>
		(...)
    </ul>
  </div>
);
}

🐊URL 파라미터와 쿼리스트링

  • URL 파라미터 예시 : /profile/velopert
  • 쿼리스트링 예시 : /articles?page=1&keyword=react

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

URL 파라미터 - useParams

  • Route 컴포넌트의 path props를 통해 설정합니다.

    예시)

  • App.js에 새로운 라우트를 작성합니다.

      <Routes>
      <Route element={<Layout />}>     
      <Route index element={<Home />} />
        //index = path="/"
      <Route path="/about" element={<About />} />
      <Route path="/profile/:username" element={<Profile />} />
    </Routes>
  • URL 파라미터 /profiles/:username과 같이 경로에 :를 사용하여 설정합니다.
  • 파라미터가 여러개인 경우 /profiles/:username/:field 같이 여러번 써먹을 수 있습니다.
  • 바로가기 링크들이 모여있는 Home.js 컴포넌트에서 Link의 path props를 통해 profile의 경로를 정해줍니다.

Home.js

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

function Home() {
  return (
    <div>
      <h1></h1>
      <p>가장 먼저 보여지는 페이지입니다.</p>
      <ul>
        <li>
          <Link to="/about">소개</Link>
        </li>
        <li>
          <Link to="/profile/soonmac">soonmac의 프로필</Link>
        </li>
        <li>
          <Link to="/profile/gildong">gildong의 프로필</Link>
        </li>
        <li>
          <Link to="/profile/void">존재하지 않은 프로필</Link>
        </li>
        <li>
          <Link to="/articles">게시글 목록</Link>
        </li>
      </ul>
    </div>
  );
}

Profile.js

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

const data = {
  soonmac: {
    name: "순대",
    description: "리액트 공부중",
  },
  gildong: {
    name: "홍길동",
    description: "홍길동전 주인공",
  },
};

function 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>
  );
}
  • data 객체에 프로필 정보들을 저장했습니다.
  const params = useParams();
  const profile = data[params.username];
  • useParams
  • username URL 파라미터를 통해 프로필을 조회한 뒤
  • 프로필이 존재하지 않으면 '존재하지 않는 프로필입니다.'라는 문구를 보여줍니다.

쿼리스트링 - useSearchParams

import {useSearchParams } from "react-router-dom";
//useSearchParams import하기

function About() {
    const [searchParams, setSearchParams] = useSearchParams();
  //useSearchParams(쿼리파라미터 객체, 쿼리파라미터 객체를 업데이트하는 함수)
  //useState랑 비슷함
    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})
    }
    console.log(searchParams);
    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>
    )
}
  const [searchParams, setSearchParams] = useSearchParams();

🍧useSearchParams(쿼리파라미터를 조회하거나 수정하는 메서드들이 담긴 객체, 업데이트 함수)

쿼리파라미터의 메서드들

  • get: 메서드를 통해 특정 쿼리파라미터를 조회
    • 조회 시 쿼리파라미터가 존재하지 않으면 null로 뜸
  • set: 메서드를 통해 특정 쿼리파라미터를 업데이트

❗ 쿼리파라미터 조회할 때 값은 무조건 문자열 타입!

      const onToggleDetail = () => {
        setSearchParams({mode, detail: detail === 'true' ? false: true})
    }

(true라고 하지 않고 'true'라고 썼음)

❗ 숫자를 다룬다면 parseInt를 사용하여 숫자타입으로 변환한다

     const onIncreaseMode = () => {
       const nextMode = mode === null ? 1 : parseInt(mode) + 1;
       setSearchParams({mode: nextMode, detail})
   }

🐊중첩된 라우트

게시글 페이지에서 게시글 하단에 목록 보여주기 - Outlet


여기서 게시글을 클릭하면

게시글 밑에 게시글 목록이 뜨도록 만듭시다!

🍧Outlet : Route의 children으로 들어가는 JSX 엘리먼트를 보여주는 컴포넌트

  • 각 페이지 컴포넌트가 보여져야하는 부분에 Outlet 컴포넌트를 사용하면된다
  • 먼저 게시글 목록 Route 컴포넌트 안에 게시글 Route 컴포넌트를 집어넣습니다.
    App.js
        <Route path="/articles" element={<Articles />}>
        	<Route path=":id" element={<Article />} />
      </Route>
  • 게시글 목록 컴포넌트에서 Outlet를 이용해 게시글 컴포넌트가 들어갈 곳을 지정해줍니다.
    Articles.js
function Articles() {
  return (
    <div>
      <Outlet />
      //여기에 children인 Article 컴포넌트가 보여짐
      <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>
  );
}

공용 레이아웃 컴포넌트 - Outlet

layout.js

function Layout() {
    return(
        <div>
            <header style={{background:'lightgray',padding: 16, fontSize:24}}>
				헤더
            </header>
            <main>
                <Outlet />
            </main>
        </div>
    )
}

🐊리액트 라우터 부가 기능

뒤로 가기 버튼 만들기 - useNavigate

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

Layout.js

  function Layout() {
    const navigate = useNavigate();
    const goBack = () => {
        navigate(-1)
  //파라미터가 숫자 타입 : 양수 - 앞으로 가기 / 음수 - 뒤로 가기
    }
    const goArticles = () => {
        navigate('/articles',{replace: true})
  //{ replace: true } : 뒤로가기를 눌렀을 때 전의 전 페이지가 나옴
    }
    return(
        <div>
            <header style={{background:'lightgray',padding: 16, fontSize:24}}>
                <button onClick={goBack}>뒤로가기</button>
                <button onClick={goArticles}>게시글 목록</button>
            </header>
            <main>
                <Outlet />
            </main>
        </div>
    )
}
  • useNavigate( ) : 양수 - 앞으로 가기 / 음수 - 뒤로 가기
  • { replace: true } :
    • 페이지를 이동할 때 현재 페이지를 기록에 남기지 않습니다.
    • 이동 후 뒤로 가기를 눌렀을 때 두 페이지 전의 페이지로 이동합니다.

🍧NavLink : 링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 or CSS 클래스 적용하는 컴포넌트

✨NavLink의 style과 className은 { isActive: boolean }을 파라미터로 전달받는다.

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

예시)

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

function 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>
  )
};


이런식으로 해당하는 글 항목에 스타일이 들어간다.

NotFound 페이지 만들기 - path=*(와일드카드)

🍧 * : 와일드카드 문자. 아무 텍스트나 매칭한다. 상단에 위치하는 라우트들의 규칙을 모두 확인하고 일치하는 라우트가 없다면 이 라우트가 화면에 나타난다.

예시)
App.js

    function App() {
  return (
    <Routes>
      <Route element={<Layout />}>     
      <Route index element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/profile/: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>
  );
}

페이지 리다이렉트하기 - Navigate 컴포넌트

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

로그인이 되어있지 않을 경우 로그인 창으로 리다이렉트하기

Login.js

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

export default Login;

MyPages.js

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

function MyPage() {
    const isLoggedIn = false;
    if(!isLoggedIn) {
        return <Navigate to ="/login" replace={true} />
    }
  //isLoggedIn이 false인 경우 : 로그인 페이지로 리다이렉트
  // replace props를 통해 '뒤로가기' 버튼을 눌렀을 때 페이지 전의 페이지로 이동하게끔
    return <div>마이페이지</div>
}

export default MyPage;

📎참고

<리액트를 다루는 기술> - 김민준(벨로퍼트), 길벗

profile
공부중

0개의 댓글