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)
댓글 남기기