React-Router

✒️ 2025-05-28 10:26 내용 수정


React-Router

Component 설명
<BrowserRouter> 브라우저 주소창에 clean URL을 사용하여 현재 위치를 저장하고 이동을 관리함
<Routes> 어플리케이션 어디에서나 렌더되며, 현재 위치와 맞는 자식 Route를 매칭시킴
Routes Component 안에 Route Component를 작성함
<Route> URL과 component들을 이어주고 데이터 로딩과 복제를 연결함
Route 안에 하위 Route를 넣을 수 있음 (/test/child)
<Outlet> 부모 Route element 안의 하위 Route의 element를 렌더링할 위치를 지정함
<Link> a태그 대신 React에서 사용하며, 클릭 시 다른 페이지로 이동시켜주는 역할
<Navigate> Link와 비슷하게 렌더링 시 현재의 위치를 바꿔주는 역할. useNavigate Hook으로 대신할 수 있음
button 태그의 onClick등과 함께 사용하여 페이지 이동을 처리할 수 있음
import {Routes, Route} from 'react-router-dom'; 

<Routes>
	<Route path='/경로' element={<Component param={param}></Component>}>
		<Route path='경로' element={<Component2></Component2>}></Route>
	</Route>
	<Route path='/경로/:id' element={<Component3 param={param}></Component3>}></Route>
</Routes>
import {Link, useNavigate} from 'react-router-dom'; 

const navigate = useNavigate();

<Link to="/경로"></Link>
<button onClick={()=>{navigate('/경로')}}>이동버튼</button>
<button onClick={()=>{navigate(-1)}}>이전 위치로 이동 버튼</button>

설치 및 사전 설정

  1. 먼저 각 페이지 및 필요한 부분 별로 Component를 사용하기 위해 파일 및 함수를 분리하여 작성한다.

    • src/component : 세부 내용을 작성하는 태그를 출력할 component
    • src/data : API, DB로부터 가져오거나, JSON, Javascript 등으로 작성된 데이터
    • src/router : 페이지 단위의 component.
    • public/image : 이미지 파일
  2. VSC 터미널에서 npm i react-router-dom@6로 react-router v6 라이브러리를 다운 받는다.

  3. index.js : BroswerRouter를 import하고 Component를 추가한다.

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'; // BrowserRouter 가져오기

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter> {/*BrowserRouter 적용*/}
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

reportWebVitals();
  1. (선택사항) Bootstrap : 기존 bootstrap을 설치해서 사용하거나 react-bootstrap을 import해서 사용하는 방식 중 편한 방식으로 적용한다.
*{margin:0; padding:0; box-sizing: border-box;}
ul, ol, li{list-style: none;}
a{text-decoration: none; color:black;}

.sec{width: 100%;}

.visual{
  height: 400px;
  background-image: url('../public/image/bg.png');
  background-size: cover;
  background-position: center;
}

.col{text-align: center;}
.col img{width: 100%;}

.col.item a:hover{color:blue;}

.detail .col{
  display: flex; justify-content: center; align-items: center;
  text-align: left;
}
.detail img{width: 50%;}

페이지별 라우팅 설정

1. App.js : 전체 Route 설정

import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import data from './data/data.js';
import { useState } from 'react';
import { Routes, Route, Link, Outlet } from 'react-router-dom';
import Main from './router/Main';
import Header from './component/Header';
import Detail from './router/Detail';
import About from './router/About';
import Event from './router/Event';

function App() {

  const imageName = '/image/shoe';
  const [items, setItems] = useState(data);

  return (
    <>
	  {/* Header는 Component로 만들어 가져온다 */}
      <Header></Header>
      
      <section className='visual'></section>
      
      <Routes> {/* Routes 내의 Route를 추가한다. */}
	    {/* 라우팅할 경로, 출력할 element(Component)를 작성한다. */}
        <Route path='/' element={<Main items={items} imageName={imageName}></Main>}></Route>
        {/* detail의 경우 parameter로 id를 받는다 */}
        <Route path='/detail/:id' element={<Detail items={items} imageName={imageName}/>}></Route>
        {/* about은 하위 경로로 /about/member, /about/location 을 가진다 */}
        <Route path='/about' element={<About />}>
          {/* 하위 경로에는 /를 작성하지 않고, About.js의 <Outlet>에 element가 출력된다 */}
          <Route path='member' element={<div>members</div>}></Route>
          <Route path='location' element={<div>location</div>}></Route>
        </Route>
        {/* event도 about처럼 /event/one, /event/two를 하위 경로로 가진다 */}
        <Route path='/event' element={<Event />}>
          {/* Event.js의 <Outlet>에 element가 출력된다 */}
          <Route path='one' element={<div>첫 주문 시 10% 할인</div>}></Route>
          <Route path='two' element={<div>생일기념 쿠폰받기</div>}></Route>
        </Route>
        {/* 요청이 잘못된 경우엔 에러를 알려주는 문구를 렌더링한다 */}
        <Route path="*" element={<h2>404! 요청하신 페이지가 없습니다!</h2>}></Route>
      </Routes>
    </>
  );
}

export default App;

2. Header : 페이지 이동 설정

import 'bootstrap/dist/css/bootstrap.min.css'; // react-bootstrap 사용 시 두 줄 모두 추가해야 한다
import {Navbar, Container, Nav, NavDropdown} from 'react-bootstrap';

