국비 코딩 풀스택 수업 22일차
어제 시험 친거 곰곰히 생각해보니
조회수 증가를 이전글, 다음글 클릭에는 넣었는데,
게시글 바로 클릭시 넣는걸 깜빡함.
스트레스 받아서 꿈에서도 코딩하는 꿈을 꿧음.
한줄.. 딱 한줄... onMounted에 딱 한줄만 추가하면 되는데 ㅜㅠㅜ
요약정리
erd 다이어그램을 만들었고,
오후에는 대부분 실습을 함.
item 홈페이지, item 상세페이지 실습으로 처음 만든 날
오전일과
백엔드
prj.vuerd.json
order테이블 만듬

장바구니 로그인 안하고 하는 경우도 있음
(실습하진 않겠지만 지금 간단히 만들어봄)
장바구니 zero N사용 제로엔
장바구니는 사용할때 회원정보 없이 사용가능하기 때문에
장바구니 -> 회원정보로 제로엔을 걸어줌
제로엔으로 안걸어주고 one N 원앤으로 걸어주고
memberid에 NULL로 걸어주면, zero N 제로엔으로 바뀜
어느걸 쓰거나 상관없음

주소컬렉션 만듬

게시판 erd만듬

clob은 책2권분량 텍스트를 넣을 수 있음.
이미지에는 blob
N-N이면 not null임 똑같은 말.
memberid 멤버아이디가 비워 있으면 게시판에 글을 쓸 수 없음.
not null이면 비워두면 안된다는 것임.
지금은 그냥 NULL로 바꿔줌
varchar2 뒤에 숫자는
오라클기준으로 글자 제한으로 붙여놓은 것임
몽고디비는 제한 x
erd 다이어그램 설계시 정적인 부분부터 설계후 동적 부분 설계
백엔드 item.js
get selectitem
쇼핑몰 메인페이지 고객이 볼 화면
// 쇼핑몰 메인페이지
// 127.0.0.1:3000/item/selectitem.json?page=1&cnt=4
router.get('/selectitem.json', async function(req, res, next) {
try {
// 1. 조회조건
const page = Number(req.query.page);
const cnt = Number(req.query.cnt);
//2. 조회 하기
const result = await Item.find({}).sort({_id:-1}).skip((page-1) * cnt).limit(cnt);
//find => [{}, {}, {}]
//findOne = > {}
console.log(result);
if(result !== null){
for(let i = 0 ; i<result.length; i++){
const result1 = await ItemImage.find({code : result[i]._id})
.select({filedata:0, filetype:0, filename:0, filesize:0})
.sort({_id : 1}).limit(1);
// console.log(result1);
// length 0인 경우 [],
// length 1인 경우 [{}]
if(result1.length > 0){
result[i].imageurl = `/itemimage/image?no=${result1[0]._id}`
}
}
return res.send( {status:200, result:result} );
}
return res.send( {status : 0} );
}
catch(e){
console.error(e);
return res.send({status:-1, result:e});
}
});
토큰검정 x 로그인 하나안하나 보여야함
원래는 판매량순 이런거 해야하는데 그냥 내림차순 해줌
스킵개념 중요중요중요중요중요중요중요중요중요중요중요
1=> 0~11, 2=>12, 3=>24 스킵(물건 여러개 보여줄떄 넘길때)
리밋 최대 12개
const result = await Item.find({}).sort({_id:-1}).skip((page-1) * 12).limit(12);
센스있게 한다면
127.0.0.1:3000/item/selectitem.json?page=1&cnt=1~12
1개씩 보여줘라 12개씩 보여줘라 할 수도있음
이게 좋음
구간마다 selectitem을 만들 수 없으니까
가변함수를 넣어서 selectitem 하나 만들어 놓고 계속 화면에
표기할 수 있음 중요중요중요중요중요중요중요중요중요중요
최종
127.0.0.1:3000/item/selectitem.json?page=1&cnt=4
const cnt = Number(req.query.cnt);
const result = await Item.find({}).sort({_id:-1}).skip((page-1) * cnt).limit(cnt);
데이터 형태 확인(데이터 어떻게 불러오는지 보고 이미지 url삽입하려고)
//find => [{}, {}, {}]
//findOne = > {}
console.log(result);
이미지 불러오는 스크립트 확인
ItemImage중요중요중요중요중요중요중요중요중요중요중요중요중요중요
어떻게 불러오는지 확인 for문
불러올때 url도 추가해줘야함.
for(let i = 0 ; i<result.length; i++){
const result1 = await ItemImage.find({code : result[i]._id})
.select({filedata:0, filetype:0, filename:0, filesize:0})
.sort({_id : 1}).limit(1);
// console.log(result1);
// length 0인 경우 [],
// length 1인 경우 [{}]
if(result1.length > 0){
result[i].imageurl = `/itemimage/image?no=${result1[0]._id}`
}
}
이미지 url을
result에 한번에 넣어주는게
배열을 따로 한번 더 안만들어서 깔끔하고 처리하기 쉬워짐
if(result1.length > 0){
result[i].imageurl = `/itemimage/image?no=${result1[0]._id}`
}
--------------------------------------------------------
한번에 result에 넣어주려는 목적
itemmodel.js
대표이미지 URL보관용 추가
imageurl : { type:String, default:'' }, // 대표이미지URL보관용
--------------------------------------------------------
if(result1.length > 0){
result[i].imageurl = `/itemimage/image?no=${result1[0]._id}`
}
이거 console.log(result1) 찍어보고
배열이 어떤지 확인하고
find로 찾았기 떄문에 [{}]
반복문 안에 있고 배열이 있다면( [{}]형태라면 ) result[0]에 이미지 url을 기입해줌
상세페이지 4개의 이미지 정보
get selectitemone
// 상세화면 페이지 (4개의 이미지 정보)
// 127.0.0.1:3000/item/selectitemone.json?code=2
router.get('/selectitemone.json', async function(req, res, next) {
try{
const code = Number(req.query.code);
const result = await Item.findOne({_id:code});
console.log((result));
if(result !== null) { // 물품 정보가 존재한다면
const result1 = await ItemImage.find({ code : code })
.select({filedata:0, filetype:0, filename:0, filesize:0})
.sort({_id : 1})
.limit(4);
for( let i = 0; i<result1.length ; i++){
result1[i].imageurl = `/itemimage/image?no=${result1[i]._id}`;
}
console.log(result1);
return res.send({status : 200, result:result, result1:result1})
}
return res.send( {status : 0} );
}
catch(e){
console.error(e);
return res.send({status:-1, result:e});
}
});
하나의 게시물에 이미지 정보를 4개까지만 들고옴
게시물의 정보 + 이미지 정보 4개
이미지가 없다면 배열이 비워진 채로 출력 [][][]
사진참고
이거 이미지 imageurl을 넣어야함
for문
for( let i = 0; i<result1.length ; i++){
result1[i].imageurl = `/itemimage/image?no=${result1[i]._id}`;
}
로그 찍어보고, 데이터 형태 보고 기입해줌
중요중요중요중요중요중요중요중요중요중요중요
주문 routes 새로만듬
order.js(app.js에 라우트 등록)
oredermodel.js
order.js
구매자의 주문하기
post orderinsert
//주문하기(주문수량, 주문자의 토큰, 물품코드, )
//127.0.0.1:3000/order/insert.json
// {"code":9, "cnt":13}
router.post('/insert.json', auth.checkToken,
async function(req, res, next) {
try {
const memberid = req.body.SID;
const itemcode = Number(req.body.code)
const ordercnt = Number(req.body.cnt)
// 2. 저장할 객체 생성
const obj = new Order();
obj.itemcode = itemcode;
obj.cnt = ordercnt;
obj.memberid = memberid;
obj.regdate = moment().format('YYYY-MM-DD HH:mm:ss');
const result = await obj.save();
// 4. 결과에 따른 반환값
if(result !== null){
return res.send( {status:200} );
}
return res.send( {status : 0} );
}
catch(e){
console.error(e);
return res.send({status:-1, result:e});
}
});
구매자라서 checkToken으로 바꿔줌
오후일과
거의 실습 느낌으로
homepage
<template>
<div>
<h3>홈화면</h3>
<vueper-slides>
<!-- 배열이 [{"title"},{}] -->
<vueper-slide
v-for="(tmp) in slides"
:key="tmp"
:style="'color:#ffffff'"
:image="tmp.image"
:title="tmp.title"
:content="tmp.content">
</vueper-slide>
</vueper-slides>
</div>
<div class="box">
<div class="item" v-for="tmp of state.rows" :key="tmp" @click="handleContent(tmp._id)">
<img v-if="tmp.imageurl !== ''" :src="tmp.imageurl" style="width:100%; height:100px"/>
<img v-if="tmp.imageurl ===''" :src="require('../assets/imgs/noimage.png')" style="width:100%; height:100px"/>
{{tmp.name}} {{tmp.price}}원
</div>
</div>
</template>
<script>
import { onMounted, reactive } from '@vue/runtime-core';
import { VueperSlides, VueperSlide } from 'vueperslides'
import 'vueperslides/dist/vueperslides.css'
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
components: {
VueperSlides,
VueperSlide
},
setup () {
const slides = [
{
title: 'Slide #1',
content: 'Slide 1 content.',
image:'https://picsum.photos/500/300?imager=12'
},
{
title: 'Slide #2',
content: 'Slide 2 content.',
image:'https://picsum.photos/500/300?imager=123'
},
{
title: 'Slide #3',
content: 'Slide 2 content.',
image:'https://picsum.photos/500/300?imager=42'
},
];
const router = useRouter();
const state = reactive({
page : 1,
cnt : 12,
rows : null,
});
onMounted(()=> {
handleData();
});
const handleData = async() => {
const url = `/item/selectitem.json?page=${state.page}&cnt=${state.cnt}`
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 handleContent = (code) => {
console.log('handleContent=>', code);
router.push({path:'/itemcontent', query:{code : code}});
};
return {slides, state, handleContent}
}
}
</script>
<style lang="css" scoped>
.box{
width : 1000px;
border : 1px solid #cccccc;
display : grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
column-gap: 5px;
row-gap : 5px;
padding : 10px;
}
.item{
border :1px solid #aecb8c;
padding : 5px;
cursor: pointer;
}
</style>
itemcontent
직접 만들어봄
<template>
<div>
<div class="box">
<div class="item" v-if="state.imgs">
<div>
<img v-if="state.imgs[0] !==undefined" :src="state.imgs[0].imageurl" />
</div>
<div class="image">
<div class="imagebox">
<img v-if="state.imgs[1] !==undefined" :src="state.imgs[1].imageurl" style="width:100%" />
</div>
<div class="imagebox">
<img v-if="state.imgs[2] !==undefined" :src="state.imgs[2].imageurl" style="width:100%"/>
</div>
<div class="imagebox">
<img v-if="state.imgs[3] !==undefined" :src="state.imgs[3].imageurl" style="width:100%"/>
</div>
</div>
</div>
<div class="item" v-if="state.row">
<div>
<h3>{{state.row.name}}</h3>
</div>
<div>
{{state.row.content}}
</div>
<div>
가격 : {{state.row.price}}원
</div>
<select v-model="state.cnt">
<option v-for="tmp of 100" :key="tmp">{{tmp}}</option>
</select>
<button @click="handleOrder()">주문하기</button>
</div>
</div>
</div>
</template>
<script>
import { reactive } from '@vue/reactivity'
import { useRoute } from 'vue-router';
import { onMounted } from '@vue/runtime-core';
import axios from 'axios';
export default {
setup () {
const route = useRoute();
const state = reactive({
code : Number(route.query.code),
row : null,
imgs : null,
token : sessionStorage.getItem("TOKEN"),
cnt : 1
});
onMounted(() => {
handleData();
})
const handleData = async() => {
const url = `/item/selectitemone.json?code=${state.code}`;
const headers = {"Content-Type":"application/json"};
const { data } = await axios.get(url, {headers});
console.log(data);
if(data.status === 200){
state.row = data.result;
state.imgs = data.result1;
}
}
const handleOrder = async() => {
if(state.token === null) {
alert('로그인 페이지로 이동')
}
else{
const url = `order/insert.json`;
const headers = {
"Content-Type":"application/json",
"token" : state.token
}
const body = {
code : state.code,
cnt : state.cnt
}
console.log(body);
const {data} = await axios.post(url, body, {headers});
if(data.status === 200){
alert('주문완료')
}
console.log(data);
}
};
return {state, handleOrder}
}
}
</script>
<style lang="css" scoped>
.box {
margin-top : 10px;
width : 800px;
border : 0px solid #cccccc;
display : grid;
grid-template-columns: 2fr 1fr;
column-gap : 5px;
row-gap : 5px;
padding : 0px;
}
.item {
border : 1px solid #aecb8c;
padding : 5px;
cursor : pointer;
}
.image {
margin-top : 10px;
width : 100%;
border : 0px solid #cccccc;
display : grid;
grid-template-columns: 1fr 1fr 1fr;
column-gap : 5px;
row-gap : 5px;
padding : 3px;
}
.imagebox {
border : 1px solid #cccccc;
}
</style>