REACT

[React]Router 페이지 분할하기

Router 페이지 분할하기

Router 시작하기

1.router-dom 설치

방법1. yarn add react-router-dom@5

방법2. npm install react-router-dom@5

2.router-dom 초기 설정

index.js 파일에서 router-dom 초기 설정이 필요하다.

방법1. BrowserRouter
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

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

// 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();
방법2. HashRouter
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { HashRouter } from 'react-router-dom';

ReactDOM.render(
  <React.StrictMode>
    < HashRouter>
      <App />
    </HashRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

// 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();
BrowserRouter와 HashRouter의 비교

HashRouter 방식은 라우팅을 할 때 잘못해서 서버 쪽으로 요청이 가지 않도록 한다. 주소에 #을 붙여 # 뒤에 오는 요청은 서버에 전달되지 않도록 해준다.

다음과 같은 오류 발생시 에디터 재실행

ERROR in ./node_modules/prop-types/node_modules/react-is/index.js

Router 사용법

Router를 import

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

<Router> 페이지 분할

<Route path=””>로 페이지를 분할할 수 있다.
      <Route path="/">
        // 해당 페이지의 html
      </Route>
      <Route path="/page">
        // 해당 페이지의 html
      </Route>
exact 속성 사용하여 주소 매칭할 수 있다.
import {Switch} from 'react-router-dom';
<Switch>
      <Route exact path="/">
        // 해당 페이지의 html
      </Route>
      <Route path="/page">
        // 해당 페이지의 html
      </Route>
</Switch>

라우팅을 하게 되면 매칭되는 모든 경로의 html을 불러오게 된다. exact 속성 혹은 <Switch> 사용하면 완전히 매칭되는 페이지만 불러오게 할 수 있다. 

컴포넌트를 담아서 사용할 수도 있다.
      <Route path="/경로" component={컴포넌트명}></Route>

 

쇼핑몰 프로젝트 Router 적용하기

페이지 분할

쇼핑몰의 메인페이지와 상품상세페이지 생성

      <Route exact path="/">
       // 메인페이지 html
      </Route>
      <Route path="/detail">
       // 상품상세페이지 html
      </Route>

메인페이지에 html 넣기

       <Route exact path="/">
        <Alert variant="success" className="background">
          <Alert.Heading>20% Season off</Alert.Heading>
          <p>
            Aww yeah, you successfully read this important alert message. This example
            text is going to run a bit longer so that you can see how spacing within an
            alert works with this kind of content.
          </p>
          <hr />
          <p className="mb-0">
            Whenever you need to, be sure to use margin utilities to keep things nice
            and tidy.
          </p>
        </Alert>
        <div className="container">
        <div className="row">
          {
            shoes.map((a,i)=>{ // a : shoes 배열 안의 각각의 데이터 
              return <Card shoes={shoes[i]} i={i} key={i} /> // shoes와 i를 props로 전송
            }) 
          }
        </div>
      </div>
      </Route>

상세페이지 html을 컴포넌트화해서 넣기

App.js
import Detail from './Detail';
      <Route path="/detail">
        <Detail/>
      </Route>
Detail.js
import React from "react";

function Detail(){
    return(
      <div className="container">
        <div className="row">
          <div className="col-md-6">
            <img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" alt="이미지"/>
          </div>
          <div className="col-md-6 mt-4">
            <h4 className="pt-5">상품명</h4>
            <p>상품설명</p>
            <p>120000원</p>
            <button className="btn btn-danger">주문하기</button> 
          </div>
        </div>
      </div> 
    )
  }

  export default Detail;

페이지 연결

네브바 메뉴 페이지 이동

Link 컴포넌트 import
import { Link, Route } from 'react-router-dom';
네브바  <Link> 적용
<Link to="/">Home</Link>
<Link to="/detail">Detail</Link>
<Nav.Link as={Link} to="/">Home</Nav.Link>
<Nav.Link as={Link} to="/detail">Detail</Nav.Link>

상세페이지 페이지 뒤로가기

상세페이지 뒤로가기 버튼 생성
<button className="btn btn-primary">뒤로가기</button> 
상세페이지 컴포넌트에서 클릭이벤트 적용
import React from "react";
// 1. useHistroy Hook을 import
import { useHistory } from 'react-router-dom';

function Detail(){

    // 2. useHistory 변수 생성
    let history = useHistory();

    return(
      <div className="container">
        <div className="row">
          <div className="col-md-6">
            <img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" alt="이미지"/>
          </div>
          <div className="col-md-6 mt-4">
            <h4 className="pt-5">상품명</h4>
            <p>상품설명</p>
            <p>120000원</p>
            <button className="btn btn-danger">주문하기</button> 
            <button className="btn btn-primary" 
            // 3. 클릭 이벤트에 useHistory Hook의 goBack() 함수 적용
            onClick={()=>{history.goBack();}}>뒤로가기</button> 
          </div>
        </div>
      </div> 
    )
  }

  export default Detail;

history.push("/") : 해당 페이지로 이동할 수 있다.

모든 페이지 html

<Switch>
      <Route exact path="/">
        // 메인페이지 html
      </Route>
      <Route path="/detail">
        <Detail/>
      </Route>
      <Route path="/:id">
        <div>모든 주소 요청에 이거 보여주셈</div>
      </Route>      
</Switch>

<Router>의 path=”/:id”는 모든 페이지 주소 요청을 의미한다. 모든 페이지에서 반드시 보여주는 UI를 넣을 때 사용할 수 있다. 여기서 <Switch>를 사용하면 위에서 설명한 exact와 마찬가지로 주소 매칭이 완벽하게 일치하는 UI만 보여준다. 즉, 경로 설정이 이미 완료된 메인페이지와 상세페이지에서는 해당 html을 보여주고 나머지 주소 요청에서 똑같은 html을 보여줄 수 있다.

상세페이지 데이터 바인딩

상품 데이터 전송

state에 저장된 shoes 데이터를 props로 전송
<Route path="/detail">
      <Detail shoes={shoes}/>
</Route>

props 전송은 상위 컴포넌트에서 하위 컴포넌트로 전송하는 것이 좋다.

Detail.js
import React from "react";
import { useHistory } from 'react-router-dom';

function Detail(props){

    let history = useHistory();

    return(
      <div className="container">
        <div className="row">
          <div className="col-md-6">
            <img src={"https://codingapple1.github.io/shop/shoes"+(props.shoes[0].id+1)+".jpg"} width="100%" alt="이미지"/>
          </div>
          <div className="col-md-6 mt-4">
            <h4 className="pt-5">{props.shoes[0].title}</h4>
            <p>{props.shoes[0].content}</p>
            <p>{props.shoes[0].price}</p>
            <button className="btn btn-danger">주문하기</button> &nbsp;
            <button className="btn btn-primary" onClick={()=>{history.goBack();}}>뒤로가기</button> 
          </div>
        </div>
      </div> 
    )
  }

  export default Detail;

상세페이지 파라미터 사용하기

useParams

상품상세페이지에 파라미터를 추가한다.
<!-- App.js -->

      <Route path="/detail/:id">
        <Detail shoes={shoes}/>
      </Route>
useParams Hook을 import한다.
// Detial.js

import { useHistory, useParams } from 'react-router-dom';
useParams를 변수에 담아준다.
    let { id } = useParams();
받아온 파라미터 데이터 바인딩한다.
 <div className="container">
        <div className="row">
          <div className="col-md-6">
            <img src={"https://codingapple1.github.io/shop/shoes"+(props.shoes[id].id+1)+".jpg"} width="100%" alt="이미지"/>
          </div>
          <div className="col-md-6 mt-4">
            <h4 className="pt-5">{props.shoes[id].title}</h4>
            <p>{props.shoes[id].content}</p>
            <p>{props.shoes[id].price}</p>
            <button className="btn btn-danger">주문하기</button> &nbsp;
            <button className="btn btn-primary" onClick={()=>{history.goBack();}}>뒤로가기</button> 
          </div>
        </div>
      </div> 

메인페이지 상품 컨테이너 연결하기

메인페이지에서 사용한 Card 컴포넌트에 클릭하면 상세페이지로 이동하게 만들어 준다.

function Card(props){
  return(
    <div className="col-md-4" onClick={()=>{history.push('/detail/'+props.shoes.id)}}>
        <img src={"https://codingapple1.github.io/shop/shoes"+(props.shoes.id+1)+".jpg"} width="100%" alt="이미지"/>
        <h4>{props.shoes.title}</h4>
        <p>{props.shoes.content}</p>
        <p>{props.shoes.price}</p>
    </div>
  )
}

 

결과 화면

메인페이지
상세페이지
최신글