function Header() {

    return(
      <header>
        <Navbar bg="dark" data-bs-theme="dark">
            <Container>
	            {/* 메인 페이지의 경로를 추가 */}
                <Navbar.Brand href="/">Shoes Shop</Navbar.Brand>
                <Navbar.Toggle aria-controls="dropdown" />
                <Navbar.Collapse id="dropdown">
                    <Nav>
                        <NavDropdown
                        id="nav-about-dropdown"
                        title="About"
                        menuVariant="dark"
                        >
                        {/* href에 라우팅 경로를 입력하며, 하위 경로까지 입력하면 해당 Component를 렌더링할 수 있다. */}
                        <NavDropdown.Item href="/about/member">멤버</NavDropdown.Item>
                        <NavDropdown.Item href="/about/location">위치</NavDropdown.Item>
                        </NavDropdown>
                    </Nav>
                    <Nav>
                        <NavDropdown
                        id="nav-event-dropdown"
                        title="Event"
                        menuVariant="dark"
                        >
                        <NavDropdown.Item href="/event/one">서비스</NavDropdown.Item>
                        <NavDropdown.Item href="/event/two">생일</NavDropdown.Item>
                        </NavDropdown>
                    </Nav>
                </Navbar.Collapse>
            </Container>
        </Navbar>
      </header>
    )
}

export default Header; 

3. 메인 페이지 : parameter를 통한 페이지 이동 추가

import Item from '../component/Item';

function Main(props) {
	// 전달 받은 데이터를 다시 Item Component에 넘겨준다
    let {items, imageName} = props;
  
    return(
      <>
	    {/* Main 페이지에 출력될 내용 */}
        <section className='sec'>
          <div className='container'>
            <div className='row'>
            {
              items.map((el, i)=>{
                return(
                  {/* 상세 내용은 Item Component에서 작성해서 출력한다 */}
                  <Item key={el.id} i={i} item={el} imageName={imageName}></Item>
                )
              })
            }
            </div>
          </div>
        </section>
      </>
    )
}

export default Main;
import { Link } from "react-router-dom";

function Item(props) {
    {/* Main Component로부터 받은 데이터를 사용해 상세 내용을 출력한다 */}
    let {i, item, imageName} = props;
    let {id, title, content, price} = item;
  
    return(
      <div className='col md-4 item'>
        {/* process.env.PUBLIC_URL 는 웹 상에서 파일 경로를 찾을 때 사용 */}
        <Link to={'/detail/'+id}>
            <img src={process.env.PUBLIC_URL + imageName + (id) + '.jpg'} alt={imageName[i]}></img>
            <h3>{title}</h3>
            <p>
            <span className='d-block'>{content}</span>
            </p>
        </Link>
      </div>
    )
}

export default Item;

react route 1.png

4. 상세보기 : parameter를 받아 상세 데이터 조회

import { useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import DetailItem from '../component/DetailItem';

function Detail(props) {
  {/* App Component로부터 전달 받은 데이터를 사용 
	  useParams로 URL에 포함된 parameter를 받을 수 있다
  */}
  
  let {id} = useParams();
  let {items, imageName} = props;
  let item = items.find((el)=>(el.id == id)); {/* 조건을 만족하는 요소를 반환 */}
  let [banner, setBanner] = useState(true); {/* 배너가 보이는지 저장하는 state 변수 */}

  useEffect(()=>{ {/* 처음 페이지 렌더링 후 1회만 effect를 실행하고, clean-up으로 setTimeout을 제거한다 */}
    let timer = setTimeout(()=>{
      setBanner(false);
    }, 2000);
    return ()=>clearTimeout(timer);
  },[])

    return(
      <>
        {
          (banner) ? <div className="banner">
            2초 후 사라지는 배너
          </div> : null
        }
        <section className='sec detail'>
          <div className='container'>
            <div className='row row-cols-1'>
              {/* 상세 내용은 DetailItem Component에서 만들어 출력한다 */}
              <DetailItem key={id} id={id} item={item} imageName={imageName}></DetailItem>
            </div>
          </div>
        </section>
      </>
    )
}

export default Detail;
import {Button} from 'react-bootstrap'; // react-bootstrap Button Component

function DetailItem(props) {
	{/* Detail로부터 전달 받은 데이터를 가져온다 */}
    let {id, item, imageName} = props;
    let {title, content, price} = item;

    return(
      <div className='col mt-4'>
        {/* process.env.PUBLIC_URL 는 웹 상에서 파일 경로를 찾을 때 사용 */}
        <img src={process.env.PUBLIC_URL + imageName + (id) + '.jpg'} alt={title}></img>
        <div className="info">
            <h3>{title}</h3>
            <p>
                <span className='d-block'>{content}</span>
                <span className='d-block'>{price} Won</span>
            </p>
            <button className="btn btn-danger">주문하기</button>
            {/* react-bootstrap의 Button Component 중 href는 a 태그를 버튼 형식으로 만든 것이다 */}
            <Button href='/'>메인으로</Button>
        </div>
      </div>
    )
}

export default DetailItem;

react route 2.png

5. 이벤트와 About : 하위 path의 내용을 Outlet으로 출력

import 'bootstrap/dist/css/bootstrap.min.css';
import { Outlet } from 'react-router-dom';

function Event(props) {
  
    return(
      <>
        <section className='sec about'>
            <div className='container'>
            <h2>오늘의 이벤트</h2>
            {/* Event의 하위 페이지들은 Outlet을 작성하면 나옴 */}
            <Outlet></Outlet> 
            </div>
        </section>
      </>
    )
}

export default Event;
import { Outlet } from 'react-router-dom';

function About(props) {
  
    return(
      <>
        <section className='sec about'>
            <div className='container'>
            <h2>About</h2>
            {/* About의 하위 페이지들은 Outlet을 작성하면 나옴 */}
            <Outlet></Outlet> 
            </div>
        </section>
      </>
    )
}

export default About;

react route 3.png

react route 4.png

react route 5.png