TIL.7 | 리액트(React) 라우팅과 넥스트(Next.js) 라우팅

원용현·2022년 11월 4일
0

TIL

목록 보기
7/18

이전 글에서는 리액트와 넥스트의 장단점과 기본적인 특징인 SSR, CSR에 대해서 알아봤는데 리액트와 넥스트에서 가장 크게 차이를 보이는 부분이 바로 라우팅 방법이다. 라우팅이란 간단히 말해 유저가 원하는 URL에 따라 해당 URL에 맞는 페이지를 보여주는 것을 의미한다.

넥스트에서는 비교적 쉽게 라우팅할 경로만 지정해주면 되는데 리액트에서는 상대적으로 경로와 컴포넌트 설정에 대해서 복잡한 모습을 보인다. 이 글에서는 리액트와 넥스트의 라우팅 방법이 어떻게 다른지 알아보려고 한다.

리액트 라우팅(React Routing)

리액트 라우팅은 코드 기반 라우팅(Code Based Routing)으로 jsx나 js code를 작성해서 routes를 정의하고 페이지 구조를 만든다. 리액트에서는 react-router-dom 라이브러리를 설치하여 라우팅을 실시하는데 신규 페이지를 불러오지 않는 상황에서 각각의 url에 따라 선택된 데이터를 하나의 페이지에서 렌더링 해주는 라이브러리이다.

설치

아래 npm과 yarn 명령어를 통해 react-router-dom 라이브러리를 설치한다.

npm
npm install react-router-dom

yarn
yarn add react-router-dom

BrowserRouter 컴포넌트

react-router-dom에는 BrowserRouter라는 라우터 컴포넌트가 존재한다. BrowserRouter는 HTML5를 지원하는 브라우저를 감지한다.

BrowserRouter없이 코드를 작성하게 되면 다음과 같이 될 것이다. 아래의 컴포넌트들은 일반적인 마켓을 가정하여 배너, 메인페이지, 상품페이지 컴포넌트를 만들었다.

App.js

import "./App.css";
import Banner from "./component/banner/banner-container";
import Main from "./component/main/main-container";
import Product from "./component/product/product-container";

function App() {
  return (
    <div className="App">
      <Banner></Banner>
      <Main></Main>
      <Product></Product>
    </div>
  );
}

export default App;

Header.js

import "./header-presenter.css";

const Header = () => {
  return <div className="Header-wrapper">헤더입니다.</div>;
};

export default Header;

Main.js

import "./main-presenter.css";

const Main = () => {
  return <div className="Main-wrapper">메인페이지입니다.</div>;
};

export default Main;

Product.js

import "./product-presenter.css";

const Product = () => {
  return <div className="Product-wrapper">상품페이지입니다.</div>;
};

export default Product;

위와 같이 작성한 후에 실행을 해보면 다음과 같은 페이지가 나온다.

컴포넌트에 대해서 라우팅을 적용하지 않은 상태이기때문에 모든 페이지가 나온다. 따라서 브라우저의 현재 경로에 따라서 다르게 나오기 위해서 react-router-dom 라이브러리를 적용한다.

1. BrowserRouter 컴포넌트 적용
<BrowserRouter>를 적용하여 컴포넌트들을 모두 감싸준다.

2. Routes, Route 컴포넌트 적용
<Routes> 컴포넌트는 여러 개의 Route들을 감싸서 그 중 규칙이 일치하는 단 하나의 컴포넌트만을 렌더링 시켜주는 역할을 한다.
<Route> 컴포넌트는 path 속성에 경로, element 속성에는 컴포넌트를 넣어 준다. 여러 라우팅을 매칭하고 싶은 경우 URL 뒤에 *을 사용한다.

3. Link 컴포넌트 적용
웹 페이지에서는 원래 링크를 보여줄 때 <a> 태그를 사용한다. 하지만 클릭 시에 페이지를 새로 불러오기 때문에 <Link> 태그를 사용한다.

  • Link 컴포넌트를 사용하는데, 생김새는 a태그를 사용한다.
  • History API를 통해 브라우저 주소의 경로만 바꾸는 기능이 내장되어 있다.
  • <Link to='경로명'>링크명</Link>

없는 페이지에 대한 처리를 위해서 404 Not Found 페이지를 추가해서 위의 방식으로 고쳐주면 코드는 다음과 같아진다.

App.js

import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./App.css";
import Banner from "./component/banner/banner-container";
import Main from "./component/main/main-container";
import Product from "./component/product/product-container";

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Banner></Banner>
        <Routes>
          <Route path="/" element={<Main />}></Route>
          <Route path="/product/*" element={<Product />}></Route>
		  <Route path="/*" element={<NotFound />}></Route>
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

Header.js

import "./header-presenter.css";

const Header = () => {
  return <div className="Header-wrapper">헤더입니다.</div>
};

export default Header;

Main.js

import "./main-presenter.css";

const Main = () => {
  return <div className="Main-wrapper">메인페이지입니다.</div>
};

export default Main;

Product.js

import "./product-presenter.css";

const Product = () => {
  return <div className="Product-wrapper">상품페이지입니다.</div>
};

export default Product;

404.js

const NotFound = () => {
  return <div>404 Error</div>
};

export default NotFound;

/로 접근할 경우에 header 컴포넌트와 Main 컴포넌트만 페이지에 나오고 /product 페이지로 접근할 경우에 header 컴포넌트와 product 컴포넌트만 나오는 모습을 볼 수 있다.

URL 파라미터

/product/:productId과 같이 경로에 :를 설정하여 값을 얻어온다. /product/:productId/:productName과 같이 파라미터를 여러개 설정해서 사용하는 것도 가능하다.
useParams를 사용해서 파라미터를 받아오는 것이 가능한데 주의할 점은 선언하는 변수명을 URL 파라미터의 이름과 같게 선언을 해야 실제 값이 들어있으니 주의해서 사용한다.

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

