📁routes/ex_sell.js
설계도면에서 필요한 정보 확인하기
item ➡️ sell ➡️ community ➡️ register➡️ member
테이블 순서로 판매자의 정보를 파악한다
sell 테이블에 참조된 커뮤니티 번호가 로그인 한 사용자의 것인지 register 테이블에서 확인한다
조회 조건 ➡️ 로그인 token
+ 조회하고자 하는 communityno
register 테이블에서 로그인한 사용자의 커뮤니티가 맞다면
sell 테이블에서 로그인한 사용자
의
➡️ 커뮤니티 번호와 일치하는 데이터(itemno)
를 모두(find)
가져온다
💡 chk값(판매중 chk=1, 판매중지 chk=0)에 관계없이 모든값을 조회
sell 테이블 데이터 조회시
itemno(FK)는 필요 조건이 아니라 가져올 결과값이다
community 테이블과 item테이블은 관계가 없지만
sell테이블로 인해 관계가 생긴다
배달어플 로직과 비슷하게 구현
📁exp_20220711/prj2.vuerd.json
(새 테이블 생성시 Database > Oracle로 변경 후 작성)
사업자등록번호를 기본키로 사용
➡️ 상호명을 기본키로 쓸 수 없다! 동일한 이름의 식당이 있을 수 있기 때문
restaurant 테이블과 food 테이블은 1:N 관계
= 하나의 식당(restaurant)은 여러개의 음식(food)를 가질 수 있다
food 테이블과 customer 테이블을 연결해주는 동적(action) 테이블이다.
고객아이디 => 고객의 연락처를 기본키로 사용한다
💡 fd_food, fd_order테이블의 기본키만 자동번호(SEQ) 부여한다
password는 앱에서는 필요 없지만 인증 방법이 달라 pc에서는 필요하다
서버생성시 고객인증 기능까지 추가하기!
📁exp_20220711/models/food
💡 model생성시 외래키(FK) 값도 설정해줘야한다!
📁exp_20220711/models/food/fd_restaurantmodel.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var restaurantSchema = new Schema({
_id : String, // 사업자번호(PK)
name : {type:String, default:''}, // 상호
phone : {type:String, default:''}, // 연락처
address : {type:String, default:''}, // 주소
regdate : {type:Date, default:Date.now}, // 등록일
categorycode : {type:String, default:''}, // 카테고리코드(FK)
password : {type:String, default:''}, // 암호(FK)
});
module.exports = mongoose.model('fd_restaurant', restaurantSchema);
📁exp_20220711/routes/food/
model 등록시 파일경로에 맞게 상위경로로 한번 더 이동한다
var Category = require("../../models/food/fd_categorymodel");
var Restaurant = require("../../models/food/fd_restaurantmodel");
📁exp_20220711/app.js
var fd_restaurantRouter = require('./routes/food/fd_restaurant');
var fd_categoryRouter = require('./routes/food/fd_category');
app.use('/api/fd_restaurant', fd_restaurantRouter);
app.use('/api/fd_category', fd_categoryRouter);
저장 후 cmd에 오류가 없다면 DB에 컬렉션이 생성된것을 확인 할 수 있다.
db.fd_categories.insertMany([
{_id:'A', content:'한식', regdate:new Date()},
{_id:'B', content:'중식', regdate:new Date()},
{_id:'C', content:'일식', regdate:new Date()},
{_id:'D', content:'양식', regdate:new Date()},
{_id:'E', content:'분식', regdate:new Date()},
]);
📁exp_20220711/models/food/fd_categorymodel.js
//nodejs mongodb ORM기반 모듈
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var categorySchema = new Schema({
_id : String, // 기본키, 분류코드(3자리 =>AAA,BBB,CCC)
content : { type: String, default: "" }, // 내용
regdate : { type: Date, default: Date.now }, // 가입일, 디폴트값 : 현재시간
});
//몽고 DB에 컬렉션 생성
module.exports = mongoose.model("fd_category", categorySchema);
📁exp_20220711/routes/food/fd_category.js
var express = require("express");
var router = express.Router();
var Category = require("../../models/food/fd_categorymodel");
// 전체 카테고리 조회
// 127.0.0.1:3000/api/fd_category/select.json
router.get("/select.json",async function (req, res, next) {
try {
const result = await Category.find({}).sort({_id:1});
if(result !== null){
return res.send({ status: 200, rows:result });
}
return res.send({ status: 0 });
} catch (e) {
console.error(e);
return res.send({ status: -1, result: e });
}
});
module.exports = router;
📁exp_20220711/app.js
var fd_restaurantRouter = require('./routes/food/fd_restaurant');
var fd_categoryRouter = require('./routes/food/fd_category');
app.use('/api/fd_restaurant', fd_restaurantRouter);
app.use('/api/fd_category', fd_categoryRouter);
📁exp_20220711/routes/food/fd_restaurant.js
식당등록 = 회원가입과 같다
식당이 곧 사람이라고 생각하면 된다!
// 식당이 곧 사람 = 개체
// 127.0.0.1:3000/api/fd_restaurant/insert.json
// {"id" : "", "name" : "", "phone" : "", "address" : "", password:""}
router.post("/insert.json", async function (req, res, next) {
try {
// 암호정보 hash
const hashPw = crypto.createHmac( 'sha256', req.body._id + auth.hashSalt ).update( req.body.password ).digest('hex');
// 저장할 객체 생성
const obj = new Restaurant();
obj._id = req.body._id
obj.name = req.body.name;
obj.phone = req.body.phone;
obj.address = req.body.address;
obj.categorycode = req.body.categorycode;
obj.password = hashPw;
// console.log('obj => ', obj);
const result = await obj.save();
console.log(result);
if(result !== null){
return res.send({ status: 200, result:result });
}
return res.send({ status: 0 });
} catch (e) {
console.error(e);
return res.send({ status: -1, result: e });
}
});
📁exp_20220711/token/fd_auth.js 생성 (로그인 후 반환될 토큰)
// 1. 모듈설치 CMD> npm i jsonwebtoken --save
const jwt = require("jsonwebtoken");
// 토큰 발행용 정보 설정=> 토큰 검증, 토큰 반환
const fd_auth = {
//회원가입, 로그인시 hash salt값
hashSalt : "a#$sdfwe$^rwqwe",
//salt값과 비슷함! 토큰 발행시 쓰는 salt값이라 생각하면 된다!
securitykey: "asdfETWei!@#_%3354erw",
//토큰 발행시 옵션
options: {
algorithm: "HS256", // 토큰 생성 알고리즘 종류
expiresIn: "10h", // 토큰 유효 시간 (10시간=>10h 설정) ex) 30분단위 설정시 => 30m
issuer: "ds_corp", // 발행자
},
//데이터 들어오면 토큰에 넣어 반환시켜주는 역할
// DATA변수에 토큰에 포함하고자 하는 정보를 전달
generateToken: (data) => {
//{FID:'aaa'}
// const token = jwt.sign(data, auth.securitykey, auth.options);
// return token;//위 두줄을 아래 한줄과 같이 쓸 수 있음!
return jwt.sign(data, fd_auth.securitykey, fd_auth.options);
},
};
module.exports = fd_auth; // 마지막에 export해줘야 다른폴더에서 사용가능!
📁exp_20220711/routes/food/fd_restaurant.js
const auth = require('../../token/fd_auth'); // fd_auth.js가져오기
// 식당 로그인 (=> 해야만 메뉴 등록가능)
// 127.0.0.1:3000/api/fd_restaurant/selectcategory.json
// {"_id":"사업자등록번호", "password":"암호"}
router.post("/selectcategory.json",async function (req, res, next) {
try {
const _id = req.body._id;
const hashPw = crypto.createHmac( 'sha256', req.body._id + auth.hashSalt ).update( req.body.password ).digest('hex');
const project = { _id : 1};
const query = { _id : _id, password:hashPw };
const result = await Restaurant.findOne(query).select(project);
console.log(result);
if(result !== null){
// 토큰에 포함할 내용(아이디와 권한정보(식당인지 고객인지))
const data = { "FID" : result._id, "ROLE":"RESTAURANT" }
// 토큰 생성
const token = auth.generateToken(data);
// 결과반환
return res.send({ status: 200, token:token });
}
return res.send({ status: 0 }); //위 조건 조회 후 결과가 없는경우 조회불가
} catch (e) {
//오류 발생시
console.error(e); // 백엔드에게 알려주는 부분
return res.send({ status: -1, result: e }); //프론트에게 알려주는 부분
}
});
CMD> vue create fd_restaurant
CMD> cd fd_restaurant
CMD> npm i axios --save
CMD> npm i vue-router@next --save
CMD> npm i vuex --save
📁vue/fd_restaurant/vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer : {
proxy : {
'/api': {
target : 'http://127.0.0.1:3000',
changeOrigin : true,
logLevel : 'debug'
}
},
port:8081
}
})
💡 프로젝트 생성시 백엔드 서버주소를 다르게 하면 페이지를 나눌 수 있다
- 서버주소 : 3000/restaurant
CMD> vue create fd_restaurant //회원용 vue- 서버주소 : 3000/
CMD> vue create fd_customer //고객용 vue
💡 vue.config.js에서 port번호를 다르게 하면
여러개의 vue를 동시에 돌릴 수 있다
- 식당용 port:8081 ➡️ http://localhost:8081
- 고객용 port:8080 ➡️ http://localhost:8080
CMD> npm run serve
식당용 페이지는 로그인페이지를 첫화면으로 구현하면 된다
고객용 페이지와는 다르게 많은 기능이 필요하지 않으며,
로그인 한 후에 사용할 수 있는 서비스가 대부분이기 때문에
로그인페이지가 메인페이지라고 생각하면 된다
📁vue/fd_restaurant/src/routes/index.js
import { createWebHashHistory, createRouter } from 'vue-router';
import Login from '@/components/restaurant/LoginPage.vue';
import Insert from '@/components/restaurant/InsertPage.vue';
const routes = [
{path:'/', redirect:'/login'},
{path:'/login', component:Login},
{path:'/insert', component:Insert},
];
const router = createRouter({
history : createWebHashHistory(),
routes : routes,
});
//페이지 이동경로추적 from에서 => to로 이동
router.beforeEach((to, from, next)=> {
console.log('to =>', to); //to가 현재임을 알 수 있다
console.log('from =>', from);
//로그인, 로그아웃은 url 저장하지 않고 제외, 나머지는 url은 모두저장
// 로그인 로그아웃은 되돌아갈 페이지가 아니기 때문에
if(to.path !== '/login' && to.path !== '/logout'){
sessionStorage.setItem("CURR_URL", to.path);
// object => string으로 변경 = JSON.stringify(변경할 object)
// string => object으로 변경 = JSON.parse(변경할 문자)
sessionStorage.setItem("CURR_QRY", JSON.stringify(to.query));
}
next();
})
export default router;
📁vue/fd_restaurant/src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import routes from './routes/index';
const app = createApp(App);
app.use(routes);
app.mount('#app')
📁vue/fd_restaurant/src/App.vue
<template>
<div class="box">
<h3>메인화면</h3>
<router-link to='/login'><button>로그인</button></router-link>
<router-link to='/insert'><button>등록하기</button></router-link>
</div>
<router-view></router-view>
</template>
<script>
export default {
}
</script>
<style>
.box{
width: 800px;
border: 5px double skyblue;
padding: auto;
margin: auto;
}
</style>
📁vue/fd_restaurant/src/components/restaurant/insertpage.vue
<template>
<div class="box">
<h3>가게등록</h3>
<div>
<label class="lbl">사업자번호</label>
<input type="text" style="border: 1px solid #cccccc; width:300px" v-model="state._id"/>
<label>중복확인</label>
</div>
<div>
<label class="lbl">암호</label>
<input type="text" style="border: 1px solid #cccccc; width:300px" v-model="state.password" />
</div>
<div>
<label class="lbl">암호확인</label>
<input type="text" style="border: 1px solid #cccccc; width:300px" v-model="state.password1"/>
</div>
<div>
<label class="lbl">상호명</label>
<input type="text" style="border: 1px solid #cccccc; width:300px" v-model="state.name"/>
</div>
<div>
<label class="lbl">연락처</label>
<input type="text" style="border: 1px solid #cccccc; width:300px" v-model="state.phone" />
</div>
<div>
<label class="lbl">주소</label>
<input type="text" style="border: 1px solid #cccccc; width:300px" v-model="state.address"/>
</div>
<div>
<label class="lbl">카테고리코드</label>
<select v-model="state.categorycode">
<option v-for="tmp of state.rows" :key="tmp" :value="tmp._id">{{tmp.content}}</option>
</select>
</div>
<br />
<hr style="border: 1px solid skyblue" />
<button @click="handleJoin">가입하기</button>
</div>
</template>
<script>
import axios from 'axios';
import { onMounted, reactive } from '@vue/runtime-core';
import { useRouter } from 'vue-router';
export default {
setup () {
const router = useRouter();
const state = reactive({
rows : [],
_id : "000-00-0000",
password : "0000",
password1 : "0000",
name : "파전",
phone : "010-9999-9999",
address : "경기",
categorycode : "",
})
// 카테고리 코드 가져오기
const handleData = async() => {
const url = `/api/fd_category/select.json`;
const headers = {"Content-Type":"application/json"};
const { data } = await axios.get(url, {headers});
// console.log(data); // => [{…}, {…}, {…}, {…}, {…}]
if(data.status === 200){
state.rows = data.rows;
}
console.log('state.rows=>',state.rows)
};
onMounted (()=> {
handleData();
});
const handleJoin = async() => {
const url = `/api/fd_restaurant/insert.json`;
const headers = {"Content-Type":"application/json"};
const body = {
_id : state._id,
password : state.password,
name : state.name,
phone : state.phone,
address : state.address,
categorycode : state.categorycode,
}
const { data } = await axios.post(url, body, {headers});
console.log(data);
if(data.status === 200){
alert('로그인되었습니다. 관리페이지로 이동합니다');
router.push({path:'/'})
}
}
return {state, handleJoin}
}
}
</script>
<style lang="css" scoped>
.box{
width: 800px;
border: 5px double skyblue;
padding: auto;
margin: auto;
}
.lbl {
display: inline-block;
width:130px
}
</style>
- handleData 함수 실행
➡️ 데이터 확인 console.log(data)- state.rows = data.rows; 담아두기
- select 태그에 v-model 걸어주기
- option 태그에 값 지정하기
- v-for
- :key
- :value ➡️ v-model에 보낼 값
- {{tmp.들어갈내용}}
<div>
<label class="lbl">카테고리코드</label>
<select v-model="state.categorycode">
<option v-for="tmp of state.rows" :key="tmp" :value="tmp._id">{{tmp.content}}</option>
</select>
</div>
- handleJoin 함수생성
- state 변수생성
- 데이터를 가져올 tag에 v-model 지정
- 결과값 확인 후 이동
if (data.status === 200) { alert('회원가입이 완료되었습니다!'); router.push({ path: '/' }); }