노력이 좋아서

<step72>'react_shoppingmall홈페이지 구현_step6'

zoaseo 2022. 7. 4. 15:28

1) 

삭제 하기 구현

product

index.js

import React from 'react';
import "./product.scss";
import axios from 'axios';
import { useParams } from 'react-router-dom';
import useAsync from '../customHook/useAsync';
import { useNavigate } from 'react-router-dom';

async function getProducts(id){
    const response = await axios.get(`http://localhost:3000/product/${id}`);
    return response.data;
}
const ProductPage = () => {
    // product/1
    const { id } = useParams();
    const [ state ] = useAsync(()=>getProducts(id),[id]);
    const { loading, data:product, error } = state;
    
    const navigate = useNavigate();
    const productDel = () => {
        axios.delete(`http://localhost:3000/product/${id}`)
        .then(result=>{
            console.log("삭제되었습니다.");
            navigate("/"); // 리다이렉션 추가
        })
        .catch(e=>{
            console.log(e);
        })
    }
    if(loading) return <div>로딩중.....</div>
    if(error) return <div>에러가 발생했습니다.</div>
    if(!product) return <div>로딩중입니다.</div>

    return (
        <div className='inner'>
            <div id="image-box">
                <img src={product.imageUrl} alt="" />
            </div>
            <div id="profile-box">
                <ul>
                    <li>
                        <div>
                            <img src="/images/icons/avatar.png" alt="" />
                            <span>{product.seller}</span>
                        </div>
                    </li>
                    <li>
                        {product.name}
                    </li>
                    <li>
                        {product.price}원
                    </li>
                    <li>등록일 </li>
                    <li>상세설명 </li>
                    <li>{product.description}</li>
                </ul>
            </div>
            <div>
                <span onClick={productDel}>삭제하기</span>
            </div>
        </div>
    );
};

export default ProductPage;

server.js

const express = require("express");
const cors = require("cors");
const app = express();
const port = 3000;
const models = require('./models');
// 업로드 이미지를 관리하는 스토리지 서버를 연결 -> multer를 사용하겠다.
const multer = require("multer");
// 이미지 파일 요청이 오면 어디에 저장할건지 지정
const upload = multer({ 
    storage: multer.diskStorage({
        destination: function(req, file, cb){
            // 어디에 저장할거냐? upload/
            cb(null, 'upload/')
        },
        filename: function(req, file, cb){
            // 어떤 이름으로 저장할거야?
            // file 객체의 오리지널 이름으로 저장하겠다.
            cb(null, file.originalname)
        }
    })
});

// json 형식의 데이터를 처리할 수 있게 설정
app.use(express.json());
// 브라우저 cors이슈를 막기위해 사용(모든 브라우저의 요청을 일정하게 받겠다)
app.use(cors());

// upload폴더에 있는 파일에 접근할 수 있도록 설정
app.use("/upload", express.static("upload"));

// 요청처리
// app.메소드(url, 함수)
// 이미지 파일을 post로 요청이 왔을 때 upload라는 폴더에 이미지를 저장하기
// 이미지가 하나일 때 single('key이름')
app.post('/image', upload.single('image'), (req,res)=>{
    const file = req.file;
    console.log(file);
    res.send({
        imageUrl: "http://localhost:3000/"+file.destination+file.filename
    })
})

// 요청처리
// app.메소드(url, 함수)

app.get('/products', async(req,res)=>{
    // 데이터 베이스 조회하기
    models.Product.findAll()
    .then(result=>{
        console.log("제품전체조회", result);
        res.send(result);
    })
    .catch(e=>{
        console.error(e);
        res.send("파일 조회에 문제가 있습니다.");
    })
});
// method get이고 url은 /product/2로 요청이 온 경우 
app.get('/product/:id', async (req,res)=>{
    const params = req.params;
    // const { id } = params;
    // 하나만 조회할 때는 findOne -> select문
    models.Product.findOne({
        // 조건절
        where: {
            id: params.id,
        }
    })
    .then(result=>{
        res.send(result);
    })
    .catch(e=>{
        console.error(e);
        res.send("상품 조회에 문제가 생겼습니다.");
    })
});
app.post("/products",(req, res)=>{
    // http body에 있는 데이터
    const body = req.body;
    // body 객체에 있는 값을 각각 변수에 할당
    const { name, price, seller, imageUrl, description } = body;
    if(!name || !price || !seller) {
        res.send("모든 필드를 입력해주세요");
    }
    else{
        // Product테이블에 레코드를 삽입
        models.Product.create({
            name,
            price,
            seller,
            imageUrl,
            description,
        }).then(result=>{
            console.log("상품 생성 결과 : ", result);
            res.send({
                result,
            })
        })
        .catch(e=>{
            console.error(e);
            res.send("상품 업로드에 문제가 생겼습니다.");
        })
    }
})
// delete 삭제하기
app.delete('/product/:id', async (req,res) => {
    const params = req.params;
    models.Product.destroy
    models.Product.destroy({ where: { id: params.id }})
    .then(res.send("상품이 삭제되었습니다."));
})
// 실행
app.listen(port, ()=>{
    console.log('쇼핑몰 서버가 동작중입니다.');
    // sequelize와 데이터베이스 연결 작업
    // 데이터베이스 동기화
    models.sequelize
    .sync()
    .then(()=>{
        console.log('DB연결 성공');
    })
    .catch(()=>{
        console.error(e);
        console.log('DB연결 에러');
        // 서버실행이 안되면 프로세스를 종료
        process.exit();
    })
});

2) 

config

constants.js

export const API_URL = "http://localhost:3000";

import { API_URL } from '../config/constants';

http://localhost:3000 => ${API_URL}

 

3)

서버에 설치하기

npm install -g nodemon

 

npx nodemon server.js

 

4)

배너 슬라이드 달기

 

참고하기 =>

https://ant.design/components/carousel/

 

Carousel - Ant Design

Timing of scrolling to the next card/picture.

ant.design

import React from 'react';
import './index.scss';
import axios from 'axios';
import MainProduct from './MainProduct';
import useAsync from '../customHook/useAsync';
import { API_URL } from '../config/constants';
import { Carousel } from 'antd';

async function getProducts(){
    const response = await axios.get(`${API_URL}/products`);
    return response.data;
}
const contentStyle = {
    height: '160px',
    color: '#fff',
    lineHeight: '160px',
    textAlign: 'center',
    position: 'absolute',
    bottom: '50px',
};
const MainPage = () => {
    const onChange = (currentSlide) => {
        console.log(currentSlide);
    }
    const [ state ] = useAsync(getProducts,[])
    const { loading, data:product, error } = state;
    if(loading) return <div>로딩중.....</div>
    if(error) return <div>에러가 발생했습니다.</div>
    if(!product) return <div>로딩중입니다.</div>

    return (
        <div>
            <div id="main">
                <div id="banner">
                <Carousel autoplay afterChange={onChange}>
                    <div>
                        <img src="images/banners/banner1.png" alt=""/>
                        <h3 style={contentStyle}>1</h3>
                    </div>
                    <div>
                        <img src="images/banners/banner1.png" alt=""/>
                        <h3 style={contentStyle}>2</h3>
                    </div>
                    <div>
                        <img src="images/banners/banner1.png" alt=""/>
                        <h3 style={contentStyle}>3</h3>
                    </div>
                </Carousel>          
                </div>
                <div id="product-list" className="inner">
                    <h2>그린조명 최신상품</h2>
                    <div id="product-items">
                        {product.map(product=> <MainProduct key={product.id} product={product}/>)}
                    </div>
                </div>
            </div>
        </div>
    );
};

export default MainPage;