zoaseo
To Infinity And Beyond
zoaseo
전체 방문자
오늘
어제
  • 분류 전체보기 (763)
    • 개발이 좋아서 (381)
      • SAP가 좋아서 (3)
      • Java가 좋아서 (42)
      • Spring이 좋아서 (50)
      • JPA가 좋아서 (0)
      • QueryDSL이 좋아서 (26)
      • Docker가 좋아서 (7)
      • Redis가 좋아서 (7)
      • AWS가 좋아서 (5)
      • CI/CD가 좋아서 (6)
      • Troubleshooting이 좋아서 (4)
      • Kotlin이 좋아서 (7)
      • SQL이 좋아서 (6)
      • HTTP가 좋아서 (21)
      • JavaScript가 좋아서 (30)
      • TypeScript가 좋아서 (6)
      • Vue가 좋아서 (21)
      • Flutter가 좋아서 (61)
      • React가 좋아서 (20)
      • Redux(React)가 좋아서 (2)
      • Angular가 좋아서 (22)
      • HTML이 좋아서 (9)
      • CSS가 좋아서 (15)
      • PHP가 좋아서 (9)
      • Illustrator가 좋아서 (2)
    • 노력이 좋아서 (169)
    • 결과물이 좋아서 (14)
    • 코딩연습이 좋아서 (168)
      • 이론이 좋아서 (62)
      • SQL이 좋아서 (90)
    • 유용한 사이트가 좋아서 (28)
    • Github (2)

인기 글

티스토리

hELLO · Designed By 정상우.
zoaseo

To Infinity And Beyond

<step72>'react_shoppingmall홈페이지 구현_step4'
노력이 좋아서

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

2022. 7. 4. 09:56

1) 

main

index.js

import React from 'react';
import './index.scss';
import axios from 'axios';
import MainProduct from './MainProduct';
import useAsync from '../customHook/useAsync';

async function getProducts(){
    const response = await axios.get("http://localhost:3000/products");
    return response.data;
}
const MainPage = () => {
    const [ state, refetch ] = useAsync(getProducts,[])
    const { loading, data:products, error } = state;
    if(loading) return <div>로딩중.....</div>
    if(error) return <div>에러가 발생했습니다.</div>
    if(!products) return <div>로딩중입니다.</div>

    return (
        <div>
            <div id="main">
                <div id="banner">
                    <img src="images/banners/banner1.png" alt=""/>
                </div>
                <div id="product-list" className="inner">
                    <h2>그린조명 최신상품</h2>
                    <div id="product-items">
                        {products.map(product=> <MainProduct key={product.id} product={product}/>)}
                    </div>
                </div>
            </div>
        </div>
    );
};

export default MainPage;

customHook

useAsync.js

import { useReducer, useEffect, useCallback } from "react";
const initialState = {
    loading: false,
    data: null,
    error: null,
}
// 로딩중 ? 데이터 받기 성공 : 데이터 받기 실패
// LOADING, SUCCESS, ERROR
function reducer(state, action){
    switch(action.type){
        case "LOADING":
            return {
                loading: true,
                data: null,
                error: null,
            };
        case "SUCCESS":
            return {
                loading: false,
                data: action.data,
                error: null,
            }
        case "ERROR":
            return {
                loading: false,
                data: null,
                error: action.error,
            }
        default:
            return state;
    }
}
function useAsync(callback, deps=[]){
    const [ state, dispatch ] = useReducer(reducer, initialState);
    const fetchDate = async () => {
        dispatch({type: "LOADING"});
        try {
            const data = await callback();
            dispatch({
                type: "SUCCESS",
                data: data,
            })
        }
        catch(e) {
            dispatch({
                type: "ERROR",
                error: e,
            })
        }
    }
    useEffect(()=>{
        fetchDate();
    }, deps);
    return [ state, fetchDate ];
}
export default useAsync;

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

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;
    
    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>등록일 2022년 6월 2일</li>
                    <li>상세설명 </li>
                </ul>
            </div>
        </div>
    );
};

export default ProductPage;

2) 

server.js

const express = require("express");
const cors = require("cors");
const app = express();
const port = 3000;
const models = require('./models');

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

