mongoDB + Postman + vue

국비 코딩 풀스택 수업 20일차(백엔드 for문 질문했음)

비루블 2022. 7. 19. 14:00

요약정리

...torefs할줄 모름 나중에 선생님에게 질문 예정

이미지 배열 만들기, 디폴트값, 이미지 데이터 담기

 

for문으로 이미지를 하나씩 업로드할때 예기치 못한 상황으로 업로드가 중간에 막힌다면,

어떻게 코드를 짜아햐는지 질문했음. 답변(원래는 한번에 올리는게 맞는데, 현재 시퀀스가 안되서 1개씩 올린다는 답변을 받았고, else로 0또는 파일의 전체개수가 아니면 output하지 않게끔 만들어라고 답변 받음.)

 

 

오전일과
auth.js
토큰에 권한을 넣었지만(멤버 로그인 부분) 

 

토큰 복원할떄 권한 가져오는 것을 안해놨었음.
auth.js > checkToken1

checkToken1은 item에서 사용함.(판매자용 페이지는 TOKEN에 있는 role이 필요하기때문)

if(data.SROLE !== 'SELLER'){
    return res.send({status:0, result:'접근불가토큰'})
}


백엔드 오류처리부분 상세하게 만들어 줘음. 중요중요중요중요중요중요중요

catch(e){
    console.error(e, e.message);
    if(e.message === 'invalid signature'){
        return res.send({status:0, result:'인증실패'})
    }
    else if(e.message === 'jwt expired'){
        return res.send({status:0, result:'시간만료'})
    }
    else if(e.message === 'invalid token'){
        return res.send({status:0, result:'유효하지 않는 토큰'})
    }

    return res.send({status:0, result:'토큰 오류'})
}


item.js
get selectone (게시물 수정시 해당 게시글 하나의 정보가 필요해서 만들어 줬음.)

// 물품목록조회(페이지네이션, 검색기능 미포함)
// 127.0.0.1:3000/item/select.json
router.get('/select.json', auth.checkToken1, async function(req, res, next) {
    try {
        // 1. 조회조건
        const query = { seller : req.body.SID }

        //2. 조회 하기
            const result = await Item.find(query).sort({_id:-1});

        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});
    }
});

 

토큰은 그닥 필요x, 그러나 그냥 넣어줬음
다시 뺏음. 토큰을 뺄때 토큰과 연관된 코드 삭제 아래 사진 참고



물음표 다음은 숫자는 쿼리(개념은 아는데 쓰는 법을 한번씩 까먹음.)

예시

const handleUpdate = () => {
    if(state.radio === ''){
        alert('수정할 항목을 선택하세요');
        return false;
    }
    router.push({path:'/selleritemupdate', query:{code:state.radio}})
}

const handleImage = () => {
    // selleritemimage?code=111
    router.push({path:'/selleritemimage', query:{code:state.radio}});
}



물품 등록
...torefs를 배움 나머진 그대로 근데 할줄모름

그래서 나는 그냥 수동으로 state.~~~~다 붙여줬음.

 



SellerItem에서 
radio를 배우는 중

<input type="radio" name="radio" v-model="state.radio" :value="tmp._id"/>

 

name을 줘야 중복체크가 안되고, value를 줘야 무엇이 체크되어있는지
고유값을 알 수 있음
state 찍어보면됨.



handleUpdate
router.push({path:'/selleritemupdate', query:{code:state.radio}})
다음페이지로 쿼리로 정보 넘겨주기(개념은 아는데 간간히 사용법을 까먹음.)


SellerItemUpdate.vue
쉬는 시간에 직접 만들었고 전부 이상 없이 전부 작동 되었다.
선생님의 코드와 비교해보니 맞게 짠거 같다

<template>
    <div class="container">
        <h3>물품 수정</h3>
        <div v-if="state.row">
            <hr/>
            <div>
                <label class="lbl">물품명</label>
                <input type="text" placeholder="물품명" v-model="state.row.name"/>
            </div>


            <div>
                <label class="lbl">내용</label>
                <textarea rows="6" placeholder="내용" v-model="state.row.content"></textarea>
            </div>

            <div>
                <label class="lbl">가격</label>
                <input type="number" placeholder="가격" v-model="state.row.price"/>
            </div>

            <div>
                <label class="lbl">수량</label>
                <input type="number" placeholder="수량" v-model="state.row.quantity"/>
            </div>

            <div>
                <label class="lbl">분류</label>
                <select v-model="state.row.category">
                    <option>AAA</option>
                    <option>BBB</option>
                    <option>CCC</option>
                    <option>DDD</option>
                    <option>EEE</option>
                </select>
            </div>
        </div>

