본문 바로가기

프론트엔드(Front-End)/React

[REACT] 리액트와 라우터(Router)

라우터란?

  • 화면에 보여질 컴포넌트를 동적으로 전환할 수 있도록 하는 기능입니다.
  • 구체적으로 설명하자면, 화면에 보여질 컴포넌트들을 사용자가 URL에 맞추어 구성하는 것입니다.
  • 리액트의 컴포넌트만으로도 화면을 구성할 수 는 있지만, 다양한 사용자의 요청에 대해서 동적으로 화면을 구성하고 동적으로 바꾸려면 라우팅 기능은 필수입니다.

index.js와 라우터

App.js 파일도 거슬러 올라가면 Router로 구성되어 있습니다. BrowserRouter라는 태그로 감싸진 형태입니다.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  
    
  
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: <https://bit.ly/CRA-vitals>
reportWebVitals();

리액트의 라우터를 사용하려면 리액트 라우터를 설치해야합니다.

npm install react-router-dom

위의 명령어를 입력하여 라우터를 설치합니다.

라우터 사용하기

  • 라우터를 설치했다면 라우터를 컴포넌트에서 사용하면 됩니다.
  • 아래의 소스코드처럼 라우터를 import해서 라우터 태그에 컴포넌트 태그를 assign 합니다.
import "./App.css";
import "./asset/Custom.css";
import "./asset/Router.css";

import { useState } from "react";
import { Route, Route, Link} from "react-router-dom";

// 컴포넌트 삽입
import HomeComp from "./components/HomeComp";
import AboutComp from "./components/AboutComp";

const App = () => {
	const [name] = useState("Router");
  return (
    <>
      <div className="main-header">
        <p>
          React의 <span>{name}</span> 에 대해 알아보기
        </p>
      </div>
      <div className="header-menu">
        <Link to="/">홈</Link>
        <Link to="/about">소개</Link>      
      </div>
      <Routes>
					<Route path="/" element={<HomeComp />} />
          <Route path="/about" element={<AboutComp />} />
      </Routes>
    </>
  );
};

export default App;

 

 

 


 

라우터와 Link 태그

  • 리액트의 라우터는 Link라고 하는 태그를 지원합니다.
  • Link 태그는 Route 태그를 통해 생성한 엘리먼트가 Routes 태그에 비로소 보이도록 제어합니다.
  • Link 태그는 Link 태그의 to 경로와 Route 태그의 path 경로가 같은 컴포넌트를 보여줍니다.
<Link to="/">홈</Link>
<Link to="/about">소개</Link>   

 

 


 

라우터의 url 파라미터

  • Route 태그의 path 속성이나 Link 태그의 to 속성은 주소를 가질 수 있습니다.
  • 이 주소 속에는 사용자가 요청한 정보가 담길 수 있습니다. (페이지 정보, 카테고리 분류 등)
  • 리액트에서는 이러한 주소에서 정보를 가져와 핸들링 할 수 있습니다.
import { useParams } from "react-router-dom";
const data = {
  jo: {
    name: "jwh",
    desc: "1-hee",
  },
  lee: {
    name: "lee",
    desc: "lee lee lee",
  },
};
const ProfileComp01 = () => {
  const params = useParams();
  const profile = data[params.username];
  return (
    <>
      <div className="comp func-comp route-comp">
        <h1>사용자 프로필</h1>
        {profile ? (
          <div>
            <h2> {profile.name}</h2>
            <p> {profile.desc}</p>
          </div>
        ) : (
          <p>존재하지 않는 프로필입니다.</p>
        )}
      </div>
    </>
  );
};

export default ProfileComp01;

ProfileComp01.js

import "./App.css";
import "./asset/Custom.css";
import "./asset/Router.css";

import { useState } from "react";
import { Route, Routes, Link } from "react-router-dom";

// 컴포넌트 삽입
import HomeComp from "./components/HomeComp";
import AboutComp from "./components/AboutComp";
import ProfileComp01 from "./components/ProfileComp01";

const App = () => {
  const [name] = useState("Router");
  const names = ["jo", "lee", "hey"];
  const linkList = names.map((name) => (
    <Link to={`/profiles/${name}`} element={ProfileComp01}>
      {name}
    </Link>
  ));
  return (
    <>
      <div className="main-header">
        <p>
          React의 <span>{name}</span> 에 대해 알아보기
        </p>
      </div>
      <div className="header-menu">
        <Link to="/">홈</Link>
        <Link to="/about">소개</Link>
        {linkList}
      </div>

      <Routes>
        {/* <Route path="/" element={<HomeComp />} /> */}
        {/* <Route index element={<HomeComp />} />{" "}
        <Route path="/about" element={<AboutComp />} /> */}
        {/* URL 파라미터로 링크 형성 */}
        <Route path="/" element={<HomeComp />} />
        <Route path="/about" element={<AboutComp />} />
        <Route path="/profiles/:username" element={<ProfileComp01 />} />
      </Routes>
    </>
  );
};

export default App;

→ App.js

 

 


 