// 요청처리
// 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('/green',async (req,res)=>{
    console.log(req);
    res.send('그린 게시판에 게시글이 등록되었습니다.');
});
app.post("/products",(req, res)=>{
    // http body에 있는 데이터
    const body = req.body;
    // body 객체에 있는 값을 각각 변수에 할당
    const { name, price, seller, imageUrl } = body;
    if(!name || !price || !seller) {
        res.send("모든 필드를 입력해주세요");
    }
    else{
        // Product테이블에 레코드를 삽입
        models.Product.create({
            name,
            price,
            seller,
            imageUrl,
        }).then(result=>{
            console.log("상품 생성 결과 : ", result);
            res.send({
                result,
            })
        })
        .catch(e=>{
            console.error(e);
            res.send("상품 업로드에 문제가 생겼습니다.");
        })
    }
})
// 실행
app.listen(port, ()=>{
    console.log('쇼핑몰 서버가 동작중입니다.');
    // sequelize와 데이터베이스 연결 작업
    // 데이터베이스 동기화
    models.sequelize
    .sync()
    .then(()=>{
        console.log('DB연결 성공');
    })
    .catch(()=>{
        console.error(e);
        console.log('DB연결 에러');
        // 서버실행이 안되면 프로세스를 종료
        process.exit();
    })
});

post 전송
데이터베이스 확인

3) 

multer 설치하기

npm install multer

 

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 } = body;
    if(!name || !price || !seller) {
        res.send("모든 필드를 입력해주세요");
    }
    else{
        // Product테이블에 레코드를 삽입
        models.Product.create({
            name,
            price,
            seller,
            imageUrl,
        }).then(result=>{
            console.log("상품 생성 결과 : ", result);
            res.send({
                result,
            })
        })
        .catch(e=>{
            console.error(e);
            res.send("상품 업로드에 문제가 생겼습니다.");
        })
    }
})
// 실행
app.listen(port, ()=>{
    console.log('쇼핑몰 서버가 동작중입니다.');
    // sequelize와 데이터베이스 연결 작업
    // 데이터베이스 동기화
    models.sequelize
    .sync()
    .then(()=>{
        console.log('DB연결 성공');
    })
    .catch(()=>{
        console.error(e);
        console.log('DB연결 에러');
        // 서버실행이 안되면 프로세스를 종료
        process.exit();
    })
});

upload

index.js

import React, { useState } from 'react';
import './upload.scss';
import 'antd/dist/antd.css';
import { Form, Divider, Input, InputNumber, Button, Upload } from 'antd';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

const UploadPage = () => {
    const navigate = useNavigate(); // 리다이렉션
    // 이미지 경로 상태관리 추가
    const [imageUrl, setImageUrl ] = useState(null);
    // 이미지 처리함수
    const onChangeImage = (info) => {
        // 파일이 업로드 중일 때
        console.log(info.file)
        if(info.file.status === "uploading"){
            return;
        }
        // 파일이 업로드 완료되었을 때
        if(info.file.status === "done"){
            const response = info.file.response;
            const imageUrl = response.imageUrl;
            // 받은 이미지 경로를 imageUrl에 넣어줌
            setImageUrl(imageUrl);
        }
    }
    const onSubmit = (values) => {
        // 서버로 데이터 전송하기
        axios.post("http://localhost:3000/products",{
            name: values.name,
            seller: values.seller,
            price: values.price,
            imageUrl: imageUrl,
        }).then((result)=>{
            console.log(result);
            navigate("/"); // 리다이렉션 추가
        })
        .catch(e=>{
            console.log(e);
        })
    }
    return (
        <div id="upload-container" className='inner'>
            <Form name='productUpload' onFinish={onSubmit}>
                <Form.Item name="imgUpload"
                    label={<div className='upload-label'>상품사진</div>}>
                <Upload name="image" action="http://localhost:3000/image"
                listType="picture" showUploadList={false} onChange={onChangeImage}>
                    {/* 업로드 이미지가 있으면 이미지를 나타내고 
                    업로드 이미지가 없으면 회색배경에 업로드 아이콘이 나타나도록... */}
                    { imageUrl ? <img src={imageUrl} 
                    alt="" width="200px" height="200px"/>
                        : <div id="upload-img-placeholder">
                            <img src="images/icons/camera.png" alt="" />
                            <span>이미지를 업로드해주세요.</span>
                        </div>                   
                    }   
                </Upload>
                </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;

 

상품 등록하기 클릭
상품 추가

 

'노력이 좋아서' 카테고리의 다른 글

<step72>'react_shoppingmall홈페이지 구현_step6'  (0) 2022.07.04
<step72>'react_shoppingmall홈페이지 구현_step5'  (0) 2022.07.04
<step71>'react_shoppingmall홈페이지 구현_step3'  (0) 2022.07.01
<step70>'react_shoppingmall홈페이지 구현_step2'  (0) 2022.06.30
<step70>'js_동기비동기_promise,async,await'  (0) 2022.06.30

    티스토리툴바