node 2022/02/03

무간·2022년 2월 3일

NosqlBooster에서

시퀀스 추가

db.sequence.insert({
_id :'SEQ_BOARDREPLY1_NO',
seq : 1,
});

파일명 routes/board.js

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

// CMD> npm i mongodb --save
// config/mongodb.js 에서 DB내용 불러와서 연결
const db = require('mongodb').MongoClient;
const dburl = require('../config/mongodb').URL;
const dbname = require('../config/mongodb').DB;

// 이미지 파일 전송
//CMD> npm i multer --save
const multer = require('multer');

// 특정 폴더에 파일로 
// 메모리 DB에 추가
const upload = multer({storage:multer.memoryStorage()});

// POST : insert
// PUT : update
// DELETE : delete
// GET : select

// localhost:3000/board/insert //글쓰기
// title, content, writer, image
// _id, regdate
router.post('/insert', upload.single("image"), async function(req, res, next) { 
    //req로 데이터가 들어옴 res로 처리 결과가 나감
    try{
        // 1. DB접속
        const dbconn = await db.connect(dburl);
        // 2. DB선택 및 컬렉션 선택
        const collection = dbconn.db(dbname).collection('sequence');
        // 3. 시퀀스에서 값을 가져오고, 그 다음을 위해서 가지고 올때 값 증가
        const result = await collection.findOneAndUpdate(
            { _id  : 'SEQ_BOARD1_NO' }, // 가저오기 위한 조건
            { $inc : {seq : 1} }      // seq갑을 1 증가시킴
        );
        console.log('========================');
        // 4. 정상동작 유무를 위한 결과 확인
        console.log(result.value.seq);
        console.log('========================');

        const obj = {
            _id      : result.value.seq,
            title    : req.body.title,
            content  : req.body.content,
            writer   : req.body.writer,
            hit      : 1,
            filename : req.file.originalname,
            filedata : req.file.buffer,
            filetype : req.file.mimetype,
            filesize : req.file.size,
            regdate  : new Date()
        };

        // 추가할 컬렉션 선택
        const collection1 = dbconn.db(dbname).collection('board1');
        // 추가하기
        const result1     = await collection1.insertOne(obj);
        // 결과 확인
        if(result1.insertedId === result.value.seq) {
            return res.send({status : 200});
        }
        // console.log(result1);
        // console.log(req.body);
        // console.log(req.file);
        return res.send({status : 0});
    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});
    }  
});

// localhost:3000/board/image?_id=108 //이미지
// 출력하고자 하는 이미지의 게시물 번호 전달
router.get('/image', async function(req,res,next){
    try{
        const no = Number(req.query['_id']);
        // const no = req.query._id

        // DB연결
        const dbconn = await db.connect(dburl);
        // DB선택 및 컬렉션 선택
        const collection = dbconn.db(dbname).collection('board1');
        
        // 이미지 정보 가져오기
        const result = await collection.findOne(
            { _id : no }, //조건
            { projection : {filedata:1, filetype:1} }, // 필요한 항목만 projection
        );
        // console.log(result);
        // application/json =>  image/png
        res.contentType(result.filetype);
        return res.send(result.filedata.buffer);

    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});
    }
});

// localhost:3000/board/select?page=1&text=검색어 // 목록
// 페이지 정렬 
router.get('/select', async function(req,res,next){
    try{
        const page = Number(req.query.page); // 페이지번호
        const text = req.query.text; // 검색어

        // DB연결, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('board1');

        // find(조건).sort(정렬).toArray()로 사용
        // abc => a, b, c 로 검색 가능 
        const result = await collection.find(
            { title : new RegExp(text,'i') }, //조건  i= 대소문자 무시
            { projection : { _id:1 , title:1, writer:1, hit:1, regdate:1 } }
        )
        .sort({ _id : -1 }) //-1 내림차순 +1 오름차순
        .skip( (page-1)*10 ) //skip(페이지 넘기는 수)
        .limit(10) //limit(10) 10개씩 정렬
        .toArray(); 
        // 오라클(o), mysql SQL문 => SELECT * FROM ORDER BY _ID DESC ...

        //결과 확인
        console.log(result);

        // 검색어가 포함된 전체 게시물 개수 => 페이지네이션 번호 생성시 필요
        const result1 = await collection.countDocuments(
            { title : new RegExp(text, 'i') },
        );
        return res.send({ status : 200, rows : result, total : result1 });        
    }
    catch(e){
        console.error(e);
        res.send({status : -1, message:e});
    }
});