라우터의 쿼리스트링

  • 사용자가 요청하는 경로 중에 ?path=123&color='yellow' 와 같이 사용자가 요청한 경로 중에서 key-value 쌍으로 이루어진 경로의 일부분을 쿼리스트링이라고 합니다.
  • 쿼리스트링은 http 통신의 get 방식에서 데이터를 송수신하는데 자주 활용됩니다.
  • 리액트의 라우터에서 쿼리스트링의 데이터를 핸들링 하려면 useLocation 이 필요합니다.
  • Hook의 일종이며, useLocation() 함수를 통해 location 객체를 얻어내어 프로퍼티로 여러 정보에 접근할 수 있습니다.
import { useLocation } from "react-router-dom";

const ProfileComp02 = () => {
  const location = useLocation();
  return (
    <>
      <div className="comp func-comp route-comp">
        <h1>쿼리스트링 예제</h1>
        <p>location 객체 정보</p>
        <p>path-name : {location.pathname}</p>
        <p>search : {location.search}</p>
        <p>hash : {location.hash}</p>
        <p>state : {location.state}</p>
        <p>key : {location.key}</p>
      </div>
    </>
  );
};

export default ProfileComp02;
  • 쿼리스트링의 정보는 location 객체로부터 얻어낼 수 있습니다.

 

 

useLocation()의 프로퍼티

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

 


 

쿼리스트링의 파싱

  • 쿼리스트링의 값을 파싱하기 위해서는 원래 npm qs 혹은 querystring 패키지를 설치하여 파싱했었습니다.
  • 그러나, 리액트 라우터에서는 v6 이후부터 useSearchParams라는 Hook를 통해서 쿼리스트링을 쉽게 다룰 수 있게 되었습니다.
  • 아래의 소스코드 참고!
import { useSearchParams } from "react-router-dom";

const UseSearchParamComp = () => {
  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 className="comp func-comp route-comp">
        <h1>useSearchParam 테스트</h1>
        <p> detail : {detail}</p>
        <p> mode : {mode}</p>
        <button onClick={onToggleDetail}> onToggleDetail</button>
        <button onClick={onIncreaseMode}> mode +1 </button>
      </div>
    </>
  );
};

export default UseSearchParamComp;

 


 

 

중첩된 라우트와 outlet

  • 게시글 목록에서 게시글로 이동했는데, 이동하기 전의 게시글 목록 등을 보고자 할 때,
  • 공통된 헤더 메뉴바 등을 사용할 때 응용 가능합니다.
  • 단, 라우트 태그를 무조건 중첩하여 작성한다고 되는 것이 아니라 <outlet /> 태그도 같이 사용해 주어야 게시글 이동해도 목록이 보이는 등의 화면 구성이 가능합니다.

중첩 라우트와 oulet을 사용한 게시글 과 게시글 목록 같이 보기

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

const Articles = () => {
  const articleStyle = {
    color: "green",
    fontSize: 24,
  };

  const numbers = [1, 2, 3];
  const LinkList = numbers.map((number) => (
    <li>
      <Link to={`/articles/${number}`}>게시글 {number}</Link>
    </li>
  ));

  return (
    <>
      <div className="comp func-comp route-comp">
        <Outlet />
        <ul>{LinkList}</ul>
      </div>
    </>
  );
};

export default Articles;
  • 게시글 목록을 핸들링 할 컴포넌트 페이지 (Articles.js)
import { useParams } from "react-router-dom";

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

export default Article;
  • 실제 게시글을 렌더링 할 컴포넌트 페이지 (Article.js)
import "./App.css";
import "./asset/Custom.css";
import "./asset/Router.css";

import { useState } from "react";
import { Route, Routes, Link } from "react-router-dom";

// 컴포넌트 삽입
import HomeComp from "./components/HomeComp";
import AboutComp from "./components/AboutComp";
import ProfileComp01 from "./components/ProfileComp01";
import ProfileComp02 from "./components/ProfileComp02";
import UseSearchParamComp from "./components/UseSearchParamComp";
// 중첩 라우트용 리스트
import Article from "./components/pages/Article";
import Articles from "./components/Articles";
// Layout
import Layout from "./Layout";
// not Found
import NotFoundComp from "./components/NotFoundComp";
// 로그인 창
import LoginComp from "./components/LoginComp";
// 마이페이지
import MyPageComp from "./components/MyPageComp";

