[React] Lamp 쇼핑몰 구현하기 1

2022. 6. 29. 12:50·Stack/React

LAMP-SHOPPING-CLIENT

 

* 설치해야하는 라이브러리

1) sass

yarn add node-sass

2) antd

npm install antd

3) axios

npm install axios / yarn add axios

4) react-router-dom

npm install react-router-dom

 

* 구현 순서

// 메인페이지

1. main 폴더 생성 후 main/index.js에 마크업하기 (첫 화면 먼저 만들기)

component 이름은 MainPage

2. 기본 셋팅(공통으로 적용되는 애들)은 app.css에서 스타일 주기 (header)

main/index.scss 파일로 index.js(마크업한 파일)에 스타일 주기 (import './index.scss';)

main에 적용되는 css만 index.scss에 넣을 것임

3. header, footer 잘라서 include 폴더 생성 후 넣고 app.js에서 component 이름으로 조립

// 상품 상세보기 페이지

4. product 폴더 생성 후 index.js(상품 상세보기) 파일 생성, component 이름은 ProductPage

5. product/index.js를 위한 product/product.scss 스타일 생성 후 product/index.js에 import './index.scss'; 해주기

6. index.js 가서 import { BrowserRouter  } from 'react-router-dom'; 선언 후 <App />을  <BrowserRouter>로 감싸기

Header랑 Footer은 모든 페이지 다 보이게 할 것이고 링크에 따라 메인페이지 상세페이지 왔다갔다 할 것이기에

App.js에서 import { Routes, Route } from 'react-router-dom'; 선언해준다 ~

 

App.js // 이렇게 되어있던 애를

 

이렇게 바꿔줌으로써 주소창에 /products를 치면 상품 페이지로 넘어가게 만들어준다 !

 

내가 이해한 내용

더보기

element로 받은 애를 저 path에 넣어주고

Navlink가 이를 또 불러주는 느낌인건가?

 

...

여기까지 상황

 

// 상품 등록 페이지

7. upload 폴더 생성 후 index.js 생성, rsc 해서 component 이름은 Uploadpage 로 함

import { Form, Divider, Input, InputNumber, Button } from 'antd';

import 'antd/dist/antd.css';   // antd 디자인 이용

선언해준 후 마크업하기

8. App.js에서 <Route path="/upload" element={<Uploadpage/>}/> 추가해준다 ~

위에 Uploadpage import도 되게 자동완성으로 추가해준다 ~

9. upload.scss 파일 생성 후 스타일 주기

10. include/Header.js에 import { NavLink } from 'react-router-dom'; 선언

이케 걸어주면 이동가능 ~

 

11. 깃허브 로그인 후 create repository (repository 이름: lamp-shopping-client)

12. VSCode 터미널

git init
git add .
git commit -m "램프쇼핑몰 보이는 화면 구현"
 git branch -M main
git remote add origin https://github.com/7ingout/lamp-shopping-client.git
git push -u origin main

입력 ~

# 앞으로 git에 올릴 땐 git add .  / git commit -m "" / git push 만 해주면 됨 !

 

완료 ~

 

...

현재까지 진행상황

 


 

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(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

// 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();

 

App.js

import './App.css';
import Footer from './include/Footer';
import Header from './include/Header';
import MainPage from './main';
import ProductPage from './product';
import { Routes, Route } from 'react-router-dom';
import Uploadpage from './upload';

function App() {
  return (
    <div className="App">
      {/* 파일이름으로 부르는 것이 아니라 component 이름을 부르기 */}
      <Header/>
      <Routes>
        <Route path="/" element={<MainPage/>}/>
        <Route path="/products" element={<ProductPage/>}/>
        <Route path="/upload" element={<Uploadpage/>}/>
      </Routes>
      <Footer/>
    </div>
  );
}

export default App;

 

App.css

* { margin: 0; padding: 0; box-sizing: border-box; }
li { list-style: none ;}
a { text-decoration: none; color: inherit; }
body {
    font-family: "나눔고딕", sans-serif;
    font-size: 16px;
    line-height: 1.6;
    color: #222;
}
input, section, textarea {
    outline: none;
}
table { border-collapse: collapse;}
#header {
  height: 64px;
}
.inner {
  width: 1200px;
  margin: 0 auto;
}
#header .inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
#header ul {
  display: flex;
}
#header ul li {
  padding: 0 10px;
}
#footer {
  padding-top: 50px;
}
#footer-info {
  border-top: 3px solid #333;
  border-bottom: 1px solid #333;
  padding-bottom: 20px;
}
#footer-info > div {
  display: flex;
  justify-content: space-between;
}
#footer-info > div div {
  width: 32%;
  line-height: 2;
}
#footer-info > div div h3 {
  border-bottom: 1px solid #ccc;
  padding-top: 20px;
  padding-bottom: 10px;
  margin-bottom: 20px;
}
#footer-info > div div li {
  border-bottom: 1px dotted #ccc;
}
#copyright {
  background-color: #eee;
  padding: 30px;
  border-top: 1px solid #ccc;
}
#footer-copy ul {
  display: flex;
  padding: 16px 0;
}
#footer-copy ul li {
  padding: 0 30px;
  line-height: 1;
}
#footer-copy ul li:not(:last-child) {
  border-right: 1px solid #ccc;
}

 