// localhost:3000/board/selectone?no=134
router.get('/selectone', async function(req,res,next){
    try{
        //1. 전송되는 값 받기(형변환에 주의 (타입) )
        const no = Number(req.query.no);

        // 2. DB연결, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('board1');

        // 3. DB에서 원하는 값 가져오기 ( findone(1개)or find(n개) )
        const result = await collection.findOne(
            { _id : no }, //조건
            { projection : { filedata:0, filename:0, filesize:0, filetype:0 } }, // 필요한 컬럼 
        );
        // 4. 가져온 정보에서 이미지 정보를 추가함
        // 이미지  URL, 이전 글번호, 다음 글번호
        result['imageurl'] ='/board/image?_id=' + no;

        // 131 이전글
        // 134 <== 현재요청되는 글번호 위치
        // 136 다음글
        
        // { _id :{ $lt  : 134} } // 134 미만
        // { _id :{ $lte : 134} } // 134 이하
        // { _id :{ $gt  : 134} } // 134 촉과
        // { _id :{ $gte : 134} } // 134 이상
        const prev = await collection.find(
            { _id :{ $lt  : no } },// 조건  
            { projection : { _id : 1 } } //필요한 컬럼만 선택
        ).sort( { _id : -1 } ).limit(1).toArray(); // 정렬은 sort(조건 1:오름차순 -1 내림차순)

        console.log(prev);   // [ { _id: 133 } ] or []
        console.log(result); // 개발자 확인 용도

        if (prev.length > 0){ // 이전글이 있다면
            result['prev'] = prev[0]._id
        }
        else{ // 이전글이 없다면
            result['prev'] = 0;
        }

        // 같은것    : find( { _id : 134 } ) find( { _id :{$eq : 134} } )
        // 같지않음  : find( { _id :{$ne : 134} } )
        // 포함      : find( { _id :{$in : [134, 135,136]} } )

        //조건 2개 일치 and
        // find ( { _id : 134, hit : 34})  
        // find ( { $and [ {_id : 134}, {hit : 34 } ] } )

        // 조건 2개중 1개만 or
        // find ({$or : [ { _id:134 }, { hit:34 } ] } )

        // 'next'
        const next = await collection.find(
            { _id :{ $gt  : no } },// 조건  
            { projection : { _id : 1 } } //필요한 컬럼만 선택
        ).sort( { _id : 1 } ).limit(1).toArray();

        console.log(next);
        console.log(result);

        if (next.length === 1 ){
            result['next'] = next[0]._id
        }
        else{
            result['next'] = 0;
        }

        res.send({status : 200, result:result });// 프론트로 전달

    }
    catch(e){
        console.error(e); // 개발자가 확인하는 용도
        res.send({status : -1, message:e}); // 프론트로 전달함
    }

});

// 조회수 1씩 증가
// localhost:3000/board/updatehit?no=134
router.put('/updatehit', async function(req,res,next){
    try{
        // 1. 정달되는 값 받기
        const no = Number(req.query.no);

        // 2. DB연결, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('board1');

        // 3. 조회수 증가
        const result = await collection.updateOne(
            { _id  : no }, // 조건
            { $inc : { hit : 10 } }, // 실제 수행할 내용 ex) hit 를 10 씩 증가시킴
        );

        // 4. DB 수행 후 반환되는 결과 값에 따라 적절한 값을 전달
        if(result.modifiedCount === 1 ){
            return res.send({status : 200}); //프론트로 전달함
        }
        return res.send({status : 0});

        
        

    }
    catch(e){
        console.error(e); // 개발자가 확인하는 용도
        res.send({status : -1, message:e}); // 프론트로 전달함
    }

});

