본문 바로가기
React

[React] React로 간단한 H&M 사이트 만들기1

by IT 정복가 2023. 3. 20.
728x90

오늘은 React로 쇼핑몰 사이트인 H&M 사이트를 약간 간단한 버젼으로 만들어 볼 것이다.

 

우선 개발 순서는 아래와 같다.

 
1. 페이지는 총 전체 상품 페이지, 로그인 페이지, 상품상세 페이지 3개가 있다.
2. 전체 상품페이지에서 전체 상품을 볼 수 있다.
3. 로그인 버튼을 누르면 로그인 페이지가 나온다.
4. 상품 디테일을 눌렀으나. 로그인이 되어있지 않은 경우 로그인 페이지가 먼저 나온다.
5. 로그인이 되어있을 경우에는 상품 디테일 페이지를 볼 수 있다.
6. 로그아웃 버튼을 클릭하면 로그아웃이 된다.
7. 로그아웃이 되면 상품 디테일페이지를 볼 수 없으며 다시 로그인 페이지가 보인다.
8. 로그인을 하면 로그아웃 버튼이 보이고 로그아웃을 하면 로그인 버튼이 보인다.
9. 검색창을 통해 상품을 검색할 수 있다.

1.1 페이지 3개로 나누기 (전체 상품 페이지, 로그인 페이지, 상품 상세 페이지)

우선, 페이지를 여러개로 나누기 위해서 리액트 라우터를 설치하자.

npm install react-router-dom@6

그 후에 index.js에 와서 <React.StrictMode> 태그를 <BrowserRouter>로 바꾸어 <App />을 감싸주자.

index.js

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';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

 

그런 다음 페이지 3개를 아주아주 간단한게 만들어 보자.

전체 상품 페이지: ProductAll.js

로그인 페이지: Login.js

상품 상세 페이지: ProductDetail.js

ProductAll.js

import React from 'react'

const ProductAll = () => {
  return (
    <div>
      전체 상품 페이지
    </div>
  )
}

export default ProductAll

Login.js

import React from 'react'

const Login = () => {
  return (
    <div>
      로그인 페이지
    </div>
  )
}

export default Login

ProductDetail.js

import React from 'react'

const ProductDetail = () => {
  return (
    <div>
      상품 디테일 페이지
    </div>
  )
}

export default ProductDetail

 

그 다음 App.js에 와서 Route, Routes를 이용해 페이지를 바꿔줄 수 있는 기능을 추가하자.

App.js

import { Route, Routes } from 'react-router-dom';
import './App.css';
import Login from './page/Login';
import ProductAll from './page/ProductAll';
import ProductDetail from './page/ProductDetail';

function App() {
  return (
    <div>
      <Routes>
        <Route path="/" element={<ProductAll/>}/>
        <Route path="/login" element={<Login/>}/>
        <Route path="/product/:id" element={<ProductDetail/>}/>
      </Routes>
    </div>
  );
}

export default App;

여기까지 하고 페이지가 잘 바뀌는지 테스트를 해보자.

잘 나온다!


1.2 네비게이션 바 만들기

3개의 페이지가 계속 바뀌어도 네비게이션바는 항상 나오도록 만들어 줄 예정이다.

그렇기 때문에 <Routes> 태그 밖에 <Navbar />라는 컴포넌트를 만들어 준다.

App.js

function App() {
  return (
    <div>
      <Navbar/>
      <Routes>
        <Route path="/" element={<ProductAll/>}/>
        <Route path="/login" element={<Login/>}/>
        <Route path="/product/:id" element={<ProductDetail/>}/>
      </Routes>
    </div>
  );
}

 

Navbar.js

import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser } from '@fortawesome/free-regular-svg-icons'
import { faSearch } from '@fortawesome/free-solid-svg-icons'

const Navbar = () => {
    const menuList = ['여성','Divided','남성','신생아/유아', '아동','H&M HOME', '스포츠','Sale', '지속가능성']
  return (
    <div>
      <div>
        <div className='login-btn'>
            <FontAwesomeIcon icon={faUser} />
            <div className='login'>로그인</div>
        </div>

      </div>
      <div className='nav-logo'>
        <img width={150} src='https://i.pinimg.com/originals/31/5c/e5/315ce56cdff7fd54307760470bd9f2cf.png'/>
      </div>
      <div className='search'>
            <div className='search-bar'>
                 <FontAwesomeIcon icon={faSearch}/>
                <input type="text" placeholder='검색할 상품을 입력하세요.'/>
            </div>
           
        </div>
      <div className='menu-area'>
        <ul className='menu-list'>
            {menuList.map((menu)=>(<li>{menu}</li>))}
        </ul>
      </div>
    </div>
  )
}

export default Navbar

 

App.css

.login-btn{
  display: flex;
  justify-content: end;
  align-items: center;
  margin: 30px 30px 0 ;
}
.login{
  margin-left: 3px;
  cursor: pointer;
}

.nav-logo{
  display: flex;
  justify-content: center;
  align-items: center;
}

.menu-area{
  display: flex;
  justify-content: center;
  align-items: center;
}

.menu-list{
  display: flex;
  list-style: none;
  padding:0;
}
.menu-list li{
  padding:10px;
  cursor: pointer;
}

.search{
  display: flex;
  justify-content: end;
  align-items: center;
  
}

.search input{
  border: none;
  margin-left: 3px;
}

.search-bar{
  border-bottom: 2px solid black;
  margin-right: 30px;
  padding: 5px;
}


2. 전체 상품페이지에서 전체 상품 보여주기

(전체 상품 목록을 보여주려면 백엔드가 필요하다. 하지만 프론트를 공부하고 있으니 백엔드 없이 하는 방법으로 해보자.)

 

우선 json.server를 다운받자.

