1.게시글 작성 시 로그인된 회원정보를 작성자로 추가
2.게시글-작성자는 populate하여 사용하도록 구현
3.게시글 수정, 삭제 시 로그인된 유저와 작성자가 일치하는지 확인
4.작성자의 게시글 모아 보기 기능 구현
PostSchema에 author 추가
populate를 사용하기 위해 ObjectID 사용
ref 를 유저 모델의 이름인 'User'로 선언
게시글에 작성자 추가
req.user에는 strategy에서 최소한의 정보로 저장한 shortid, email,username만 가지고 있음
Post생성 시 user의 ObjectID를 전달해야하는데, 이를 위해 User에서 shortId로 회원을 검색하여 한번 더 검증
type:ObjectID로 선언한 필드에 객체가 주어지면 자동으로 ObjectID사용
게시글 find 시 populate를 추가하여 ObjectID로 저장된 author를 각 게시글에 주입
사용시 post.author.{field} 로 사용 가능
게시글 수정, 삭제 시 작성자를 populate하여 로그인된 사용자와 일치하는지 확인
기본적으로 MongoDB는 Document 검색 시, 전체 문서를 하나씩 확인 함
하나씩 확인하기 때문에 매우 비효율적인 검색 수행
데이터가 많아질 경우 속도 저하의 가장 큰 원인이 됨
(1)Index
MongoDB는 검색을 위해 Document를 정렬하여 저장하는 기능을 제공 함
Index를 설정하면 주어진 쿼리를 효율적으로 수행하여 성능을 향상시킬 수 있음
*다중 키, 좌표, 텍스트 등의 특별한 값으로 정리되는 인덱스도 제공
(2)author에 index 설정하기
PostSchema의 author 속성에 index:true 옵션을 사용하면 mongoose가 자동으로 MongoDB에 인덱스를 생성해줌
이미 데이터가 많은 상태에서 인덱스를 추가할 시 작업 시간이 길어져, MongoDB가 응답하지 않을 수 있음 -> 예상되는 인덱스를 미치 추가하는 것이 좋음
(3)회원 게시글 라우팅 추가하기
RESTful한 구성을 위해, 회원->게시글의 경로를 /users/{userId}/posts로 구성
게시글 목록 view는 기존에 작성한 posts/list.pug를 재활용
게시글 목록 화면을 재활용하기 위해 수정
유저의 게시글인 경우 "###의 게시글"이라는 제목 사용
게시글의 사용자 이름에 유저의 게시글 link 추가
const { Router } = require('express');
const { Post, User } = require('../models');
const asyncHandler = require('../utils/async-handler');
const router = Router();
router.get('/', asyncHandler(async (req, res) => {
if (req.query.write) {
res.render('post/edit');
return;
}
const page = Number(req.query.page || 1);
const perPage = Number(req.query.perPage || 10);
// getPaginatedPosts 사용하기
const [posts,totalPage] =await Post.getPaginatedPosts({},page,perPage)
res.render('post/list', { posts, page, perPage, totalPage, path: req.baseUrl });
}));
router.get('/:shortId', asyncHandler(async (req, res) => {
const { shortId } = req.params;
const post = await Post.findOne({ shortId }).populate('author');
if (req.query.edit) {
res.render('post/edit', { post });
return;
}
res.render('post/view', { post });
}));
router.post('/', asyncHandler(async (req, res) => {
const { title, content } = req.body;
if (!title || !content) {
throw new Error('제목과 내용을 입력 해 주세요');
}
// 로그인 된 사용자의 shortId 로 사용자를 찾아 게시글 생성시 작성자로 추가
const author=await User.findOne({
shortId:req.user.shortId
})
if(!author){
throw new Error('No Author')
}
const post = await Post.create({ title, content,author });
res.redirect(`/posts/${post.shortId}`);
}));
router.post('/:shortId', asyncHandler(async (req, res) => {
const { shortId } = req.params;
const { title, content } = req.body;
if (!title || !content) {
throw new Error('제목과 내용을 입력 해 주세요');
}
const post = await Post.findOne({ shortId }).populate('author'); // 작성자 populate
// 작성자와 로그인된 사용자의 shortId 가 다를경우 오류 발생
await Post.updateOne({ shortId }, { title, content })
if(post.author.shortId!==req.user.shortId){
throw new Error('작성자가 아닙니다')
}
res.redirect(`/posts/${shortId}`);
}));
router.delete('/:shortId', asyncHandler(async (req, res) => {
const { shortId } = req.params;
const post = await Post.findOne({ shortId }).populate('author')
// 작성자 populate
// 작성자와 로그인된 사용자의 shortId 가 다를경우 오류 발생
if(post.author.shortId!==req.user.shortId){
throw new Error('작성자가 아닙니다')
}
await Post.deleteOne({ shortId });
res.send('OK');
}));
module.exports = router;
const mongoose = require('mongoose');
const PostSchema = require('./schemas/post');
const UserSchema = require('./schemas/user');
const Post = mongoose.model('Post', PostSchema);
Post.getPaginatedPosts = async (query, page, perPage) => {
const [total, posts] = await Promise.all([
Post.countDocuments(query),
Post
.find(query)
.sort({ createdAt : -1 })
.skip(perPage * (page - 1))
.limit(perPage)
.populate('author')
// populate 추가하기
]);
const totalPage = Math.ceil(total / perPage);
return [posts, totalPage];
}
exports.Post = Post;
exports.User = mongoose.model('User', UserSchema);
const { Router } = require('express');
const { User, Post } = require('../models');
const asyncHandler = require('../utils/async-handler');
const router = Router();
router.get('/:shortId/posts', asyncHandler(async (req, res) => {
const { shortId } = req.params;
// 유저 게시글 모아보기 기능 완성하기
const author =await User.findOne({shortId})
if(!author){
throw new Error('사용자가 없습니다')
}
const page=Number(req.query.page||1)
const perPage=Number(req.query.perPage||10)
const [posts,totalPage]=await Post.getPaginatedPosts({author},page,perPage)
res.render('post/list', {posts, page, perPage, totalPage, user, path: req.baseUrl + req.url });
}));
module.exports = router;
const { Schema } = require('mongoose');
const shortId = require('./types/short-id');
const PostSchema = new Schema({
shortId,
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
author: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
index:true
// index 추가하기
},
}, {
timestamps: true,
});
module.exports = PostSchema;