main/index.js

import React, {useState, useEffect} from 'react';
import './index.scss';
import axios from 'axios';

const MainPage = (props) => {
    const [ products, setProducts ] = useState([]);
    useEffect(()=>{
        axios.get("http://localhost:3000/products")
        .then((result)=>{
            const products = result.data.products;
            setProducts(products);
        }).catch((e)=>{
            console.log(e);
        })
    }, [])
 
    return (
        <div>
            <div id="main">
                <div id="banner">
                     {/* 바로 images 하면 public에 있는 images 들어감 */}
                    <img src="images/banners/banner1.png" alt="" />
                </div>
                <div id="product-list" className='inner'>
                    <h2>그린조명 최신상품</h2>
                    <div id="product-items">
                        {/* 나중에 map 이용해서 밑에꺼 8개 뿌려줄거임 */}
                        {products.map(product => (
                            <div className="product-card" key={product.id}>
                                <div className='product-img'>
                                    <img src={product.imgsrc} alt="" />
                                </div>
                                <div className='product-contents'>
                                    <span className='product-name'>제품명 {product.name}</span>
                                    <span className='product-price'>가격 {product.price}</span>
                                    <div className='product-seller'>
                                    <img src="images/icons/avatar.png" alt=""/>{product.seller}
                                    </div>
                                </div>
                            </div>
                        ))}   
                    </div>
                </div>
            </div>
        </div>
    );
};
export default MainPage;

 

main/index.scss

#banner {
    img {
        width: 100%;
    }
}
#product-list {
    h2 {
        padding: 30px 0;
    }
    #product-items {
        display: flex;
        flex-wrap: wrap;
        .product-card {
            width: 24%;
            border: 1px solid #ccc;
            border-radius: 16px;
            margin-bottom: 16px;
            overflow: hidden;
            &:not(:nth-child(4n+0)) {
                margin-right: 1.3333%;
            }
            .product-img {
                img {
                    width: 100%;
                }
            }
            .product-contents {
                padding: 24px;
                span {
                    display: block;
                }
                .product-seller {
                    padding-top: 16px;
                    display: flex;
                    align-items: center;
                    img {
                        width: 50px;
                    }
                }
            }
        }
    }
}

 

include/Header.js

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

const Header = () => {
    return (
        <div id="header">
            <div className="inner">
                <h1><NavLink to ='/'>램프쇼핑</NavLink></h1>
                <ul>
                    {/* <li>상품등록하기</li>
                    <li>상품보기</li> */}
                    <li><NavLink to ='/upload'>상품등록하기</NavLink></li>
                    <li><NavLink to ='/products'>상품보기</NavLink></li>
                </ul>
            </div>
        </div>   
    );
};

export default Header;

 

include/Footer.js

import React from 'react';

const Footer = () => {
    return (
        <div id="footer">
            <div id="footer-info">
                <div className='inner'>
                    <div>
                        <h3>무통장 입금계좌</h3>
                        <p>BANK ACCOUNT</p>
                        <p>301-1234-5678-01</p>
                        <p>예금주 - 김그린(그린조명)</p>
                    </div>
                    <div>
                        <h3>고객센터</h3>
                        <p>영업시간 이외에는 문의 게시판을 이용해주시면 담당자 확인 후 빠른 답변 도와드리겠습니다.</p>
                        <p id="tel">02-1263-1245</p>
                    </div>
                    <div>
                        <h3>공지사항</h3>
                        <ul>
                            <li>조명가이드 <span>2022-06-20</span></li>
                            <li>신상품 입고 안내 <span>2022-06-10</span></li>
                            <li>Mall 오픈을 축하드립니다. <span>2022-02-20</span></li>
                        </ul>
                    </div>
                </div>
            </div>
            <div id="footer-copy">
                <div className='inner'>
                    <ul>
                        <li>홈</li>
                        <li>그린매장안내</li>
                        <li>이용약관</li>
                        <li>개인정보처리방침</li>
                    </ul>
                </div>
                <div id="copyright">
                    <div className='inner'>
                        상호 : 그린조명 주소 : 울산광역시 남구 삼산중로 100번길
                        대표전화 : 국번없이 052-1234-4223 대표이사 : 김그린
                        개인정보관리자 : 이블루 사업자 등록번호 : 102-12-12345
                        copyright(c) Green Lamp,.LTD all rights reserved.
                    </div>
                </div>
            </div>
        </div>
    );
};

