node 2022/01/26

무간·2022년 1월 26일

-- DB접속하기
서버주소 : 1.234.5.158, 포트번호 : 37017, DB명 : db200
아이디: id219 , 암호: pw219

==================================================
// -- 벡엔드 만들기
CMD> express -e exp_20220126

CMD> cd exp_20220126

CMD> npm install => node_modules

CMD> nodemon --inspect ./bin/www => 실행 (소스코드 변경 적용됨)

크롬에서 localhost:3000 로 확인

==================================================
// 참고 : https://github.com/expressjs/multer
// -- 파일첨부 모듈 설치
CMD> npm install multer --save

// 참고 : https://github.com/mongodb/node-mongodb-native
CMD> npm i mongodb --save

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

// 파일명 : config/mongodb.js
module.exports = {
URL : 'mongodb://id200:pw200@1.234.5.158:37017/db200',
ID : 'id200',
PW : 'pw200',
DB : 'db200',
};

==================================================
-- 한개 추가
db.sequence.insert(
{ _id : 'SEQ_BOARD1_NO', seq : 100 }
);

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

파일명 app.js

라우터 연결

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var boardRouter = require('./routes/board');
var memberRouter = require('./routes/member');
var itemRouter = require('./routes/item');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/index', indexRouter);
app.use('/users', usersRouter);
app.use('/board', boardRouter);
app.use('/member', memberRouter);
app.use('/item', itemRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

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

파일명 config/mongodb.js

필요한 정보 저장(다른 파일에서 DB에 관한 정보 전달)

module.exports ={
    URL : 'mongodb://id219:pw219@1.234.5.158:37017/db219',
    ID  : 'id219',
    PW  : 'pw219',
    DB  : 'db219',
}

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

파일명 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});
    }
});

module.exports = router;


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

파일명 routes/item.js

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

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

// 이미지 파일 전송
// 메모리 DB에 추가
const multer = require('multer');
const upload = multer({storage:multer.memoryStorage()});