<!--         <div>
            <label class="lbl">이미지</label>
            <img :src="state.img" style="width:150px"/>
            <input type="file" @change="handleImage($event)"/>
        </div>
        
        <div>
            <label class="lbl"></label>
            <button @click="handleInsert">글쓰기</button>
        </div> -->
        <div>
            <button @click="handleUpdate()">수정하기</button>
        </div>
    </div>    
</template>

<script>
import { reactive } from '@vue/reactivity'
import { onMounted } from '@vue/runtime-core'
import { useRoute, useRouter } from 'vue-router'
import axios from 'axios'
export default {
    setup () {
        const route = useRoute();
        const router = useRouter();

        const state = reactive({
            code  : Number(route.query.code),
            row   : [],
            token : sessionStorage.getItem("TOKEN")
        })

        const handleData = async() => {
            const url =`/item/selectone.json?code=${state.code}`;
            const headers = {
                "Contents-Type":"application/json",
                token : state.token
            }
            const {data} = await axios.get(url, {headers});
            if(data.status === 200){
                state.row = data.result;
            }
        }

        onMounted(()=>{
            handleData();
        });


        //물품 수정
        const handleUpdate = async() =>{
            const url = `/item/update.json`;
            const headers = {"Content-Type":"application/json", token : state.token}
            const body = {
                code     : state.code,
                name     : state.row.name,
                content  : state.row.content,
                price    : state.row.price,
                quantity : state.row.quantity,
                category : state.row.category
            }
            const {data} = await axios.put(url, body, {headers});
            if(data.status === 200){
                alert('수정이 완료되었습니다.')
                router.push({path:'/selleritem'})
            }
            

        }
        

        return {state, handleUpdate}
    }
}
</script>

<style lang="css" scoped>
        .container {
        width: 600px;
        border: 1px solid #cccccc;
        padding:20px;
    }
    .lbl {
        display: inline-block;
        width: 90px;
    }

</style>

 



handleimage 이미지 등록을 만들것임

중요중요중요중요중요중요중요중요중요중요중요중요중요중요

SellerItemImage.vue물품이미지
최소2개 최대5개 이미지 업로드하게끔
버튼 개수 제한

<template>
    <div>
        <h3>물품이미지</h3>
        {{state}}
        <hr/>


        <button @click="handleAppend()">항목추가(+)</button>
        <button @click="handleRemove()">항목삭제(-)</button>
        <div v-for="(tmp, idx) of state.cnt" :key="tmp">
            <img :src="state.imageurl[idx]" style="width: 30px"/>
            <input type="file" @change="handleChangeImage($event, idx)"/>

        </div>

        <button @click="handleImageAction()">이미지일괄등록</button>
    </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),
            cnt : 2,
            imageurl : [],
            imagedata : []
        });

        onMounted(()=>{
            for(let i=0; i<5; i++){
                state.imageurl.push(require('../../assets/logo.png'))
                state.imagedata.push( new File([""], ""))
                //빈파일데이터( new File(["파일내용"], "파일이름"))
            }
        });


        const handleAppend = () =>{
            state.cnt++;
            if(state.cnt > 5){
                state.cnt = 5;
            }
        };

        const handleRemove = () =>{
            state.cnt--;
            if(state.cnt < 2){
                state.cnt = 2;
            }
        };

        const handleChangeImage = (e, idx) =>{
            console.log(e, idx);
            if(e.target.files.length>0){// 파일을 선택함
                state.imageurl[idx] = URL.createObjectURL(e.target.files[0])
                state.imagedata[idx] = e.target.files[0];
            }
            else{//파일을 취소함
                state.imageurl[idx] = require('../../assets/logo.png')
                state.imagedata[idx] = new File([""], "")
                //빈파일데이터( new File(["파일내용"], "파일이름"))
            }
        }

        const handleImageAction = async() =>{
            const url = `/itemimage/insertbatch.json`
            const headers = {
                "Content-Type":"multipart/form-data",
                token : sessionStorage.getItem("TOKEN")
                }
            
            const body = new FormData();
            body.append("code", state.code);

            for(let i=0; i<state.cnt; i++){
                body.append("image", state.imagedata[i])
            }
            const {data} = await axios.post(url, body, {headers});
            console.log(data);
        }
        

        return {state, handleAppend, handleRemove, handleChangeImage, handleImageAction}
    }
}
</script>