// 글 삭제
// localhost:3000/board/delete?no=134
router.delete('/delete', async function(req,res,next){
    try{
        // 1. 정달되는 값 받기
        const no = Number(req.query.no);

        // 2. DB연결, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('board1');

        //3. 삭제 수행
        const result = await collection.deleteOne(
            { _id : no }
        );

        // 4. 결과 반환
        if(result.deletedCount === 1 ){
            return res.send({status : 200});
        }
        return res.send({status : 0});
    }
    catch(e){
        console.error(e); // 개발자가 확인하는 용도
        res.send({status : -1, message:e}); // 프론트로 전달함
    }

});

// 글 수정 : 글번호(조건), 제목, 내용, 작성자
// localhost:3000/board/update?no=134
router.put('/update', async function(req,res,next){
    try{
        // 1. 정달되는 값 받기
        const no = Number(req.query.no);    //query
        const title = req.body.title;       //body
        const content = req.body.content;   //body
        const writer = req.body.writer;     //body

        // 2. DB연결, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('board1');

        //3. 변경 수행
        const result = await collection.updateOne(
            { _id : no },
            { $set : { title:title, content:content, writer:writer } }
        );
        console.log(result);

        // 4. 결과 반환
        if(result.modifiedCount === 1 ){
            return res.send({status : 200});
        }
        return res.send({status : 0});
    }
    catch(e){
        console.error(e); // 개발자가 확인하는 용도
        res.send({status : -1, message:e}); // 프론트로 전달함
    }

});

// 답글쓰기
// 기본키 : 답글번호(자동)   - 줄별 데이터를 구분하는 고유한 값
// 내용, 작성자,            - 데이터 
// 외래키 : 원본글번호       -  다른곳(board1의 글번호)의 데이터로만 구성해야 됨!! 
// 등록일(자동))            - 데이터

// localhost:3000/board/insertreply
router.post('/insertreply', async function(req,res,next){
    try{       
        // 1. DB접속, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('sequence');
        // 2. 시퀀스에서 값을 가져오고, 그 다음을 위해서 가지고 올때 값 증가
        const result = await collection.findOneAndUpdate(
            { _id  : 'SEQ_BOARDREPLY1_NO' }, // 가저오기 위한 조건
            { $inc : {seq : 1} }      // seq갑을 1 증가시킴
        );

        const obj = {
            _id     : result.value.seq, // 기본키 - 답글번호
            content : req.body.content,      // 답글 내용
            writer  : req.body.writer,        // 답글 작성자
            boardno : Number(req.body.boardno),      // 외래키 - 원본글번호
            regdate : new Date()    // 답글 작성일자
        }

        const collection1 = dbconn.db(dbname).collection('boardreply1');
        const result1 = await collection1.insertOne(obj);

        // 결과확인
        if(result1.insertedId === result.value.seq){
            return res.send({status:200})
        }
        return res.send({status:0})      
        
    }
    catch(e){
        console.error(e); // 개발자가 확인하는 용도
        res.send({status : -1, message:e}); // 프론트로 전달함
    }

});

// 답글조회 
// localhost:3000/board/selectreply?no=134
router.get('/selectreply', async function(req,res,next){
    try{
        //1. 전송되는 값 받기(형변환에 주의 (타입) )
        const no = Number(req.query.no);

        // 2. DB연결, DB선택 및 컬렉션 선택
        const dbconn = await db.connect(dburl);        
        const collection = dbconn.db(dbname).collection('boardreply1');

        // 3. DB에서 원하는 값 가져오기 ( findone(1개)or find(n개) )
        const result = await collection.find(
            { boardno : no } //조건            
        ).toArray();

        return res.send({status : 200, result: result});

    }
    catch(e){
        console.error(e); // 개발자가 확인하는 용도
        res.send({status : -1, message:e}); // 프론트로 전달함
    }
});

