Stack/React

[React] Router / 중첩 라우팅

7ingout 2022. 6. 28. 11:02

https://reactrouter.com/

 

Declarative routing for React apps at any scale | React Router

Version 6 of React Router is here! React Router v6 takes the best features from v3, v5, and its sister project, Reach Router, in our smallest and most powerful package yet.

reactrouter.com

 

Router

페이지 이동

 

* 기존 웹페이지는 여러개의 페이지를 사용 (새로운 페이지를 로드하는 방식)

<a href = "sub.html">서브페이지</a>

 

* 리액트는 sap 방식

새로운 페이지를 로드하지 않고 하나의 페이지 안에서 필요한 데이터만 가져오는 형태를 가짐

 

* 라우팅

사용자가 요청한 url에 따라 해당 url에 맞는 페이지를 보여주는것

<BrowserRouter>

 

* React-Rounter: 신규페이지를 불러오지 않는 상황에서 각각의 url에 따라 선택된 데이터를 하나의 페이지에서 렌더링해주는 라이브러리

터미널 열고 npm install react-router-dom 치기 ~

 

<Link>

<NavLink> 링크 클릭시 컴포넌트에 active 클래스를 붙여줌 (클릭한 링크의 스타일을 지정할 수 있음)

 

* URL파라미터

- 사용법: 경로에 : 사용해서 설정한다.

ex> /product/:productId

<Route path = "/product/:productId">

<NavLink to="/product/1">

 

useParams() => 파라미터값을 가지고 있는 객체를 반환

 

* 쿼리스트링

주소창에 ?로 시작하는 애

product/1/신발?search=productName&q=demo#text

useLocation()

- hash: 주소의 # 문자열 뒤의 값 (#text => hash)

- pathname: 현재 주소 경로 (product/1/신발 => pathname)

- search: ?를 포함한 쿼리스트링 (?search=productName&q=demo => search)

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

=> 쿼리스트링 값을 가지고 있는 객체를 반환

 

중첩 라우팅

1. Outlet 사용

<Routes>

                <Route path="/" element={<Main/>}/>

                <Route path="/product" element={<Product/>}/>

                <Route path="/detail" element={<Detail/>}/>

                                    <Rounte path="detail1" element={</Detail1>}/>

                                    <Rounte path="detail2" element={</Detail2>}/>

                </Route>

</Routes>

 

return (

                <div>

                                상품상세보기입니다.

                                <Oultet />

                </div>


2. 부모 Route의 path 마지막에 /* 표시 ==> 중첩 라우터의 사용을 명시적으로 표시

자식 라우터는 부모 라우터 컴포넌트 안에 작성

 


 

App.js

import './App.css';
import Header from './components/Header';
import Main from './components/Main';
import Product from './components/Product';
import DetailProduct from './components/DetailProduct';
import NotFound from './components/NotFound';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Detail1 from './components/Detail1';
import Detail2 from './components/Detail2';
import Info from './components/Info';

function App() {
  return (
    <BrowserRouter>
    <div className="App">
      <Header/>
      {/* 
      Routes 컴포넌트는 여러 Route를 감싸서 그 중 url이 일치하는 라우트 단 하나만 랜더링 시켜줌 
      Route는 path 속성에 경로, element 속성에 컴포넌트를 넣어줌
      */}
      <Routes>
        <Route path='/' element={<Main/>} />
        <Route path='/product/:productId/:productName' element={<Product/>} />
        <Route path='/detail' element={<DetailProduct/>}>
          <Route path='detail1' element={<Detail1/>}/>
          <Route path='detail2' element={<Detail2/>}/>
        </Route>  
        <Route path="info/*" element={<Info/>}/>
        {/* 상단에 위치하는 경로를 모두 확인, 일치하는 경로가 없는 경우 처리 */}
        <Route path='*' element={<NotFound/>} />
      </Routes>
    </div>
    </BrowserRouter>
  );
}

export default App;

 

components/Header.js

import React from 'react';
import { NavLink } from 'react-router-dom'; // Link,

