mongoDB + Postman + vue

국비 코딩 풀스택 수업 22일차

비루블 2022. 7. 21. 12:45

어제 시험 친거 곰곰히 생각해보니

조회수 증가를 이전글, 다음글 클릭에는 넣었는데,

게시글 바로 클릭시 넣는걸 깜빡함.

스트레스 받아서 꿈에서도 코딩하는 꿈을 꿧음.

한줄.. 딱 한줄... 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>