const { productId } = useParams()

App.js

import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./App.css";
import NotFound from "./component/404/404";
import Header from "./component/header/header-container";
import Main from "./component/main/main-container";
import Product from "./component/product/product-container";

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Header></Header>
        <Routes>
          <Route path="/" element={<Main />}></Route>
          <Route path="/product/:productId" element={<Product />}></Route>
          <Route path="/*" element={<NotFound />}></Route>
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

Product.js

import { useParams } from "react-router-dom";
import "./product-presenter.css";

const Product = () => {
  const { productId } = useParams();

  return (
    <>
      <div className="Product-wrapper">{productId}번 상품페이지입니다.</div>
    </>
  );
};

export default Product;

파라미터의 값을 받아서 해당 값을 페이지의 구성요소로 사용하는 것이 가능해졌다.

쿼리 스트링

웹 주소 중에서 ?로 구성된 주소를 한번 쯤은 본적이 있을 것이다. 예를들어 http:/localhost:3000/product/1?t1=test1&t2=test2#test3과 같은 주소가 있다고 할 때 쿼리스트링을 사용하여 해당 값들을 얻어올 수 있다.

useLocation

  • hash : 주소의 #문자열 뒤의 값
  • pathname : 현재 주소 경로  
  • search : ?를 포함한 쿼리스트링  
  • state : 페이지로 이동시 임의로 넣을 수 있는 상태 값  
  • key : location 객체의 고유 값, 초기값은 default, 페이지가 변경될 때 마다 고유의 값이 생성된다.

Product.js

import { useLocation, useParams } from "react-router-dom";
import "./product-presenter.css";

const Product = () => {
  const { productId } = useParams();
  const location = useLocation();

  return (
    <>
      <div className="Product-wrapper">{productId}번 상품페이지입니다.</div>
      <ul>
        <li>hash : {location.hash}</li>
        <li>pathname : {location.pathname}</li>
        <li>search : {location.search}</li>
        <li>state : {location.state}</li>
        <li>key : {location.key}</li>
      </ul>
    </>
  );
};

export default Product;

위의 사진과 같이 주소에 적힌 내용들을 가져오는 것을 볼 수가 있다.

useSearchParams

  • ? 뒤의 key와 value 값의 쌍으로 연결된 값을 얻어올 때 사용한다.
  • state 처럼 배열의 비구조화 할당을 통해 값을 지정한다.
  • const [searchParams, setSearchParams] = useSearchParams()
  • setSearchParams에 객체 형태의 key, value 쌍을 넣어 쿼리스트링을 지정할 수 있다. 기본적으로 이전의 값에 덮어씌우므로 이전 값이 남아 있지 않음을 주의한다.

Product.js

import { useLocation, useParams, useSearchParams } from "react-router-dom";
import "./product-presenter.css";

const Product = () => {
  const { productId } = useParams();
  const location = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();
  const t1 = searchParams.get("t1");
  const t2 = searchParams.get("t2");
  const t3 = searchParams.get("t3");

  return (
    <>
      <div className="Product-wrapper">{productId}번 상품페이지입니다.</div>
      <ul>
        <li>hash : {location.hash}</li>
        <li>pathname : {location.pathname}</li>
        <li>search : {location.search}</li>
        <li>state : {location.state}</li>
        <li>key : {location.key}</li>
      </ul>
      <ul>
        <li>t1 : {t1}</li>
        <li>t2 : {t2}</li>
        <li>t3 : {t3}</li>
      </ul>
    </>
  );
};

export default Product;

넥스트 라우팅 (Next Routing)

리액트에서는 기본적으로 react-router-dom 라이브러리를 통해서 라우팅을 구현할 수 있었다. 넥스트는 이와는 다르게 넥스트 자체에서 라우팅을 위한 라이브러리 자체를 내장하고 있다. 리액트는 컴포넌트 단위로 페이지를 라우팅했다면, 넥스트에서는 파일 구조로 라우팅을 구현한다.

처음 yarn create next-app을 통해서 넥스트 프로젝트를 생성하면 기본적으로 pages라는 폴더가 생성된다. 해당 폴더가 페이지에 접속할 때 기본적으로 나오는 루트('/')경로와 같다.

폴더 안에 index.js 파일이 만들어지면 해당 파일이 경로에 대해서 실행되는 파일이 되며, pages 폴더 내부에 새로운 폴더를 생성하면 해당 폴더의 이름이 주소의 경로가 된다.

위의 파일 구조에서 /pages 안의 index.js는 '/'이며 /pages/product/[productId]안의 index.js가 '/product/[productId]'가 될 것이다.

동적 라우팅(Dynamic Routing)

넥스트에서는 동적 라우팅을 지원한다. '/product/1'이나 '/product/2'로 접근을 한다면 /pages/product/[productId]에서 html, css, js를 받아오게 된다.

동적 라우팅 된 정보를 이용해서 js를 실행할 때, 데이터를 받아오는 것 등에서 다른 동작을 가져오도록한다.

해당 정보들은 next/router에서 지원하는 useRouter()를 사용해서 가져온다.

import { useRouter } from 'next/router'

export default function NextPage() {
	const router = useRouter()
    
    return (
    	<h1>이 페이지의 제품 id는 {router.query.productId}입니다.</h1>
    )
}

위와 같이 작성하면 useRouter를 통해서 주소에서 원하는 정보를 가져오는 것이 가능하다.

주의할 점은 폴더 구조를 지정할 때 []로 묶어서 폴더를 생성해야 router.query에서 정보를 가져올 수 있다. []에 적은 것이 router.query 객체의 키로 사용된다.

0개의 댓글