const Header = (props) => {
    return (
        <div>
            <h1>GreenMall</h1>
            <ul>
                {/* <li><Link to="/">메인페이지</Link></li>
                <li><a href="/">메인페이지</a></li> 
                {/* a태그는 새로고침 시켜줌 */}
                {/* <li><Link to="/product">상품페이지</Link></li>
                <li><Link to="/detail">상품자세히보기</Link></li> */} 

                {/* isActive일 때 클래스네임을 okay로 붙여줘 */}
                <li><NavLink to="/" className={({isActive})=>isActive? "okay" : ""}>메인페이지</NavLink></li>
                <li><NavLink to="/product/*/*" className={({isActive})=> "green " + (isActive? "okay" : "")}>상품페이지</NavLink></li>
                <li><NavLink to="/detail" className={({isActive})=>isActive? "okay" : ""}>상품자세히보기</NavLink></li>
                <li><NavLink to="/detail/detail1" className={({isActive})=>isActive? "okay" : ""}>1번상품자세히보기</NavLink></li>
                <li><NavLink to="/detail/detail2" className={({isActive})=>isActive? "okay" : ""}>2번상품자세히보기</NavLink></li>
                <li><NavLink to="/info" className={({isActive})=>isActive? "okay" : ""}>정보보기</NavLink></li>
            </ul>
        </div>
    );
};

export default Header;

 

components/Main.js

import React from 'react';
import { Link } from 'react-router-dom'

const Main = (props) => {
    return (
        <div>
            메인 페이지입니다.
            <ul>
                <li><Link to="/product/1/abc?search=productName&q=demo#test">1번상품</Link></li>
                <li><Link to="/product/2/ccc?search=greengreen&q=abc#2번test">2번상품</Link></li>
            </ul>
        </div>
    );
};

export default Main;

 

components/Product.js

import React from 'react';
import { useParams, useLocation } from 'react-router-dom'

const Product = (props) => {
    // useParams() 파라미터값을 가지고 있는 객체를 반환
    const { productId, productName } = useParams();
    // useLocation() 쿼리스트링 값을 가지고 있는 객체를 반환
    const location = useLocation();
    console.log(useParams());
    console.log(useLocation());
    return (
        <div>
            { productId }상품 페이지입니다.
            <div>{ productName }페이지입니다.</div>
            <ul>
                <li>hash: {location.hash}</li>
                <li>pathname: {location.pathname}</li>
                <li>search: {location.search}</li>
                <li>key: {location.key}</li>
            </ul>
        </div>
    );
};

export default Product;

 

components/DetailProduct.js

import React from 'react';
import { Outlet } from 'react-router-dom';

const DetailProduct = (props) => {
    return (
        <div>
            상품 자세히 보기입니다.
            <Outlet/>
        </div>
    );
};

export default DetailProduct;

 

conponents/Detail1.js

import React from 'react';

const Detail1 = (props) => {
    return (
        <div>
            상세보기 1입니다.
        </div>
    );
};

export default Detail1;


conponents/Detail2.js

import React from 'react';

const Detail2 = (props) => {
    return (
        <div>
            상세보기 2입니다.
        </div>
    );
};

export default Detail2;

 

components/Info.js

import React from 'react';
import { Link, Routes, Route } from 'react-router-dom'
import Info1 from './Info1';
import Info2 from './Info2';

const Info = (props) => {
    return (
        <div>
            정보 페이지입니다.
            <div>
                {/* 서브라우터는 / 없이 적어도 됨 */}
                <Link to="info1">정보1</Link>
                <Link to="info2">정보2</Link>
            </div>
            <Routes>
                <Route path="info1" element={<Info1/>}/>
                <Route path="info2" element={<Info2/>}/>
                <Route/>
            </Routes>
        </div>
    );
};

export default Info;

 

components/Info1.js

import React from 'react';

const Info1 = (props) => {
    return (
        <div>
            정보서브 컴포넌트 1입니다.
        </div>
    );
};

export default Info1;

 

components/Info2.js

import React from 'react';

const Info2 = (props) => {
    return (
        <div>
            정보서브 컴포넌트 2입니다.
        </div>
    );
};

export default Info2;

 

components/NotFound.js

import React from 'react';

const NotFound = (props) => {
    return (
        <div>
            페이지를 찾을 수 없습니다.
        </div>
    );
};

export default NotFound;

 

App.css

.App {
  text-align: center;
}
.active {
  background-color: pink;
  color: #fff;
}