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