(React) 쇼핑몰 만들기 - 리액트 라우터 - 상세페이지 구현

고민지·2022년 7월 23일

React

목록 보기
15/25
post-thumbnail

상세페이지가 보여줄 html을 담기 위해 Detail.js 파일을 생성한다.
아래 코드를 넣어준다.

/* eslint-disable */

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%"
          />
        </div>
        <div className="col-md-6">
          <h4 className="pt-5">상품명</h4>
          <p>상품설명</p>
          <p>120000</p>
          <button className="btn btn-danger">주문하기</button>
        </div>
      </div>
    </div>
  );
}

export default Detail;

그리고 App.js에서 아래코드로 변경한다.

<Routes>
	<Route
      path="/"
      element={
        <>
        <div className="main-bg"></div>
        <Shoe shoes={shoes}></Shoe>
      </>
      }
	/>
	<Route path="/detail" element={<Detail />} />
</Routes>

Detail Component를 적용한 모습이다.

이처럼 path가 "/"인 부분도 깔끔하게 하기 위해 Component를 만들어 정리해주자.

이제 routes 폴더를 따로 만들어 관리해주자.
동시에 component 폴더도 만들고 Shoe.js를 옮긴다.

routes 폴더 안에 Home.js 생성

/* eslint-disable */

import Shoe from "../components/Shoe";

function Home(props) {
  return (
    <>
      <div className="main-bg"></div>
      <Shoe shoes={props.shoes} />
    </>
  );
}

export default Home;

또 routes 폴더 안에 About.js 생성

/* eslint-disable */

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

function About() {
  return (
    <div>
      <h3>About 페이지</h3>
      <Outlet></Outlet>
    </div>
  );
}

export default About;

Shoe.js 수정

/* eslint-disable */

function Shoe(props) {
  return (
    <div className="container">
      <div className="row">
        {props.shoes.map((item, index) => {
          return (
            <div className="col-md-4" key={index}>
              <img src={item.src} width="80%" />
              <h4>{item.title}</h4>
              <p>{item.content}</p>
              <p>{item.price}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default Shoe;

App.js 수정

/* eslint-disable */

import "./App.css";
import { Button, Navbar, Container, Nav } from "react-bootstrap";
import { Routes, Route, Link, useNavigate, Outlet } from "react-router-dom";
import { useState } from "react";

import data from "./shoes";

import Detail from "./routes/Detail";
import Home from "./routes/Home";
import About from "./routes/About";

function App() {
  const [shoes, setshoes] = useState(data);
  const navigate = useNavigate();

  return (
    <div className="App">
      <Navbar bg="dark" variant="dark">
        <Container>
          <Navbar.Brand
            onClick={() => {
              navigate("/");
            }}
          >
            My shop
          </Navbar.Brand>
          <Nav className="me-auto">
            <Nav.Link
              onClick={() => {
                navigate("/");
              }}
            >
              Home
            </Nav.Link>
            <Nav.Link
              onClick={() => {
                navigate("/detail");
              }}
            >
              Detail
            </Nav.Link>
          </Nav>
        </Container>
      </Navbar>

      <Routes>
        <Route path="/" element={<Home shoes={shoes} />} />
        <Route path="/detail" element={<Detail shoes={shoes} />} />
        <Route path="/about" element={<About />}>
          <Route path="member" element={<div>멤버정보</div>} />
          <Route path="location" element={<div>위치정보</div>} />
        </Route>
        <Route path="*" element={<div>없는 페이지입니다.</div>} />
      </Routes>
    </div>
  );
}

export default App;

코드가 훨씬 보기 깔끔해졌다.
Home 과 Detail 페이지에 props 를 이용하여 shoes를 전달해주었다.

Route 안에 Route (nested route)

/about/member, /about/location 으로 접속하면 <About/> 페이지와 그 안에 있는 <Outlet/> 위치에 각 element 요소가 나타난다.

Route path="*"

path에 없는 주소로 이동할때 보여주는 페이지

App.js에서
Link 태그는 안이쁘니 지워버리고 useNavigate()을 이용해 메뉴바에 링크를 간단하게 걸어주었다.

navigate(1) 👉 앞으로가기
navigate(2) 👉 2번앞으로가기
navigate(-1) 👉 뒤로가기
navigate(-2) 👉 2번뒤로가기
.
.

상세페이지에 데이터 바인딩을 해보자.

/* eslint-disable */

function Detail(props) {
  return (
    <div className="container">
      <div className="row">
        <div className="col-md-6">
          <img
            src={props.shoes[0].src}
            width="100%"
          />
        </div>
        <div className="col-md-6">
          <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>
        </div>
      </div>
    </div>
  );
}

export default Detail;

App.js 에서 /detail/ 뒤에 어느 글자가 와도 <Detail/> 페이지로 이동할 수 있게 수정해보자.

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

그리고 Detail.js 를 아래와 같이 수정해준다.

/* eslint-disable */

import { useParams } from "react-router-dom";
import shoes from "../shoes";

function Detail() {
  const { id } = useParams();
  const shoe = shoes.filter((shoe) => shoe.id === Number(id));
  if (shoe.length === 0) {
    return (
      <div>
        <h3>해당 상품은 존재하지 않습니다.</h3>
      </div>
    );
  } else {
    return (
      <div className="row">
        <div className="col-md-6">
          <img src={shoe[0].src} width="100%" />
        </div>
        <div className="col-md-6">
          <h4 className="pt-5">{shoe[0].title}</h4>
          <p>{shoe[0].content}</p>
          <p>{shoe[0].price}</p>
          <button className="btn btn-danger">주문하기</button>
        </div>
      </div>
    );
  }
}

export default Detail;

useParams() 가 :id 부분에 있는 글자를 가져와준다.
그 글자를 id라는 변수에 넣고 이 변수는 String 이기 때문에 Number()로 숫자로 변형해줘야 사용할 수 있다.
그리고 filter() 를 사용하여 속성 id가 해당 숫자와 같은 객체의 배열을 shoe 변수에 담는다.
filter()의 반환 값은 배열인 것을 주의해야한다!

그런데, :id에 물품의 id가 아닌 이상한 글자가 오면?
if (shoe.length === 0) 조건식을 이용하여 해당 상품이 없다고 보여주면 된다.

이제 detail/상품번호 이렇게 하면 id가 상품번호와 같은 상품 상세화면을 보여줄 수 있다.

profile
도전 성취 성장을 향한 개발자

0개의 댓글