// 컬렉션은 itme1에 항목을 추가하는것
// localhost:3000/item/insert
// 전송되는 값 : name,content,price,quantity,image
// 자동으로 생성 : _id,regdate
router.post('/insert', upload.single("image"),async function(req, res, next) {
    try{
        // DB접속
        const dbconn = await db.connect(dburl);
        // 2. DB선택 및 컬렉션 선택
        const collection = dbconn.db(dbname).collection('sequence');
        // 3. 시퀀스에서 값을 가져오고, 그 다음을 위해서 가지고 올때 값 증가
        const result = await collection.findOneAndUpdate(
            { _id  : 'SEQ_ITEM1_NO' },
            { $inc : {seq : 1} }      
        );
        // console.log('========================');        
        // console.log(result.value.seq);
        // console.log('========================');
        const obj = {
            _id      : result.value.seq,
            name     : req.body.name,
            content  : req.body.content,
            price    : Number(req.body.price),
            quantity : Number(req.body.quantity),
            filename : req.file.originalname,
            filedata : req.file.buffer,
            filetype : req.file.mimetype,
            filesize : req.file.size,
            regdate  : new Date()    
        };
        // DB선택.컬렉션 생성 및 선택
        const collection1 = dbconn.db(dbname).collection('item1');
        // 선택된 컬렉션에 데이터 추가
        const result1 = await collection1.insertOne(obj);
        if(result1.insertedId === result.value.seq) {
            return res.send({status : 200});
        }
        // console.log('========================');
        // console.log(result1);
        // console.log('========================');
        // console.log(req.body);
        // console.log('========================');
        // console.log(req.file);
        // console.log('========================');

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

module.exports = router;

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

파일명 : vue.config.js => 변경시 서버 재구동

module.exports = {
    devServer : {
        // 벡엔드의 주소를 짧게 사용하기 위해서
        // http://localhost:3000/board/select => board/select
        proxy : {
            '/board' :{
                target       : 'http://localhost:3000',
                changeOrigin : true,
                logLevel     : 'debug'
            }
        },
        port : 8080
    },
    
}

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

파일명 src/component/Board.vue

<template>
    <div>
        <h3>src/component/Board.vue</h3>

        <router-link to="/boardwrite">글쓰기</router-link>

        <table border="1">            
            <thead>
                <tr>
                    <th>no</th>
                    <th>title</th>
                    <th>writer</th>
                    <th>hit</th>
                    <th>date</th>
                </tr>                
            </thead>
            <tbody>
                <tr  v-for="tmp in state.items.result" :key="tmp">
                    <td @click="handleBoardContent(tmp._id)">{{tmp._id}}</td>
                    <td>{{ tmp.title }}</td>
                    <td>{{ tmp.writer }}</td>
                    <td>{{ tmp.hit }}</td>
                    <td>{{ tmp.regdate }}</td>
                </tr>
            </tbody>            
        </table>

    </div>
</template>

<script>
import { onMounted, reactive } from 'vue';
import { useRouter } from 'vue-router';
import axios from 'axios'

export default {
    setup () {
        const router = useRouter();
        
        const state = reactive({
            items : {},
            page  : 1,
            text  : '',
        });

        // 생명주기 onMounted()
        onMounted( async() => {
            const url = `/board/select?page=${state.page}&text=${state.text}`
            const headers = {"Content-Type":"application/sjon"};
            const response = await axios.get(url,{headers});
            console.log(response.data);

            if(response.data.status === 200 ){
                state.items.result = response.data.rows;
            }
        });

        // function handleBoardContent(no) { }
        const handleBoardContent = (no) =>{
            console.log(no);
            // 127.0.0.1:3000/boardcontent?no=2
            router.push({name:"BoardContent", query:{no:no}});

        }



        return {state, handleBoardContent}
                
    },
    

}
</script>

<style lang="scss" scoped>

</style>

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

파일명 src/component/BoardWrite.vue

<template>
    <div>
        <h3>src/component/BoardWrite.vue</h3>
        {{state}}<br />
        제목 : <input type="text" v-model="state.title"/><br />

        내용 : 
            <div style="width:600px">
                <ckeditor :editor="editor.editor" :config="editor.editorConfig" v-model="state.content"></ckeditor>
            </div>
            
        작성자 : <input type="text" v-model="state.writer" /><br />
        이미지 : <img :src="state.imgurl" style="width:50px;height:50px"/>
        <input type="file" @change="handleImage"/><br />
        <button @click="handleWrite">글쓰기</button>

    </div>
</template>

<script>
import { reactive } from 'vue';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {    
    setup () {
        const router = useRouter();
        //object
        const editor ={
            editor : ClassicEditor,
            editorData : <p>테스트</p>,
            editorConfig : {}
        }
        const state = reactive({
            title   : '',
            content : '',
            writer  : '',
            imgdata : '',
            imgurl  : require('../assets/default.jpg'),
        });

        const handleImage = (e) =>{
            console.log(e);
            if(e.target.files[0]){// 첨부
                state.imgdata = e.target.files[0];
                // 크롬 내부에 임시로 이미지를 표시하기 위한 URL생성
                state.imgurl = URL.createObjectURL(e.target.files[0]);

            }
            else{// 취소
                state.imfdata = '';
                state.imgurl = require('../assets/default.jpg');
            }

        };

        const handleWrite = async() =>{
            if(state.title === ''){
                alert('제목을 입력하세요.');
                return false;
            }
            if(state.content === ''){
                alert('내용을 입력하세요.');
                return false;
            }
            if(state.writer === ''){
                alert('작성자를 입력하세요.');
                return false;
            }
            if(state.imgdata === ''){
                alert('사진을 입력하세요.');
                return false;
            }            

            // 유효성 검사 후 백엔드 호출
            const url = `/board/insert`;
            // 이미지 파일이 있어서 application이 아닌 multipart/form-data사용
            const headers = {"Content-Type":"multipart/form-data"}; 
            const body = new FormData(); // 이미지가 있는 경우
            body.append("title", state.title);
            body.append("content", state.content);
            body.append("writer", state.writer);
            body.append("image", state.imgdata);
            
            const response = await axios.post(url,body,{headers});
            console.log(response.data);

            if(response.data.status === 200){
                alert('작성이 완료 되었습니다.');
                router.push({name:"Board"});
            }
            



        };
        return {state, handleImage, handleWrite, editor}
    }
}
</script>

<style lang="css">
    .ck-editor__editable {
        min-height: 500px;
    }
</style>

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

파일명 src/components/boardcontent.vue

<template>
    <div>
        <h3>src/components/boardcontent.vue</h3>
        {{state}}
        <img :src="`/board/image?_id=${state.no}`" style="width:100px; height:100px"/>

    </div>
</template>

<script>
import { reactive } from 'vue';
import { onMounted } from 'vue';
import { useRoute } from 'vue-router';// query 받기

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

        const state = reactive({
            no : route.query.no
        });

        onMounted( async() => {
            //const url = `/board/selectone`;
        });
        

        return {state}
    }
}
</script>

<style lang="scss" scoped>

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

0개의 댓글