[Node.js] 게시판 CRUD 만들기, 게시글 작성/상세/수정/삭제
글 작성자: 망고좋아
반응형
🎯 게시판 CRUD 만들기
📝 모델 선언하기
🛠 /models/schemas/post.js
const mongoose = require("mongoose");
const { Schema } = require("mongoose");
const shortId = require("./types/short-id");
module.exports = new Schema(
{
shortId,
title: String,
content: String,
author: String,
},
{
timestamps: true,
}
);
🛠 /models/index.js
exports.Post = mongoose.model("Post", PostSchema);
- MongoDB의 ObjectID는 URL에 사용하기 좋은 값이 아니기 때문에 대체할 수 있는 아이디를 shortId로 생성해준다.
- 제목, 내용, 작성자를 String 타입으로 스키마에 선언 (회원가입 로그인 후 작성자 연동으로 변경 예정)
- timestamps 옵션으로 작성 시간, 수정 시간을 자동으로 기록해 준다.
📝 shortId
const { nanoid } = require("nanoid");
const shortId = {
type: String,
default: () => { // shortId의 값이 전달되지 않았을 때 자동으로 함수를 실행해서 nanoid를 생성해서 shortId를 default으로 지정
return nanoid();
},
require: true,
index: true,
};
module.exports = shortId;
- ObjectId를 대체할 shortId 타입을 Mongoose Custom Type으로 선언해준다.
- 중복 없는 문자열을 생성해 주는 nanoid 패키지 활용
- default를 이용해 모델 생성 시 자동으로 ObjectId를 대체할 아이디 생성해준다.
📝 게시글 작성
📕 게시글 작성 흐름
/posts?write=true
로 작성 페이지 접근<form action="/posts" method="post">
이용해 post 요청 전송router.post
이용하여 post 요청 처리res.redirect
이용하여 post 완료 처리
📕 작성 페이지 만들기
🛠 /routes/posts.js
const { Router } = require("express");
const router = Router();
router.get("/", (req, res, next) => {
if (req.query.write) {
res.render("posts/edit"); //req.query.write값이 있다면 posts/edit 템플릿으로 이동
return;
}
....
});
....
module.exports = router;
🛠 /views/posts/edit.pug
form(action="/posts", method="post")
table
tbody
tr
th 제목
td: input(type="text" name="title")
tr
th 제목
td: textarea(name="content")
td
td(colspan="2")
input(type="submit" value="등록")
📕 POST 요청 처리하기
const { Post } = require("./models");
...
router.post("/", async (req, res, next) => {
const { title, content } = req.body;
try {
// create 생성
await Post.create({
title,
content,
author: "DUMMY", // set dummy author before add users
});
res.redirect("/"); // 게시글 작성이 완료되면 root path로 이동, posts/post:shortid로 전달하면 작성한 게시글로 이동
} catch (err) {
next(err);
}
});
...
📝 게시글 목록 및 상세
📕 게시글 목록 및 상세 흐름
/posts
로 목록 페이지 접근<a href="/posts/:shortId">
이용하여 상세 URL Link- router.get('/:shortId') path parameter 이용하여 요청 처리
📕 게시글 목록 구현하기
🛠 /routes/posts.js
router.get("/", async (req, res, next) => { // posts 경로로 접근했을 때
const posts = await Post.find({}); // 작성된 모든 글 가져오기
res.render("posts/list", { posts }); //posts/list에 posts 값 전달
});
🛠 /view/posts/list.pug
...
table
tbody
each post in posts
tr
td
a(href=`/posts/${post.shortId}`) = post.title // 경로설정
td= post.author
td= formatDate(post.createdAt) // formatDate는 따로 추가한 함수이다.
tfoot
tr
td(colspan="3")
a(href="/posts?write=true") 등록하기
📕 formatDate 함수 추가하기
🛠 app.js
const dayjs = require("dayjs");
app.locals.formatDate = (date) => {
return dayjs(date).format("YYYY-MM-DD HH:mm:ss"); // 특정 포맷으로 변환
};
📕 게시글 상세 구현하기
🛠 /routes/posts.js
router.get("/:shortId", async (req, res, next) => {
const { shortId } = req.params;
const post = await Post.findOne({ shortId }); // 게시글 검색
if (!post) {
next(new Error('Post NotFound');
return;
}
...
res.render("posts/view", { post });
});
🛠 /views/posts/view.pug
...
table
tbody
tr
td(colspan="2")= post.title
tr
td= post.author
td= formatDate(post.createdAt)
tr
td(colspan="2"): pre= post.content
tr
td: a(href=`/posts/${post.shortId}?edit=true`) 수정
td button(onclick=`deletePost("${post.shortId")`) 삭제
📝 게시글 수정
📕 게시글 수정 흐름
/posts/{shortId}?edit=true
로 수정 페이지 접근- 작성 페이지를 수정 페이지로도 동작하도록 작성
<form action="/posts/:shortId" method="post">
로 post 요청 전송- html form은 PUT method를 지원하지 않기 때문에 post 사용
📕 수정 페이지 만들기
🛠 /routes/posts.js
router.get("/:shortId", async (req, res, next) => {
...
if (req.query.edit) { // query가 edit으로 들어왔을 때
res.render("posts/edit", { post }); // 아까 상세 페이지에서 찾았던 post값 넘겨주기
}
...
});
🛠 /view/posts/edit.pug
...
- var action = post ? `/posts/${post.shortId}` : "/posts"
form(action=action, method="post")
table
tbody
tr
th 제목
td: input(type="text" name="title" value=post&&post.title)
tr
th 제목
td: textarea(name="content")= post&&post.content
td
td(colspan="2")
- var value = post ? "수정" : "등록"
input(type="submit" value=value)
📕 수정 요청 처리하기
...
router.post('/:shortId', async (req, res, next) => {
const { shortId } = req.params;
const { title, content } = req.body;
const post = await Post.findOneAndUpdate({ shortId }, {
title, content,
}); // shortId 검색해서 내용을 title, content로 변경하겠다는 의미
if (!post) {
next(new Error('Post NotFound');
return;
}
res.redirect(`/posts/${shortId}`);
});
📝 게시글 삭제
📕 게시글 삭제 흐름
- 게시글 상세 페이지에 삭제 버튼 추가
- html form은 DELETE 메서드를 지원하지 않음
- JavaScript를 이용해 fetch 함수로 HTTP Delete 요청 전송
- router.delete의 응답을 fetch에서 처리
📕 HTTP Delete 요청 전송 및 응답 처리
🛠 /posts/view.pug
td
button.delete(
onclick=`deletePost("${post.shortId}")`) 삭제
...
...
script(type="text/javascript").
function deletePost(shortId) {
fetch('/posts/' + shortId, { method: 'delete' })
.then((res) => {
if (res.ok) {
alert('삭제되었습니다.');
window.location.href = '/posts';
} else {
alert('오류가 발생했습니다.');
console.log(res.statusText);
}
})
.catch((err) => {
console.log(err);
alert('오류가 발생했습니다.');
})
}
📕 DELETE 요청 처리하기
🛠 /routes/posts.js
const { Post } = require("./models");
...
router.delete("/:shortId", async (req, res, next) => {
const { shortId } = req.params;
try {
await Post.delete({ shortId });
res.send("OK");
} catch (err) {
next(err);
}
});
...
반응형
'프로그래밍 > Node.js' 카테고리의 다른 글
[Node.js] 게시판 페이지네이션 구현하기, Pagination (0) | 2021.12.09 |
---|---|
[Node.js] Async Request Handler (0) | 2021.12.09 |
[Node.js] Node.js REST API란? (0) | 2021.12.04 |
[Node.js] Node.js Middleware란? (0) | 2021.12.04 |
[Node.js] Express.js 동작 방식 (0) | 2021.12.02 |
댓글
이 글 공유하기
다른 글
-
[Node.js] 게시판 페이지네이션 구현하기, Pagination
[Node.js] 게시판 페이지네이션 구현하기, Pagination
2021.12.09 -
[Node.js] Async Request Handler
[Node.js] Async Request Handler
2021.12.09 -
[Node.js] Node.js REST API란?
[Node.js] Node.js REST API란?
2021.12.04 -
[Node.js] Node.js Middleware란?
[Node.js] Node.js Middleware란?
2021.12.04