https://www.npmjs.com/package/json-server

 

json-server

Get a full fake REST API with zero coding in less than 30 seconds. Latest version: 0.17.2, last published: 24 days ago. Start using json-server in your project by running `npm i json-server`. There are 311 other projects in the npm registry using json-serv

www.npmjs.com

npm install -g json-server

그 후에 db.json 파일을 만들어 그 안에 데이터를 넣어준다.

아래는 db.json 안에 들어갈 데이터의 내용이다.

{
  "products": [
    {
      "id": 0,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/ec/ef/ecef9d77c56c519e24a76b83331ae2c18f73f50d.jpg],origin[dam],category[],type[LOOKBOOK],res[y],hmver[1]&call=url[file:/product/main]",
      "title": "벨티드 트윌 코트",
      "price": 99900,
      "choice": true,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 1,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/af/50/af50afa939c28ba24fd263fb573b4ad5f0858b14.jpg],origin[dam],category[],type[DESCRIPTIVESTILLLIFE],res[y],hmver[2]&call=url[file:/product/main]",
      "title": "슬림핏 맘 하이웨이스트 앵클 진",
      "price": 29900,
      "choice": true,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 2,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/7a/49/7a4980a16ce6888b33481763ea1f0a05807f50d1.jpg],origin[dam],category[],type[LOOKBOOK],res[y],hmver[1]&call=url[file:/product/main]",
      "title": "와이드 하이웨이스트 진",
      "price": 39900,
      "choice": false,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 3,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/10/34/10341727ce95aae07695e7b13392a1d802ffe1f3.jpg],origin[dam],category[],type[LOOKBOOK],res[y],hmver[1]&call=url[file:/product/main]",
      "title": "퍼프 슬리브 드레스",
      "price": 39900,
      "choice": false,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 4,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/0f/46/0f463d61dbddb48bea8e329d9693f17fbff7f925.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "프릴 디테일 블라우스",
      "price": 29900,
      "choice": true,
      "new": false,
      "size": ["S", "M", "L"]
    },
    {
      "id": 5,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/95/20/9520af2b69e69ef18fbad1d2673d47b1afe2b107.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "자카드 셔츠 드레스",
      "price": 39900,
      "choice": false,
      "new": false,
      "size": ["S", "M", "L"]
    },
    {
      "id": 6,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/66/eb/66ebc8ebbdc939176137327d43db5c22d2d0292f.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "더블 브레스티드 재킷",
      "price": 39900,
      "choice": true,
      "new": false,
      "size": ["S", "M", "L"]
    },
    {
      "id": 7,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/be/3c/be3cfb9b7d99937f42b99b859dee546f8a3216b7.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "오버사이즈 집업 후디",
      "price": 99900,
      "choice": false,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 8,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/ad/e9/ade9a2fa40f186b2db875ceb35ca279ca99b300a.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "프린트 크롭트 탑",
      "price": 14900,
      "choice": true,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 9,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/44/a9/44a926e37e57244c829c3f7a049fc8aa629d276e.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "프린트 티셔츠",
      "price": 19900,
      "choice": false,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 10,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/fa/54/fa547c437bf08076aa46dd66f1a2e6a2cf4a4e3e.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "와이드 트윌 팬츠",
      "price": 19900,
      "choice": true,
      "new": false,
      "size": ["S", "M", "L"]
    },
    {
      "id": 11,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/d1/aa/d1aa5d5d4a3d0699689e7f6187514ad70dc958d6.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "플레어 로라이즈 진",
      "price": 29900,
      "choice": true,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 12,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/ec/6c/ec6c0b3d387ce9b51d68ee440b979291e929c773.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "프린트 크롭트 탑",
      "price": 14900,
      "choice": true,
      "new": true,
      "size": ["S", "M", "L"]
    },
    {
      "id": 13,
      "img": "https://lp2.hm.com/hmgoepprod?set=source[/37/43/37434b12a61d3e75cb8e7fcb2e0b6cd6ca028c5f.jpg],origin[dam],category[],type[LOOKBOOK],res[z],hmver[1]&call=url[file:/product/main]",
      "title": "플레어 로라이즈 진",
      "price": 29900,
      "choice": false,
      "new": false,
      "size": ["S", "M", "L"]
    }
  ]
}

데이터까지 넣었다면 이제 json server를 실행시켜야 한다.

그렇게 하기 위해서 새로운 터미널에 아래 코드를 작성한다.

npx json-server --watch db.json --port 5000

여기까지 되면 API만 호출해 주면 된다.

 

다시 ProductAll.js로 와서 아래처럼 작성해주자. (부트스트랩을 사용했다.)

ProductAll.js

import React, { useEffect, useState } from 'react'
import { Container,Row, Col } from 'react-bootstrap';
import ProductCard from '../component/ProductCard';

const ProductAll = () => {

  const [productList, setProductList] = useState([]);
  const getProducts=async()=>{
    let url = `http://localhost:5000/products`
    let response = await fetch(url)
    let data = await response.json()
    setProductList(data)
  }
  useEffect(()=>{
    getProducts()
  },[])
  return (
    <div>
      <Container>
        <Row>
        {productList.map(item=><Col lg={3}><ProductCard item={item} /></Col> )}
        </Row>
      </Container>
      
    </div>
  )
}

export default ProductAll

 

ProductCard.js

import React from "react";

const ProductCard = ({ item }) => {
  return (
    <div>
      <img width={300} src={item?.img} />
      <div>{item?.choice == true ? "Conscious choice" : " "}</div>
      <div>{item?.title}</div>
      <div>{item?.price}</div>
      <div>{item?.new == true ? "신제품" : ""}</div>
    </div>
  );
};

export default ProductCard;

 

잘 나온당

 

728x90