자유게시판은 글과 사진을 자유롭게 올리며 의견을 주고 받는 기능을 한다.
게시글은 사진을 여러장 올리거나 안 올릴 수 있다.
app.js에 다음과 같은 코드를 작성한다.
app.use('/free_board', require('./routes/free_board'));
routes 폴더와 util 폴더에 각각 free_board.js라는 파일을 만들고 이전 user와 비슷한 형태로 코드를 작성하면 된다.
routes 폴더의 free_board.js 파일에는 다음과 같은 내용이 우선 작성되어야 한다.
const express = require('express');
const router = express.Router();
const models = require('../models');
const freeBoard = require('../util/free_board');
const image = require('../util/image');
const comment = require('../util/comment');
const recommend = require('../util/recommend');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function(req, file, callback){
directory = __dirname.replace(/routes/g, 'upload\\');
callback(null, directory);
},
filename: function(req, file, callback){
callback(null, new Date().toISOString().replace(/:/g, '-') + file.originalname);
}
});
var upload = multer({
storage: storage
});
module.exports = router;
필요한 유틸들을 받아오고, 이미지를 처리해야하므로 앞서 user.js에서 작성했던 것과 같이 multer를 작성해준다.
우선 모든 게시글을 보여주는 기능이다. routes 폴더에 작성된 내용이다.
router.post('/show_all', (req, res, next) => {
freeBoard.showAll((err, postData) => {
if(err){
return next(err);
}
var len = postData.length;
var i;
var temp;
for(i = 0; i < len; i++){
temp = JSON.parse(postData[i].contentImage);
postData[i].contentImage = temp;
}
return res.json({"result": postData});
})
})
showAll 함수를 이용해 해당 값을 모두 받아온 뒤, 전체 결과에 대해 contentImage를 parse하여 저장해준다. 이는 게시글을 저장할 때 여러장의 사진을 올리는 경우 사진의 번호를 리스트의 형태로 관리하기 때문이다.
다음은 util에 작성된 showAll 함수이다.
function showAll(callback){
db.free_boards.findAll({
where:{}
})
.then(result => {
if(result.length == 0){
return callback(errorWrapper(201));
}
return callback(null, result);
})
.catch(err => {
return callback(errorWrapper(0, err));
})
}
showAll 함수는 free_boards 데이터베이스를 sequelize를 이용하여 전체 값을 받아오는 함수이다. 만약 받아올 값이 없는 경우 에러를 반환하며 이외에는 결과를 반환한다. errorWrapper는 추후에 따로 정리하도록 하겠다.
다음은 유저 본인이 올린 게시글을 확인하는 기능이다. routes 폴더에 작성된 내용이다.
router.post('/show_all_user_board', (req, res, next) => {
freeBoard.showAllUserBoard(req.body.id, (err, postData) => {
if(err){
return next(err);
}
var len = postData.length;
var i;
var temp;
for(i = 0; i < len; i++){
temp = JSON.parse(postData[i].contentImage);
postData[i].contentImage = temp;
}
return res.json({"result": postData});
})
});
직전에 설명한 것과 매우 흡사한 형태로 showAllUserBoard 함수를 이용한다는 점 외엔 동일하다.
다음은 showAllUserBoard 함수이다.
function showAllUserBoard(id, callback){
db.free_boards.findAll({
where:{
writer: id
}
})
.then(result => {
if(result.length == 0){
return callback(errorWrapper(201));
}
return callback(null, result);
})
.catch(err => {
return callback(errorWrapper(0, err));
})
}
해당 함수는 id를 파라미터로 받아온다. 해당 값을 sequelize를 활용하여 writer가 id와 일치하는 free_boards의 모든 값을 받아온다. 이후의 과정은 showAll 함수와 동일하다.
다음은 특정 게시글을 보여주는 기능이다. routes 폴더에 작성된 내용이다.
router.post('/show_one', (req, res, next) => {
freeBoard.showOneBoard(req, (err, postData) => {
if(err){
return next(err);
}
var imageData = JSON.parse(postData.contentImage);
postData.contentImage = imageData;
comment.showComment(postData.postNum, 1, (err, commentData) => {
if(err){
return next(err);
}
return res.json({"board": postData, "comment": commentData});
})
})
});
showOneBoard함수를 이용하여 값을 받아온 뒤 contentImage 값을 parse하여 넣는다. 이 기능에서는 댓글도 보여줘야 하므로 comment의 showComment함수를 이용하여 해당 값을 받아온 뒤 이전에 갖고 있던 게시글의 데이터와 함께 반환해준다.
다음은 showOneBoard 함수이다.
function showOneBoard(req, callback){
db.free_boards.findOne({
where:{
postNum: req.body.postNum
}
})
.then(result => {
if(result.length == 0){
return callback(errorWrapper(201));
}
return callback(null, result.dataValues);
})
.catch(err => {
return callback(errorWrapper(201));
})
}
req를 파라미터로 받아오는 함수이다. req의 body.postNum이 free_boards 데이터베이스의 postNum과 일치하는 값을 찾는다. 이후 해당 값을 반환해준다.
다음은 showComment 함수이다. 해당 함수는 util 폴더에 comment.js 파일을 생성하여 작성한다.
function showComment(postNum, postType, callback){
db.comments.findAndCountAll({
where:{
postType: postType,
postNum: postNum
}
})
.then(result => {
return callback(null, result);
})
.catch(err => {
return callback(errorWrapper(0, err));
})
}
postNum과 postType을 파라미터로 받는다. 여기서 postType이란 자유게시판인지 투표게시판인지 판단하는 변수로 자유게시판은 1, 투표게시판은 2로 전달하여 준다.
sequelize의 findAndCountAll 함수를 이용하여 postType과 postNum이 일치하는 댓글을 찾아 해당 결과들의 갯수를 함께 반환해준다.
다음은 게시글을 작성하는 기능이다.
router.post('/create', upload.array('imgFile'), async (req, res, next) => {
if(!req.files){
models.free_boards.create({
title: req.body.title,
contentText: req.body.contentText,
writer: req.body.id,
views: 0,
recommend: 0
})
.then(result => {
return res.json({"result": true});
})
.catch(err => {
return next(err);
})
}
else{
var len = req.files.length;
var i = 0;
var list = [];
for (; i < len; i++){
var temp = await image.promiseSaveImage(req, i);
list.push(temp.imageNum);
}
list = JSON.stringify(list);
models.free_boards.create({
title: req.body.title,
contentText: req.body.contentText,
contentImage: list,
writer: req.body.id,
views: 0,
recommend: 0
})
.then(result => {
return res.json({"result": true});
})
.catch(err => {
return next(err);
})
}
});
다소 길게 작성되었다. 이는 create를 따로 함수로 두지 않아서 발생한 문제이다. 추후 다른 프로젝트에서는 고려하여 작성하도록 하겠다. 이미지 파일을 업로드 하기때문에 upload를 이용하였는데, 이때 여러 장의 이미지를 올릴 수 있어야 하므로 upload.array로 작성하였다. 만약 req.files가 존재하지 않는 경우 즉, 이미지를 첨부하지 않고 글을 작성한 경우 제목, 글 내용, 작성자만을 데이터베이스에 저장한다.
이미지를 첨부하여 작성한 경우 해당 이미지의 수만큼 데이터베이스에 이미지 정보를 저장해준 뒤, contentImage 요소에 list를 저장하는 부분을 추가하여 작성하였다.
promiseSaveImage 함수는 이미지 데이터를 저장해주는 함수로 코드는 이러하다.
function promiseSaveImage(req, num){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.images.create({
user: req.body.id,
image: req.files[num].path
})
.then(result => {
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(602));
})
}, 100);
});
}
promise를 이용한 함수로 해당 코드는 util/image.js를 생성하여 작성하였다.
images 데이터베이스에 사용자의 아이디와 이미지가 저장된 경로를 저장해준다.
다음은 게시글을 수정하는 기능이다.
router.post('/edit_text', async (req, res, next) => {
try{
var post = await freeBoard.showOnePromise(req);
if(post.writer != req.body.id){
err = new Error();
err.type = 107;
err.message = "ID doesn't match"
throw err;
}
var edit = await freeBoard.editText(req);
return res.json({"result" : true});
} catch(err) {
return next(err);
}
});
해당 기능은 showOnePromise를 이용해 수정하고자하는 게시글의 정보를 받아온 뒤, 게시글의 작성자가 현재 사용자와 일치할 경우 게시글 본문의 내용을 수정할 수 있게 해주는 기능을 한다.
게시글의 수정은 editText 함수를 이용하여 진행한다.
다음은 showOnePromise 함수이다.
function showOnePromise(req){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.free_boards.findOne({
where:{
postNum: req.body.postNum
}
})
.then(result => {
if(result.length == 0){
reject(errorWrapper(201));
}
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(201));
})
}, 100);
});
}
프로미스를 이용한 함수로 sequelize의 findOne을 이용하여 현재 게시글의 번호와 일치하는 데이터를 찾아 반환해준다.
다음은 editText 함수이다.
function editText(req){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.free_boards.update(
{
contentText: req.body.contentText
},
{
where: {
postNum: req.body.postNum
}
})
.then(result => {
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(203));
})
}, 100);
});
}
sequelize의 update를 이용하여 새로 작성된 본문 내용인 contentText를 postNum이 일치하는 값을 찾아 수정해준다.
자유게시판의 마지막 기능인 글 삭제 기능이다. 코드는 이러하다.
router.post('/delete', async (req, res, next) => {
try{
var post = await freeBoard.showOnePromise(req);
if(post.writer != req.body.id){
err = new Error();
err.type = 107;
err.message = "ID doesn't match"
throw err;
}
var commentResult = await comment.deletePostComment(req, 1);
var recommendResult = await recommend.deleteFreeRecommend(req);
if (!post.contentImage){
var result = await freeBoard.deleteBoard(req);
}
else{
var imageList = JSON.parse(post.contentImage);
var len = imageList.length;
var i;
var imageResult;
var imageLink;
for(i = 0; i < len; i++){
imageLink = await image.findImage(imageList[i]);
imageResult = await image.deleteImage(imageLink.image);
}
var result = await freeBoard.deleteBoard(req);
}
return res.json({"result" : true});
} catch(err) {
return next(err);
}
});
처리해야할 사항들이 다소 많다. 우선 showOnePromise를 이용해 해당 글의 작성자가 현재 사용자와 일치하는지 확인한 후, deletePostComment를 이용해 댓글을 삭제하고, deleteFreeRecommend를 이용해 추천을 삭제한다. 만약 이미지를 첨부하지 않은 게시글일 경우 바로 삭제를 진행하고, 이미지를 첨부했을 경우 해당 이미지들을 삭제한 후 글을 최종적으로 삭제한다. 이미지의 삭제는 findImage 함수를 이용해 이미지 경로를 찾은 뒤 deleteImage 함수를 이용해 해당 이미지를 삭제하는 방식으로 진행된다.
deletePostComment 함수는 다음과 같다.
function deletePostComment(req, postType){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.comments.destroy({
where: {
postType: postType,
postNum: req.body.postNum
}
})
.then(result => {
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(402));
})
}, 100);
});
}
해당 함수는 파라미터로 req와 postType을 받는다. postType은 이전에도 설명하였듯 게시글의 종류를 의미한다. 현재 진행하는 것은 자유게시판이므로 1을 전달받는다. 이후 sequelize의 destroy를 이용하여 postType과 postNum이 일치하는 데이터를 찾아 삭제하게 된다.
다음은 deleteFreeRecommend 함수이다.
function deleteFreeRecommend(req){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.recommends.destroy({
where: {
postType: 1,
postNum: req.body.postNum
}
})
.then(result => {
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(504));
})
}, 100);
});
}
해당 함수는 자유게시판의 추천 데이터를 지우기 위한 함수로 postType이 1이고 postNum이 전달받은 파라미터의 값과 일치하는 데이터를 찾아 삭제하게 된다.
다음은 findImage 함수이다.
function findImage(num){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.images.findOne({
where: {
imageNum: num
}
})
.then(result => {
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(603));
})
}, 100);
});
}
sequelize의 findOne을 이용하여 imageNum이 전달받은 파라미터 num과 일치하는 값을 찾아 반환해준다.
다음은 deleteImage 함수이다.
function deleteImage(link){
return new Promise((resolve, reject) => {
setTimeout(() => {
fs.unlink(link, (err) => {
if(err){
reject(errorWrapper(602));
}
link.replace(/\\/g, '\\');
db.images.destroy({
where: {
image: link
}
})
.then(result => {
resolve(null, result.dataValues);
})
.catch(err => {
reject(errorWrapper(602));
})
})
}, 100);
});
}
fs.unlink 를 이용하여 파라미터로 받아온 링크를 이용하여 파일을 삭제하고, sequelize의 destroy를 이용하여 해당 데이터베이스의 정보도 삭제한다.
다음은 deleteBoard 함수이다.
function deleteBoard(req){
return new Promise((resolve, reject) => {
setTimeout(() => {
db.free_boards.destroy({
where: {
postNum: req.body.postNum
}
})
.then(result => {
resolve(result.dataValues);
})
.catch(err => {
reject(errorWrapper(202));
})
}, 100);
});
}
sequelize의 destroy를 이용하여 postNum이 일치하는 값을 찾아 데이터베이스에서 삭제한다.
지금까지 자유게시판의 기능을 살펴보았다. 가장 어려웠던 점은 사진을 여러장 저장하는 방법이었다. 데이터베이스의 요소를 계속해서 늘리기보단 다른 방법이 있을 것이라 생각하였고, 이는 리스트를 만들어 스트링화 한 뒤 필요한 경우 json의 형태로 parse하여 해결하였다.
다음 글에선 투표게시판의 기능을 살펴보도록 하겠다.
'학교수업 이야기 > 캡스톤 디자인(3-2)' 카테고리의 다른 글
user 기능(2) (0) | 2021.01.06 |
---|---|
user 기능(1) (0) | 2020.12.30 |
디비 구상(2) (0) | 2020.12.29 |
디비 구상(1) (0) | 2020.12.29 |
서버 개발 시작 (0) | 2020.12.22 |