export default Footer;

 

product/index.js

import React from 'react';
import "./product.scss"

const ProductPage = (props) => {
    return (
        <div className='inner'>
            <div id="image-box">
                <img src="/images/products/product1.jpg" alt =""/>
            </div>
            <div id="profile-box">
                <ul>
                    <li>
                        <div>
                            <img src="/images/icons/avatar.png" alt=""/>
                            <span>그린</span>
                        </div>
                    </li>
                    <li>
                        제품명 새로운 조명
                    </li>
                    <li>
                        가격 50000원
                    </li>
                    <li>등록일 2022년 6월 2일</li>
                    <li>상세설명</li>
                </ul>
            </div>
        </div>
    );
};

export default ProductPage;

 

product/product.scss

#image-box {
    padding-top: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    img {
        width: 50%;
        border-radius: 50%;
    }
}
#profile-box {
    padding: 16px;
    margin-top: 10px;
    li {
        border-bottom: 1px solid #ccc;
        line-height: 40px;
        div {
            display: flex;
            align-items: center;
            img {
                width: 70px;
            }
        }
    }
}

 

upload/index.js

import React from 'react';
import './upload.scss';
import 'antd/dist/antd.css';
import { Form, Divider, Input, InputNumber, Button } from 'antd';

const Uploadpage = (props) => {
    return (
        <div id="upload-container" className='inner'>
            <Form name="productUpload">
                <Form.Item name="imgUpload"
                    label={<div className='upload-label'>상품사진</div>}>
                    <div id="upload-img-placeholder">
                        <img src="images/icons/camera.png" alt="" />
                        <span>이미지를 업로드 해주세요.</span>
                    </div>
                </Form.Item>
                <Divider/>
                <Form.Item name="seller" 
                    label={<div className='upload-label'>판매자명</div>}>
                    <Input className="nameUpload" size='large'
                    placeholder='판매자 이름을 입력하세요'/>
                </Form.Item>
                <Divider/>
                <Form.Item name="name"
                label={<div className='upload-label'>상품이름</div>}>
                    <Input
                        className='upload-name'
                        size='large'
                        placeholder='상품 이름을 입력해주세요'/>
                </Form.Item>
                <Divider/>
                <Form.Item name="price"
                label={<div className='upload-label'>상품가격</div>}>
                    <InputNumber defaultValue={0} size="large"/>
                </Form.Item>
                <Divider/>
                <Form.Item name="description"
                label={<div className='upload-label'>상품소개</div>}>
                <Input.TextArea
                    size='large'
                    id = "product-description"
                    maxLength={300}
                    placeholder="상품 소개를 적어주세요"
                />
                </Form.Item>
                <Form.Item>
                    <Button id="submit-button" size="large" htmlType='submit'>
                        상품등록하기
                    </Button>
                </Form.Item>
            </Form>
        </div>
    );
};

export default Uploadpage;

 

upload/upload.scss

#upload-img-placeholder {
    width: 200px;
    height: 200px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border: 1px solid rgb(230, 230, 230);
    background: rgb(250, 250, 253);
    margin-left: 16px;
}
#upload-img-placeholder > img {
    width: 50px;
    height: 50px;
}
#upload-img-placeholder > span {
    font-size: 14px;
    color: rgb(195, 194, 204);
}
.upload-label {
    width: 100px;
    font-size: 18px;
    text-align: left;
}

'Stack > React' 카테고리의 다른 글

[React] Lamp 쇼핑몰 구현하기 3 (서버에 연결하기)  (0) 2022.07.01
[React] Lamp 쇼핑몰 구현하기 2 (Node.js 서버 만들기)  (0) 2022.06.30
[React] API 연동하기 2  (0) 2022.06.29
[React] useState / useReducer / Context API 복습  (0) 2022.06.29
[React] API 연동하기  (0) 2022.06.28
'Stack/React' 카테고리의 다른 글
  • [React] Lamp 쇼핑몰 구현하기 3 (서버에 연결하기)
  • [React] Lamp 쇼핑몰 구현하기 2 (Node.js 서버 만들기)
  • [React] API 연동하기 2
  • [React] useState / useReducer / Context API 복습
7ingout
7ingout
  • 7ingout
    Hello, 7ingout world!
    7ingout
  • 전체
    오늘
    어제
    • 분류 전체보기 (205)
      • Project (5)
      • Stack (173)
        • React (40)
        • JavaScript (50)
        • TypeScript (14)
        • HTML (11)
        • CSS (31)
        • Spring (9)
        • PHP (15)
        • SQL (3)
        • Python (0)
      • ETC (9)
      • Design (13)
        • Illustrator (6)
        • Photoshop (7)
      • Articloid (4)
        • 7ingout (4)
  • 공지사항

    • ☻
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
7ingout
[React] Lamp 쇼핑몰 구현하기 1
상단으로

티스토리툴바