const App = () => {
  const [name] = useState("Router");
  const names = ["jo", "lee", "hey"];
  const linkList = names.map((name) => (
    <Link to={`/profiles/${name}`} element={ProfileComp01}>
      {name}
    </Link>
  ));
  return (
    <>
      <div className="main-header">
        <p>
          React의 <span>{name}</span> 에 대해 알아보기
        </p>
      </div>
      <div className="header-menu">
        <Link to="/">홈</Link>
        <Link to="/about">소개</Link>
        {linkList}
        <Link to="/profiles/comp02">쿼리스트링</Link>
        <Link to="/profiles/us-comp01">쿼리스트링-파싱</Link>
        <Link to="/articles">글목록</Link>
        <Link to="/hello/world">404 페이지 이동</Link>
        <Link to="/login">로그인</Link>
        <Link to="/my-page">마이페이지</Link>
      </div>

      <Routes>
        {/* <Route path="/" element={<HomeComp />} /> */}
        {/* <Route index element={<HomeComp />} />{" "}
        <Route path="/about" element={<AboutComp />} /> */}
        {/* URL 파라미터로 링크 형성 */}
        <Route element={<Layout />}>
          <Route path="/" element={<HomeComp />} />
          <Route path="/about" element={<AboutComp />} />
          <Route path="/profiles/:username" element={<ProfileComp01 />} />
          <Route path="/profiles/comp02" element={<ProfileComp02 />} />
          <Route path="/profiles/us-comp01" element={<UseSearchParamComp />} />
          <Route path="/articles" element={<Articles />}>
            <Route path=":id" element={<Article />} />
          </Route>
          <Route path="*" element={<NotFoundComp />} />
          <Route path="/login" element={<LoginComp />} />
          <Route path="/my-page" element={<MyPageComp />} />
        </Route>
      </Routes>
    </>
  );
};

export default App;
  • App.js


 

NavLink

  • Link 태그에 동적 스타일 속성이 추가된 태그입니다.
  • 선택된 태그에 대하여 자동으로 전용 CSS 속성을 부여 가능합니다.
import { NavLink, Link, Outlet } from "react-router-dom";

const Articles = () => {
  const articleStyle = {
    color: "green",
    fontSize: 24,
  };

  const numbers = [1, 2, 3];
  const NavLinkList = numbers.map((number) => (
    <li>
      <NavLink to={`/articles/${number}`} style={({ isActive }) => (isActive ? articleStyle : undefined)}>
        게시글 {number}
      </NavLink>
    </li>
  ));

  return (
    <>
      <div className="comp func-comp route-comp">
        <Outlet />
        <ul>{NavLinkList}</ul>
      </div>
    </>
  );
};

export default Articles;

 

 


 

NotFound

  • 존재하지 않는, 알 수 없는 주소로 사용자가 접근하려고 할 경우에 표시할 페이지를 구성 가능합니다.
  • 404 오류에 전용적으로 사용 가능하며 서버 오류에 대한 페이지 구성은 별도로 구상해야 합니다.
  • 라우터 태그의 path 속성에 * 링크를 넣은뒤 element 속성에 404 전용 컴포넌트를 할당해주면 됩니다.
const NotFoundComp = () => {
  return (
    <>
      <div className="comp func-comp route-comp">
        <h1>404 NOT FOUND</h1>
        <p>페이지를 찾을 수 없습니다.</p>
      </div>
    </>
  );
};

export default NotFoundComp;

 

 


 

Navigate 태그

  • 조건에 따라서 화면을 programmatically 하게 렌더링 해야할 경우 사용됨
  • 로그인을 안하면 마이페이지를 못보게 하는 등의 조건 설정에 용이합니다.
import { useState } from "react";
import { Navigate } from "react-router-dom";

const LoginComp = () => {
  const [text, setText] = useState("");
  const [login, setLogin] = useState(false);
  const handleKeyDown = (e) => {
    if (e.key === "Enter") {
      if (e.target.value) {
        alert("로그인 되었습니다.!");
        setText("");
        setLogin(true);
      }
    }
  };

  const handleOnChange = (e) => {
    setText(e.target.value);
  };

  if (login) {
    return <Navigate to="/my-page" replace={true} />;
  }
  return (
    <>
      <div className="comp class-comp login-comp">
        <span>ID : </span>
        <input placeholder="아이디를 입력해주세요" value={text} onKeyDown={handleKeyDown} onChange={handleOnChange} />
        <p>입력 값 : {text}</p>
      </div>
    </>
  );
};

export default LoginComp;

→ LoginComp.js

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

const MyPageComp = ({ log }) => {
  if (!log) {
    return <Navigate to="/login" replace={true} />;
  }
  return <div>마이페이지</div>;
};

export default MyPageComp;

MyPageComp.js

  • 현재는 로그인 했으면 마이페이지 / 안했으면 로그인 기능이 실제 구현된 것은 아님
  • props로 boolean 값을 주어 페이지 표시할 지 안 할 지 결정 가능한데 부모 컴포넌트에서 수동으로 값을 변경해주어야 함!
import "./App.css";
import "./asset/Custom.css";
import "./asset/Router.css";

(... 중략 ...)

const App = () => {
 (... 중략 ...)

  return (
    <>
      <div className="main-header">
        <p>
          React의 <span>{name}</span> 에 대해 알아보기
        </p>
      </div>
      <div className="header-menu">
        (... 중략 ...)

      </div>

      <Routes>      
        <Route element={<Layout />}>
					(... 중략 ...)
          <Route path="/login" element={<LoginComp />} />
          <Route path="/my-page" element={<MyPageComp />} />
        </Route>
      </Routes>
    </>
  );
};

export default App;

→ App.js