SQL
CREATE TABLE `t_product` ( `product_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `product_name` varchar(200) NOT NULL, `product_price` int(11) NOT NULL DEFAULT 0, `delivery_price` int(11) NOT NULL DEFAULT 0, `add_delivery_price` int(11) NOT NULL DEFAULT 0, `tags` varchar(100) DEFAULT NULL, `outbound_days` int(2) NOT NULL DEFAULT 5, `seller_id` int(10) unsigned NOT NULL, `category_id` int(10) unsigned DEFAULT NULL, `active_yn` char(1) NOT NULL DEFAULT 'Y', `created_date` datetime NOT NULL DEFAULT current_timestamp(), PRIMARY KEY (`product_id`), KEY `t_product_FK` (`seller_id`), KEY `t_product_FK_1` (`category_id`), CONSTRAINT `t_product_FK` FOREIGN KEY (`seller_id`) REFERENCES `t_seller` (`seller_id`), CONSTRAINT `t_product_FK_1` FOREIGN KEY (`category_id`) REFERENCES `t_category` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `t_seller` ( `seller_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `email` varchar(50) NOT NULL, `phone` varchar(20) NOT NULL, PRIMARY KEY (`seller_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `t_category` ( `category_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `category1` varchar(100) NOT NULL, `category2` varchar(100) NOT NULL, `category3` varchar(100) DEFAULT NULL, PRIMARY KEY (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `t_image` ( `image_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `product_id` int(10) unsigned NOT NULL, `type` int(1) NOT NULL DEFAULT 1 COMMENT '1-썸네일,2-제품이미지,3-제품설명이미지', `path` varchar(100) NOT NULL, PRIMARY KEY (`image_id`), KEY `t_image_FK` (`product_id`), CONSTRAINT `t_image_FK` FOREIGN KEY (`product_id`) REFERENCES `t_product` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `t_user` ( `email` varchar(50) NOT NULL, `type` int(1) NOT NULL DEFAULT 1 COMMENT '1-buyer,2-seller', PRIMARY KEY (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Node 서버
sql.js
module.exports = { productDetail: { query: ` SELECT tp.*, tc.category1 , tc.category2 , tc.category3 , ti.path, ti.type FROM t_product tp, t_category tc , t_image ti WHERE tp.category_id =tc.category_id AND tp.product_id =ti.product_id AND ti.type=3 AND tp.product_id=? ` }, productMainImages: { query: `SELECT * from t_image ti WHERE ti.type=2 AND product_id =? ` }, productInsert: { query: `INSERT INTO t_product set ?` }, productImageInsert: { query: `insert into t_image set ?` }, imageList: { query: `select * from t_image where product_id=?` }, imageDelete: { query: `delete from t_image where image_id=?` }, }
app.js
const express = require('express'); const app = express(); const session = require('express-session'); const fs = require("fs"); const mysql = require('mysql'); const db = { connectionLimit: 10, host: '192.168.174.1', port: '3308', user: 'dev', password: '1111', database: 'dev' } const dbPool = mysql.createPool(db); app.use(session({ secret: 'secret code', resave: false, saveUninitialized: false, cookie: { secure: false, maxAge: 1000 * 60 * 60 //쿠기 유효시간 1시간 } })); /* Expressjs에서 JSON Request Body 파싱하기 expressjs에서 웹 서비스를 제작 했을때, json으로 이루어진 Request Body를 받았을 경우, 요청값을 제대로 받아오지 못하는 문제가 발생한다. expressjs에서는 이러한 문제를 해결하는 방법으로 크게 2가지 방법을 사용할 수 있다. body-parser 모듈 사용(4.16 이전 버전). express.json() 사용 */ //express 4.16 이전 버전 //app.use(bodyParser); //express 4.16 이후 버전 app.use(express.json({ limit: '50mb' })); app.use(session({ secret: 'secret code', //세션에 대한 키값 resave: false, //request 요청이 왔을때 session 수정사항을 다시 저장할 것인지 saveUninitialized: false, // session 저장 내역이 없더라도 다시 저장할 것인지 cookie: { secure: false, maxAge: 1000 * 60 * 60 //쿠키 유효시간 1시간 } })); const server = app.listen(3000, () => { console.log('Server started. port 3000. '); }); app.post('/api/:alias', async (request, res) => { if (!request.session.email) { // return res.status(401).send({ error: "Youn need to login." }); } try { /* 다음과 같은 형식으로 데이터를 넘겨준다 {"param":["1"]} */ res.send(await req.db(request.params.alias, request.body.param)); } catch (err) { console.log("err : " + err); res.status(500).send({ error: err }); } }); app.post('/upload/:productId/:type/:fileName', async (request, res) => { let { productId, type, fileName } = request.params; const dir = `${__dirname}/uploads/${productId}`; const file = `${dir}/${fileName}`; if (!request.body.data) return fs.unlink(file, async (err) => res.send({ err })); const data = request.body.data.slice(request.body.data.indexOf(';base64,') + 8); if (!fs.existsSync(dir)) fs.mkdirSync(dir); fs.writeFile(file, data, 'base64', async (error) => { await req.db('productImageInsert', [{ product_id: productId, type: type, path: fileName }]); if (error) { res.send({ error }); } else { res.send("ok"); } }); }); app.get('/download/:productId/:fileName', (request, res) => { const { productId, type, fileName } = request.params; const filepath = `${__dirname}/uploads/${productId}/${fileName}`; res.header('Content-Type', `image/${fileName.substring(fileName.lastIndexOf("."))}`); if (!fs.existsSync(filepath)) res.send(404, { error: 'Can not found file.' }); else fs.createReadStream(filepath).pipe(res); }); const req = { async db(alias, param = [], where = '') { return new Promise((resolve, reject) => dbPool.query(sql[alias].query + where, param, (error, rows) => { if (error) { if (error.code != 'ER_DUP_ENTRY') console.log(error); resolve({ error }); } else resolve(rows); })); } };
Vue
vue.config.js
업로드 다운로드 프록시 설정
const target = "http://127.0.0.1:3000"; module.exports = { devServer: { port: 8080, proxy: { '^/api': { target, changeOrigin: true }, '^/upload': { target, changeOrigin: true, }, '^/download': { target, changeOrigin: true, } } } }
mixins.js
mixins 파일에 $base64 추가
base64로 인코딩하는가?
데이터베이스에 전송할 때 이미지인 경우 base64 타입을 요구하는 경우가 있을 수 있습니다. 보통 이미지는 base64로 인코딩 후 blob DB에 업로드합니다. 또는 인증과 관련된 정보 역시 base64로 인코딩 후 전송하기도 합니다.
$base64(file) { return new Promise(resolve => { var a = new FileReader(); a.onload = e => resolve(e.target.result); a.readAsDataURL(file); }); },
ImageInsert.vue
<template> <div> <main class="mt-3"> <div class="container"> <h2 class="text-center">제품 사진 등록</h2> <div class="mb-3 row"> <label class="col-md-3 col-form-label">제품ID</label> <div class="col-md-9"> {{productId}} </div> </div> <div class="mb-3 row"> <label class="col-md-3 col-form-label">제품명</label> <div class="col-md-9"> {{productDetail.product_name}} </div> </div> <div class="mb-3 row"> <label class="col-md-3 col-form-label">썸네일이미지</label> <div class="col-md-9"> <div class="row"> <div class="col-lg-3 col-md-4 col-sm-2" :key="i" v-for="(m,i) in productImage.filter(c=>c.type==1)"> <div class="position-relative"> <img :src="`/download/${productId}/${m.path}`" class="img-fluid" /> <div class="position-absolute top-0 end-0" style="cursor:pointer;" @click="deleteImage(m.image_id)">X</div> </div> </div> </div> <input type="file" class="form-control" accept="image/png,image/jpeg" @change="uploadFile($event.target.files, 1)"> <div class="alert alert-secondary" role="alert"> <ul> <li>이미지 사이즈 : 350*350</li> <li>파일 사이즈 : 1M 이하</li> <li>파일 확장자 : png, jpg만 가능</li> </ul> </div> </div> </div> <div class="mb-3 row"> <label class="col-md-3 col-form-label">제품이미지</label> <div class="col-md-9"> <div class="row"> <div class="col-lg-3 col-md-4 col-sm-2" :key="i" v-for="(m,i) in productImage.filter(c=>c.type==2)"> <div class="position-relative"> <img :src="`/download/${productId}/${m.path}`" class="img-fluid" /> <div class="position-absolute top-0 end-0" style="cursor:pointer;" @click="deleteImage(m.image_id)">X</div> </div> </div> </div> <input type="file" class="form-control" accept="image/png,image/jpeg" @change="uploadFile($event.target.files, 2)"> <div class="alert alert-secondary" role="alert"> <ul> <li>최대 5개 가능</li> <li>이미지 사이즈 : 700*700</li> <li>파일 사이즈 : 1M 이하</li> <li>파일확장자 : png,jpg 만 가능</li> </ul> </div> </div> </div> <div class="mb-3 row"> <label class="col-md-3 col-form-label">제품설명이미지</label> <div class="col-md-9"> <div class="row"> <div class="col-lg-6 col-md-8"> <input type="file" class="form-control" accept="image/png,image/jpeg" @change="uploadFile($event.target.files, 3)"> <div class="alert alert-secondary" role="alert"> <ul> <li>파일 사이즈 : 5M 이하</li> <li>파일 확장자 : png, jpg만 가능</li> </ul> </div> </div> <div class="col-lg-6 col-md-4" :key="i" v-for="(m,i) in productImage.filter(c=>c.type==3)"> <div class="position-relative"> <img :src="`/download/${productId}/${m.path}`" class="img-fluid" /> <div class="position-absolute top-0 end-0" style="cursor:pointer;color:white;" @click="deleteImage(m.image_id)">X</div> </div> </div> </div> </div> </div> <div class="mb-3 row m-auto"> <div class="mb-3 row m-auto"> <button type="button" class="btn btn-lg btn-dark" @click="goToList">확인</button> </div> </div> </div> </main> </div> </template> <script> export default { data(){ return { productId:0, proudctName:"", productDetail: {}, productImage: [], } }, computed:{ user(){ return this.$store.state.user; } }, created() { this.productId = this.$route.query.product_id; this.getProductDetail(); this.getProductImage() }, mounted() { if(this.user.email==undefined){ //로그인 되어 있지 않으면 첫화면으로 alert("로그인을 해야 이용할 수 있습니다."); this.$router.push({path:'/'}); } }, methods: { goToList(){ this.$router.push({path:'/salesList'}); }, async getProductDetail() { console.log("상세페이지 - getProductDetail : " + this.productId); let productDetail = await this.$api("/api/productDetail", "POST", { param: [this.productId] }); console.log(productDetail.category1); if (productDetail.length > 0) { this.productDetail = productDetail[0]; }else{ console.log("0"); } console.log(productDetail.category1); }, async getProductImage() { this.productImage = await this.$api("/api/imageList", "POST" ,{param:[this.productId]}); console.log('this.productImage',this.productImage) }, deleteImage(id) { this.$swal.fire({ title: '정말 삭제 하시겠습니까?', showCancelButton: true, confirmButtonText: `삭제`, cancelButtonText: `취소` }).then(async (result) => { if (result.isConfirmed) { await this.$api("/api/imageDelete", "POST" ,{param:[id]}); this.getProductImage(); this.$swal.fire('삭제되었습니다!', '', 'success'); } }); }, async uploadFile(files, type) { let name = ""; let data = null; if (files) { name = files[0].name; data = await this.$base64(files[0]); } const { error } = await this.$api(`/upload/${this.productId}/${type}/${name}`, "POST", { data }); if (error) { return this.$swal("이미지 업로드 실패했습니다. 다시 시도하세요."); } this.$swal("이미지가 업로드 되었습니다."); setTimeout(() => { this.getProductImage(); }, 1000); } }, } </script> <style scoped> </style>
https://github.com/braverokmc79/my-vue-project-1.git
#npm init
node 디렉토리에서 서버 구동
#node app.js
client 디렉토리에서 vue 구동
#npm run serve
댓글 ( 4)
댓글 남기기