
1.short-id.js
const { nanoid } = require('nanoid');
const shortId = {
type: String,
default: () => {
return nanoid()
},
require: true,
index: true,
}
module.exports = shortId;
2.post.js
const { Schema } = require('mongoose');
const shortId=require('./types/short-id.js')
const PostSchema = new Schema({
// shortId 추가
shortId,
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
author: {
type: String,
default: '작성자',
},
}, {
timestamps: true,
});
module.exports = PostSchema;
3.app.js
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const mongoose = require('mongoose');
const dayjs = require('dayjs');
const indexRouter = require('./routes');
const postsRouter = require('./routes/posts');
mongoose.connect('mongodb://localhost:27017/simple-board');
mongoose.connection.on('connected', () => {
console.log('MongoDB Connected');
});
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.locals.formatDate = (date) => {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss');
}
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('/', indexRouter);
app.use('/posts', postsRouter);
// catch 404 and forward to error handler
app.use((req, res, next) => {
next(createError(404));
});
// error handler
app.use((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;
4.index.js
const { Router } = require('express');
const router = Router();
router.get('/', (req, res) => {
res.redirect('/posts')
});
module.exports = router;
5.posts.js
const { Router } = require('express');
const { Post } = require('../models');
const router = Router();
router.get('/', async (req, res, next) => {
if (req.query.write) {
res.render('post/edit');
return;
}
const posts =await Post.find({})
// 게시글 목록
res.render('post/list', { posts });
});
router.get('/:shortId', async (req, res, next) => {
const { shortId } = req.params;
const post = await Post.findOne({shortId})
// shortId 로 게시글 찾기
if (req.query.edit) {
res.render('post/edit', { post });
return;
}
res.render('post/view', { post });
});
router.post('/', async (req, res, next) => {
const { title, content } = req.body;
try {
if (!title || !content) {
throw new Error('제목과 내용을 입력해 주세요');
}
const post = await Post.create({title,content})
// 게시글 생성
res.redirect(`/posts/${post.shortId}`);
} catch (err) {
next(err);
}
});
router.post('/:shortId', async (req, res, next) => {
const { shortId } = req.params;
const { title, content } = req.body;
try {
if (!title || !content) {
throw new Error('제목과 내용을 입력해 주세요');
}
await Post.updateOne({shortId},{title,content})
// shortId 로 게시글 수정
res.redirect(`/posts/${shortId}`);
} catch (err) {
next(err);
}
});
router.delete('/:shortId', async (req, res, next) => {
const { shortId } = req.params;
await Post.deleteOne({shortId})
// shortId 로 게시글 삭제
res.send('OK');
});
module.exports = router;
6.list.pug
extends ../layout
block content
div
h1 전체 글 목록
table
tbody
each post in posts
tr
td: a(href="/posts/"+post.shortId)= post.title
td= post.author
td= formatDate(post.createdAt)
a(href="/posts?write=true"): button 등록하기
7.view.pug
extends ../layout
block content
h1= post.title
table
tbody
tr
th= post.author
th(colspan="2")= formatDate(post.createdAt)
tr
td(colspan="3")
pre= post.content
tr
td: a(href="/posts"): button 목록으로
td: a(href="?edit=true"): button 수정
td: button.delete(onclick='deletePost()') 삭제
script(type="text/javascript").
function deletePost() {
fetch('/posts/#{post.shortId}', { method: 'delete' })
.then((res) => {
if (res.ok) {
alert('삭제되었습니다.');
window.location.href = '/posts';
} else {
alert('오류가 발생했습니다.');
}
})
.catch((err) => {
alert('오류가 발생했습니다.');
});
}
8.edit.pug
extends ../layout
block content
h1= post ? "글 수정하기": "새 글 등록하기"
form(action=post ? "/posts/"+post.shortId : "/posts", method="post")
div
label 제목
input(type="text" name="title" value=post&&post.title)
div
label 내용
textarea(name="content")= post&&post.content
div: input(type="submit" value=post ? "수정":"등록")