module.exports = router;

====================================================

조회 : await axios.get(url, {headers:headers});
추가 : await axios.post(url, body, {headers:headers});
수정 : await axios.put(url, body, {headers:headers});
삭제 : await axios.delete(url, {headers:headers, data:{}});

====================================================

파일명 src/components/boardcontent.vue

<template>
    <div>
        <h3>src/components/boardcontent.vue</h3>
        
        <div v-if="state.item">
            제목   : {{state.item.title}} <br />
            내용   : {{state.item.content}} <br />
            작성자 : {{state.item.writer}} <br />
            조회수 : {{state.item.hit}} <br />
            이미지 : <img :src="state.item.imageurl" style="width:100px; height:100px"/>
            <hr />

            <router-link to="/board"><button>목록으로</button></router-link>
            <button @click="handleDelete">삭제</button>
            <button>수정</button>
            <button v-if="state.item.prev > 0" @click="handleData(1)">이전글</button>
            <button v-if="state.item.next > 0" @click="handleData(2)">다음글</button>

            <hr />            

            <table border="1">            
                <thead>
                    <tr>
                        <th>no</th>
                        <th>writer</th>                    
                        <th>content</th>
                        <th>date</th>
                    </tr>                
                </thead>
                <tbody>
                    <tr  v-for="item in state.reply" :key="item">
                        <td> {{ item._id}} </td>
                        <td> {{ item.writer }} </td>                    
                        <td> {{ item.content }} </td>
                        <td> {{ item.regdate }} </td>
                    </tr>
                </tbody>            
            </table>

            <hr />            
            <textarea rows="6" placeholder="댓글내용"></textarea>  <br />
            <input type="text" placeholder="댓글작성자" />
            <button>댓글저장</button>


        </div>
    </div>
</template>

<script>
import axios from 'axios';  //벡엔드연동
import { reactive, onMounted } from 'vue'; //state변수, 
import { useRoute } from 'vue-router'; // 페이지 이동후route.query
import { useRouter } from 'vue-router'; // 페이지 이동시킴

export default {
    setup () {
        const route = useRoute();
        const router = useRouter();

        // state변수 생성
        const state = reactive({
            no : route.query.no   //?no =113
        });

        // 삭제하기 메소드 생성
        const handleDelete = async() => {
            if(confirm('삭제하시겠습니까?')) {
                const url = `/board/delete?no=${state.no}`;
                const headers = {"Content-Type":"application/json"};
                const response = await axios.delete(url, {headers:headers, data:{}});
                console.log(response.data);
                if(response.data.status === 200){
                    router.push({ name:"Board", query:{page:1,text:''} })
                }
            }
        }

        const handleMount = async(no) => {                        
            const url = `/board/selectone?no=${no}`;
            const headers = {"Content-Type":"application/json"};

            const response = await axios.get(url, {headers});
            console.log(response.data);
            if(response.data.status === 200){
                console.log(response.data.result);
                state.item = response.data.result;
            }
        };

        const handleReplyMound = async(no) => {
            const url = `/board/selectreply?no=${no}`;
            const headers = {"Content-Type":"application/json"}; 

            const response = await axios.get(url,{headers});
            if(response.data.status === 200 ){
                state.reply = response.data.result;
            }
        }

        onMounted(async() => {
            await handleMount(state.no);
            await handleReplyMound(state.no);
        });

        const handleData = async(idx) =>{
            if(idx === 1) { // 이전글
            router.push({name:"BoardContent",query:{no:state.item.prev}})
                state.no = state.item.prev;
                await handleMount(state.no);                
            }
            else if(idx ===2 ){
                router.push({name:"BoardContent",query:{no:state.item.next}})
                state.no = state.item.next;
                await handleMount(state.no);
            }
        }



        return {state, handleDelete, handleData, handleReplyMound}
    },

}
</script>

<style lang="scss" scoped>

</style>
profile
당신을 한 줄로 소개해보세요

0개의 댓글