<style lang="scss" scoped>

</style>


이미지부분 변수 사진

onMounted(()=>{
    for(let i=0; i<5; i++){
        state.imageurl.push(require('../../assets/logo.png'))
        state.imagedata.push( new File([""], ""))
        //빈파일데이터( new File(["파일내용"], "파일이름"))
    }
});

onMounted로 모든 배열에 디폴트이미지 + 디폴드(공백 정보)를 넣어줬음
이미지 배열 중요중요중요중요중요중요중요중요중요중요


배열마다 디폴트값은 logo파일을 적용
e.target.files.length에 따라 파일이 있나없나를 확인하는 것임

궁금해서 console.log(e)로 찍어보니 해당 files에 length가 따로 있었음
1이면 파일이 있다, 0이면 파일이 없다.

 


파일이미지 뿐만아니라 데이터도 있어야함

const state = reactive({
    code : Number(route.query.code),
    cnt : 2,
    imageurl : [],
    imagedata : []
});
const handleChangeImage = (e, idx) =>{
    console.log(e, idx);
    if(e.target.files.length>0){// 파일을 선택함
        state.imageurl[idx] = URL.createObjectURL(e.target.files[0])
        state.imagedata[idx] = e.target.files[0];
    }
    else{//파일을 취소함
        state.imageurl[idx] = require('../../assets/logo.png')
        state.imagedata[idx] = new File([""], "")
        //빈파일데이터( new File(["파일내용"], "파일이름"))
    }
}

 


오후

itemimage.js 중요중요중요중요중요중요중요중요중요중요중요
다중이미지 업로드 만듬
점심먹고 집중력이 좀 떨어져서
postman에서 살짝 못따라갔음.(이부분은 아래서 혼자서 해결함. 아래서 설명 예정)

var express = require('express');
var router = express.Router();

const multer = require('multer');
const upload = multer( {storage : multer.memoryStorage()} );

require('moment-timezone');
var moment = require('moment');
moment.tz.setDefault('Asia/Seoul');

// 외부에서 사용
const auth = require('../token/auth');

var Item       = require('../models/itemmodel');
var ItemImage  = require('../models/itemimagemodel');


// 127.0.0.1:3000/itemimage/insertbatch.json
// 토큰, 물품코드, 여러개 이미지
router.post('/insertbatch.json',  
        upload.array("image"), auth.checkToken1,
        async function(req, res, next) {
    try {
        console.log(req);
        // 1. 전달받은 물품코드가 현재 로그인한 판매자의 것인지 확인
        const query  = { 
            _id     : Number(req.body.code),   
            seller  : req.body.SID
        };

        const obj = await Item.findOne(query);

        console.log(obj);
        if(obj !== null) { //조회 후 결과가 있으면
            // req.files  => [  {}, {} ]   => 2
            let cnt = 0;
            for( let i=0;i<req.files.length; i++) {
                // 저장할 객체 생성
                let obj1        = new ItemImage();
                obj1.code       = Number(req.body.code);                    
                obj1.filedata   = req.files[i].buffer;                   
                obj1.filename   = req.files[i].originalname;                   
                obj1.filetype   = req.files[i].mimetype;                   
                obj1.filesize   = req.files[i].size;                   
                obj1.regdate    = moment().format('YYYY-MM-DD HH:mm:ss');                   

                // DB에 저장    
                let result = await obj1.save();
                if(result !== null) {
                    cnt++;
                }
            }
            
            if( cnt === req.files.length ) {
                return res.send( {status : 200} ); 
            }
        }
        return res.send( {status : 0} );
    }
    catch(e){
        console.error(e);
        return res.send({status:-1, result:e});
    }
});



// 127.0.0.1:3000/itemimage/insert.json
// 토큰 , 물품코드, 1개 이미지
router.post('/insert.json',  
        upload.single("image"), auth.checkToken1,
        async function(req, res, next) {
    try {
        // 1. 전달값 받기
        const seller = req.body.SID;
        const code   = Number(req.body.code);

        // 2. 전달받은 물품코드가 현재 로그인한 판매자의 것인지 확인
        const query  = { 
            _id     : code,   
            seller  : seller
        };

        const obj = await Item.findOne(query);
        console.log(obj);
        if(obj !== null) { //조회 후 결과가 있으면
            //등록가능 위치
            const obj1 = new ItemImage();
            obj1.code = code;
            obj1.filedata = req.file.buffer;
            obj1.filename = req.file.originalname;
            obj1.filetype = req.file.mimetype;
            obj1.filesize = req.file.size;
            obj1.regdate  = moment().format('YYYY-MM-DD HH:mm:ss');

            const result = obj1.save();
            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});
    }
});



