국비 코딩 풀스택 수업 32일차 주문 실시간 확인(mqtt), 카카오 맵 지도 화면띄우기, template에서 id 가져오기(getElementById)
요약정리
customer>OrderPage.vue(주문)에서 mqttconnection으로 mqtt연결
restaurant>OrderPage.vue(주문목록)에서도 mqttconnection으로 mqtt연결
식당의 주문목록에서는 mqttSubcribe(구독자)가 필요(topic식당아이디, qos(전송방식 및 주기)설정)
데이터가 오면 반응하도록 message 감지 => 감지시 handleData 다시한번 호출
카카오 맵 지도 화면에 띄우기
오전일과
식당페이지 주문부분
실시간
restaurant > OrderPage.vue
실시간 코드 기입(고객주문 페이지에서 복붙)
exp > routes> fd_broker.js 작성(실시간 정보 받아오기 준비)
var express = require('express');
var router = express.Router();
// CMD > npm i aedes
// 인증설정하기(id=aaa, pw=bbb)
// 클라이언트, 아이디, 암호(byte배열)
// bit(0,1) => byte(010101)
const aedes = require('aedes')({
// 1. 인증하기
authenticate : (client, username, password, callback) => {
//byte배열을 문자열로 변경이 먼저
var pw = Buffer.from(password);
callback(null, (username === 'aaa' && pw.toString() === 'bbb'));
},
// 2. 전송가능
authorizePublish : (client, packet, callback) => {
callback(null, packet)
},
// 3. 수신가능
authorizeSubcribe : (client, packet, callback) => {
callback(null, packet)
},
});
// 서버생성
const httpServer = require('http').createServer();
const ws = require('websocket-stream');
ws.createServer({server:httpServer}, aedes.handle);
httpServer.listen(1884, function(){
console.log('web mqtt 1884포트 구동');
});
const tcpServer = require('net').createServer(aedes.handle);
tcpServer.listen(1883, function(){
console.log('smart app용 mqtt 1883포트 구동');
})
// 잘 접속되나 확인
// client안에 정보가 많아서 아이디만 빼줌
aedes.on('client', function(client){
console.log(`${client.id}접속함`);
});
aedes.on('clientDisconnect', function(client){
console.log(`${client.id} 나감`);
});
aedes.on('subcribe', function(subscriptions, client){
console.log(`${client.id} 구독함`);
})
aedes.on('publish', function(packet, client){
console.log('패킷', packet);
console.log('클라이언트', client);
if(client){
console.log(`${packet.payload.toString()}, ${client.id} 보냄`);
}
})
module.exports = router;
데이터가 오면 여기서 반응
fd_restaurant > src> stores> index.js
import axios from 'axios';
import { createStore } from 'vuex';
export default createStore({
// state : { // 공통변수
// logged : false // 로그인의 상태를 저장
// },
state() {
let tmp = false;
if(sessionStorage.getItem("TOKEN") !== null){
tmp = true;
}
return {
logged : tmp, // 로그인 상태
userid : '', // 로그인한 사용자 아이디(사업자번호)
}
},
getters : { //변수의 값을 가지고 감
getLogged(state) {
return state.logged;
},
getUserid(state) {
return state.userid;
},
},
mutations : { // 변수 값을 변경함
setLogged(state, value) {
state.logged = value;
},
setUserid(state, value) {
state.userid = value;
},
},
actions : { // 비동기 처리
async handleAuth(context) {
const url = `/api/fd_restaurant/auth.json`;
const headers = {
"Content-Type":"application/json",
"token" : sessionStorage.getItem("TOKEN")
};
const { data } = await axios.get(url,{headers});
console.log(data);
if(data.status === 200) {
context.state.userid = data.result;
}
}
}
});
index.js
import axios from 'axios';
import { createStore } from 'vuex';
export default createStore({
// state : { // 공통변수
// logged : false // 로그인의 상태를 저장
// },
state() {
let tmp = false;
if(sessionStorage.getItem("TOKEN") !== null){
tmp = true;
}
return {
logged : tmp, // 로그인 상태
userid : '', // 로그인한 사용자 아이디(사업자번호)
}
},
getters : { //변수의 값을 가지고 감
getLogged(state) {
return state.logged;
},
getUserid(state) {
return state.userid;
},
},
mutations : { // 변수 값을 변경함
setLogged(state, value) {
state.logged = value;
},
setUserid(state, value) {
state.userid = value;
},
},
actions : { // 비동기 처리
async handleAuth(context) {
const url = `/api/fd_restaurant/auth.json`;
const headers = {
"Content-Type":"application/json",
"token" : sessionStorage.getItem("TOKEN")
};
const { data } = await axios.get(url,{headers});
console.log(data);
if(data.status === 200) {
context.state.userid = data.result;
}
}
}
});
handleAuth
이것은 주기적 호출하면 좋음
로그인할떄, 지금은 주문할때 필요
새로고침하면 사라지기때문에, order페이지에서 onMounted로 다시 호출 하는 것임.
fd_restaurant> src> components> restaurant> OrderPage.vue
위에 작성한 코드들 끌어오기
<template>
<div class="container">
<h3>주문관리페이지</h3>
<table>
<thead>
<tr>
<th>주문번호</th>
<th>주문수량</th>
<th>메뉴번호</th>
<th>고객아이디</th>
<th>주문/취소</th>
<th>주문일</th>
</tr>
</thead>
<tbody>
<tr v-for="tmp of rows" :key="tmp">
<td>{{tmp._id}}</td>
<td>{{tmp.cnt}}</td>
<td>{{tmp.foodcode}}</td>
<td>{{tmp.customercode}}</td>
<td v-if="tmp.step === 1">주문</td>
<td v-if="tmp.step !== 1">취소</td>
<td>{{tmp.regdate}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { computed, onMounted, reactive, toRefs } from '@vue/runtime-core'
import axios from 'axios';
import mqtt from 'precompiled-mqtt'
import { useStore } from 'vuex';
export default {
setup () {
const store = useStore();
const state = reactive({
token : sessionStorage.getItem("TOKEN"),
rows : [],
host : '127.0.0.1', //mqtt broker서버주소
port : 1884, //포트번호
options : {
clean : true, //세션 초기화
reconnectPeriod : 20000, //재접속시간
clientId : 'web_cs_' + new Date().getTime(), // 접속아이디(고유)
username : 'aaa', //아이디
password : 'bbb' //암호
},
client : null, // 접속객체
userid : computed(()=> store.getters.getUserid)
});
const mqttConnection = () => {
const url = `ws://${state.host}:${state.port}`;
try {
state.client = mqtt.connect(url, state.options);
state.client.on('connect',() => { //접속 성공
console.log('connect success');
});
state.client.on('error', error => { //접속 오류
console.log('connect error', error);
});
//데이터가 오면 여기서 반응
state.client.on('message', (topic, message) => { // 메세지가 왔을때
console.log(`receive message ${topic} : ${message}`);
handleData();
});
}
catch(e) {
console.log('mqtt error', e);
}
};
const mqttSubscribe = () =>{
if(state.client !== null){
const topic = `/restaurant/${state.userid}`
//const topic = `/#`
console.log(topic);
state.client.subscribe(topic, {qos:0}, (error, res) =>{
if(error) {
alert('구독설정 실패')
}
console.log('subscribe success', res);
});
}
};
onMounted(async()=>{
//토큰의 유효성 검사 actions의 handleAuth호출하기
await store.dispatch('handleAuth');
mqttConnection();
mqttSubscribe();
handleData();
});
const handleData = async() =>{
const url = `/api/fd_order/select.json`;
const headers = {"Content-Type":"application/json", token : state.token};
const {data} = await axios.get(url, {headers})
console.log(data);
if(data.status === 200){
state.rows = data.rows;
}
}
return {state, ...toRefs(state)}
}
}
</script>
<style lang="css" scoped>
.container {
width : 800px;
padding : 20px;
border : 1px solid #cccccc;
}
</style>
const store = useStore();
온마운트
onMounted(async()=>{
//토큰의 유효성 검사 actions의 handleAuth호출하기
await store.dispatch('handleAuth');
mqttConnection();
mqttSubscribe();
handleData();
});
//state.userid
userid : computed(()=> store.getters.getUserid)
mqttSubcribe에
topic(userid 기입) 오류나는 학생들이 많아서 `/#`으로도 써봄.
const topic = `/restaurant/${state.userid}`
//const topic = `/#`
메세지 전송 감지하고 핸들데이터 호출할 수 있게 handleData 기입
state.client.on('message', (topic, message) => { // 메세지가 왔을때
console.log(`receive message ${topic} : ${message}`);
handleData();
});
오후일과 < 카카오 지도 넣기 >
exp> model> food> fd_ridermodel.js
var mongoose = require('mongoose');
const AutoIncrement = require('mongoose-sequence')(mongoose);
var Schema = mongoose.Schema;
var deliverySchema = new Schema({
_id : Number,
startdate : { type:Date, default:Date.now },//주문일자
enddate : { type:Date, default:Date.now },//주문일자
riderphone : { type:String, default:'' }, //연락처
orderno : { type:Number, default:0 }, // 상품코드
restaurantcode : { type:String, default:'' }, // 1주문, 2취소
});
deliverySchema.plugin(AutoIncrement,{
id : "SEQ_DELIVERY_CODE",
inc_field : '_id', // 위에 생성한 스키마 중에서 어떤항목(_id)
start_seq : 100001 // 시작값, 생략시 1부터 시작
});
module.exports = mongoose.model('fd_delivery', deliverySchema);
exp> routes> fd_> fd_rider.js
var express = require('express');
var router = express.Router();
var Rider = require('../../models/food/fd_ridermodel');
var Delivery = require('../../models/food/fd_deliverymodel');
// 배달자 1명 조회
// 127.0.0.1:3000/api/fd_rider/selectone.json?_id=3142
router.get('/selectone.json', async function(req, res, next) {
try {
const query = {_id : req.query._id};
const result = await Rider.findOne(query);
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});
}
});
// 배달자 목록
// 127.0.0.1:3000/api/fd_rider/select.json?page=1
router.get('/select.json', async function(req, res, next) {
try {
const page = Number(req.query.page);
const result = await Rider.find({}).sort({_id:1}).skip((page-1)*10).limit(10);
console.log(result);
if(result !== null){
const total =await Rider.countDocuments({});
return res.send({status:200, result:result, total:total});
}
return res.send({status:0});
}
catch(e){
console.error(e);
return res.send({status:-1, result:e});
}
});
// 배달자 등록
// 127.0.0.1:3000/api/fd_rider/insert.json
// {"_id":"3142", "name":"김종훈", "lat": 11.123123123, "lng": 11.123123123123}
router.post('/insert.json', async function(req, res, next) {
try {
const obj = new Rider();
obj._id = req.body._id;
obj.name = req.body.name;
obj.lat = Number(req.body.lat);
obj.lng = Number(req.body.lng);
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});
}
});
module.exports = router;
fd_restaurant> src> components> rider> RiderPage.vue
<template>
<div>
<h3>배달자관리</h3>
<button @click="handleriderinsert()">배달자등록</button>
<table border="1">
<thead>
<tr>
<th>연락처</th>
<th>이름</th>
<th>위도</th>
<th>경도</th>
<th>등록일</th>
<th>버튼</th>
</tr>
</thead>
<tbody>
<tr v-for="tmp in state.rows" :key="tmp">
<td style="cursor: pointer; color: blue;" @click="handleMap(tmp._id)">{{tmp._id}}</td>
<td>{{tmp.name}}</td>
<td>{{tmp.lat}}</td>
<td>{{tmp.lng}}</td>
<td>{{tmp.regdate}}</td>
<td><button @click="handleMap(tmp._id)">위치확인</button></td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { onMounted, reactive } from '@vue/runtime-core'
import axios from 'axios'
import { useRouter } from 'vue-router'
export default {
setup () {
const router = useRouter();
const state = reactive({
page:1,
rows:[],
})
onMounted(()=>{
handleData();
})
const handleData = async() =>{
const url = `/api/fd_rider/select.json?page=1`;
const headers = {"Content-Type":"application/json"};
const {data} = await axios.get(url, {headers});
console.log(data);
if(data.status === 200){
state.rows = data.result;
}
}
const handleMap = (phone) => {
router.push({path:'/map', query:{id : phone}})
}
const handleOne = (tmp) => {
router.push({path:'/riderselectone', query:{id : tmp}})
}
const handleriderinsert = () => {
router.push({path:'/riderinsert'})
}
return {state, handleMap, handleOne, handleriderinsert}
}
}
</script>
<style lang="scss" scoped>
</style>
fd_restaurant> src> components> rider> RiderInsertPage.vue(혼자 만듬)
<template>
<div>
<h3>라이더 등록</h3>
<input placeholder="번호" v-model="_id"/><br/>
<input placeholder="이름" v-model="name"/><br/>
<input placeholder="위도" v-model="lat"/><br/>
<input placeholder="경도" v-model="lng"/><br/>
</div>
<div>
<button @click="handleRiderInsert()">등록하기</button>
</div>
</template>
<script>
import { reactive, toRefs } from '@vue/reactivity'
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
setup () {
const router = useRouter();
const state = reactive({
_id : '',
name : '',
lat : '',
lng : '',
})
const handleRiderInsert = async() => {
const url = `/api/fd_rider/insert.json`;
const headers = {"Content-Type" : "application/json"};
const body = {
"_id" : state._id,
"name" : state.name,
"lat" : state.lat,
"lng" : state.lng,
};
const {data} = await axios.post(url, body, {headers});
console.log(data);
if(data.status === 200){
alert('배달자 등록 완료');
router.push({path:'/rider'})
}
else{
alert('회원가입 실패')
}
};
return {state, ...toRefs(state), handleRiderInsert}
}
}
</script>
<style lang="scss" scoped>
</style>
중요중요중요중요중요중요중요중요중요중요중요중요중요중요중요중요
중요중요중요중요중요중요중요중요중요중요중요중요중요중요중요중요
fd_restaurant> src> components> rider> MapPage.vue
<template>
<div>
{{state}}
<hr/>
<div v-if="state.row">
아이디 : {{row._id}}<br/>
이름 : {{row.name}}<br/>
위도 : {{row.lat}}<br/>
경도 : {{row.lng}}<br/>
가입일 : {{row.regdate}}<br/>
</div>
<hr/>
<div id="map" style="width:500px;height:400px; border:1px solid #cccccc"></div>
</div>
<div>
<button @click="handleBack()">뒤로가기</button>
</div>
</template>
<script>
import { reactive, toRefs } from '@vue/reactivity'
import { useRoute, useRouter } from 'vue-router';
import axios from 'axios';
import { onMounted } from '@vue/runtime-core';
export default {
setup () {
const route = useRoute();
const router = useRouter();
const state = reactive({
id : route.query.id,
row : null,
});
const handleData = async() =>{
const url = `/api/fd_rider/selectone.json?_id=${state.id}`;
const headers = {"Content-Type": "application/json"};
const {data} = await axios.get(url, {headers});
console.log(data);
if(data.status === 200){
state.row = data.result;
}
}
onMounted(async()=>{
await handleData();
const script = document.createElement("script")
script.setAttribute("src", "//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=e3c5397581b7ac5aaa0dbc7b4d7c3c18")
document.head.appendChild(script);
console.log(window);
script.onload = () => window.kakao.maps.load(initMap);
});
const initMap = () => {
const div = document.getElementById("map");
const position = new window.kakao.maps.LatLng( state.row.lat, state.row.lng )
const options = {
center : position,
level : 4,
}
const map = new window.kakao.maps.Map(div, options);
new window.kakao.maps.Marker({
map : map,
position : position
});
};
const handleBack = () =>{
router.go(-1)
}
return {state, ...toRefs(state), handleBack}
}
}
</script>
<style lang="scss" scoped>
</style>
위도, 경도 찾기
위치 찾아서 일단 해당 위도경도로 회원가입 시켜놨음.
https://apis.map.kakao.com/web/guide/
https://developers.kakao.com/console/app/781154
vue3에서 다음 카카오 지도 맵 쓰는 방법
<div id="map" style="width:500px;height:400px; border:1px solid #cccccc"></div>
onMounted(async()=>{
await handleData();
const script = document.createElement("script")
script.setAttribute("src", "//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=본인키")
document.head.appendChild(script);
console.log(window);
script.onload = () => window.kakao.maps.load(initMap);
});
※주의점 : vue3에서 쓸때 autoload=false&를 넣어줘야함(설명 사이트에는 적혀있지 않음)
initmap
전부 가이드 보고 하는 것임
div변수에 document.getElementById("map")
을 이용하여 template에 있는 map을 가져다옴
const initMap = () => {
const div = document.getElementById("map");
const position = new window.kakao.maps.LatLng( state.row.lat, state.row.lng )
const options = {
center : position,
level : 4,
}
const map = new window.kakao.maps.Map(div, options);
new window.kakao.maps.Marker({
map : map,
position : position
});
};