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>