module.exports = router;


그래서 백엔드cmd에서 files의 내용을 보지 못했고, 코드를 짜는데 
정보를 변수에 저장을 못했음.
그래서 선생님 웹보드 백엔드 itemimage.js 복붙함.
비교해보니 집중력이 떨어졌던게 맞다.
변수 선언을 let으로 해야 for문에서 변수변경이 가능한데
const로 하고 있었음.

 


여기서부터 아까 못따라갔던것 찾아낸 것..
files로옴
2번 세이브하고싶음
그래서 반복문 돌려서 2번하는 것
// 127.0.0.1:3000/itemimage/insertbatch.json
// 토큰, 물품코드, 여러개 이미지
===================== 해당코드로 포스트맨에 보내면 files가 백엔드cmd에 표기
===================== 포스트맨은 사진참고(토큰필요)

router.post('/insertbatch.json',  
        upload.array("image"), auth.checkToken1,
        async function(req, res, next) {
    try {
        console.log(req);
        // 1. 전달받은 물품코드가 현재 로그인한 판매자의 것인지 확인
        const query  = { 
            _id     : Number(req.body.code),   
            seller  : req.body.SID
        };

        const obj = await Item.findOne(query);

        console.log(obj);
        // if(obj !== null) { //조회 후 결과가 있으면
        //     // req.files  => [  {}, {} ]   => 2
        //     let cnt = 0;
        //     for( let i=0;i<req.files.length; i++) {
        //         // 저장할 객체 생성
        //         let obj1        = new ItemImage();
        //         obj1.code       = Number(req.body.code);                    
        //         obj1.filedata   = req.files[i].buffer;                   
        //         obj1.filename   = req.files[i].originalname;                   
        //         obj1.filetype   = req.files[i].mimetype;                   
        //         obj1.filesize   = req.files[i].size;                   
        //         obj1.regdate    = moment().format('YYYY-MM-DD HH:mm:ss');                   

        //         // DB에 저장    
        //         let result = await obj1.save();
        //         if(result !== null) {
        //             cnt++;
        //         }
        //     }
            
        //     if( cnt === req.files.length ) {
        //         return res.send( {status : 200} ); 
        //     }
        // }
        return res.send( {status : 0} );
    }
    catch(e){
        console.error(e);
        return res.send({status:-1, result:e});
    }
});


===========================
위 코드로 백엔드에 2개의 사진을 업로드하여
output을 확인
files로 배열의 형태 [{}, {}]로 오고 있었음.
그래서 아래에 작성할때 req.files[i].~~~로 작성해서
반복문으로 1장씩 업로드

이미지 업로드
파일의 형태

필요한것만 쏙쏙 빼와서 obj1에 넣어줬다.

 

 

 


내일 시험이라 실습시간을 가졌음. 오늘 일정 끝.



시험
board.js이거 백엔드는 그대로 사용 + 그대로 제출
vue 프론트는 새로 만들기


질문사항=============================
for문으로
서버에 1장씩 업로드하는데
예를 들어 5개의 파일을 업로드 하다가
3번째 파일을 저장할때 인터넷이 끊긴다 치면
2번쨰 파일까진 저장이 되었고 
리턴 status는 0이나 -1이 뜰건데
그렇게 되면 2번쨰 파일까지는 for문 안에서 obj1.save했으니까
저장된거 아닌가요?
3,4,5번째 이미지 서버에 업로드가 안됬으면 이미지 등록이
실패로 끝나야하는데(0개 아니면 5개), 어떻게? 

status는 0이나 -1으로 뜨지만 결국 서버에는 2번째 사진까지만 업로드
된거 아닌가요?

===================답변
many를 써서 할려고 했는데 시퀀스가 안되서 이렇게 그냥 했다.
그대신
                if(result !== null) {
                    cnt++;
                }
   else{
   }

else를 추가하여 0또는 5로만 산출되게끔 만들어줄것.