본 문서는 2022년 3월 24일 에 작성되었습니다.
여기서는 Velog - unchaptered / Mognoose (Basic) 에서 다루지 않은 다양한 기법을 포함할 생각입니다.
물론, 전 게시글에서도 Schema, Model 기능을 따로 설명하지는 않았습니다.
또한 mongoose 의 수많은 메서드나 예약어, 명령어 들도 설명하지 않았습니다.
이 부분은 개인의 학습에 맡기며, 저 또한 이를 무차별적으로 외우는 것이 불필요하다고 느꼈습니다.
따라서, 본문에서는 해당 내용을 알고 있다는 전제를 깔고 설명을 할 생각입니다.
본 내용 중 다수는 JavaScript 지식이 필요합니다.
아래 포스트를 참고해주시고 Async-Await, Promise 는 구글링을 해주세요.
Velog - unchaptered / 시리즈 | JavaScript
방법 | (기대) 호출 수 |
---|---|
RESTful API 식 | M * N 번 |
Promise.All([]) 사용 | N + 1 번 |
populate 사용 | 3 번 |
virtual populate 사용 | 3 번 ( 용량 차지 안함, 성능은 ? ) |
denomalize 사용 | 1번 ( 극한의 IO, 하지만 별도의 처리 필요 ! ) |
기본적으로 DB 에서 값을 받아오는 과정은 딜레이가 있습니다.
따라서 이 결과를 사용하기 위해서 동기 처리(async - await) 가 필요합니다.
하지만 어떠한 DB 접근은 서로 무관한 접근 이기 때문에, 비동기처리(promise) 를 통해서 성능을 향상 시킬 수 있습니다.
// 동기 처리를 이용한 방법 N * M 의 호출
const getUser = async (req, res) => {
const userList = await UserModel.fine({}).limit(10);
const postList = await PostModel.find({}).limit(10);
const commentList = await CommentModel.find({}).limit(10);
}
// 비동기 처리 + 동기 처리를 이용한 방법 N + 1 의 호출
const getUser = async (req, res) => {
const [ userList, postLists, commentList ] = await Promise([
UserModel.fine({}).limit(10),
PostModel.find({}).limit(10),
CommentModel.find({}).limit(10)
]);
}
RDBMS 의 Relationship 처럼 MongoDB 도 reference 가 존재합니다.
이렇게 reference 설정이 되어있는 경우, mongoose 의 populate 를 사용할 수 있습니다.
// Populate 활용
const getUser = async (req, res) => {
const userList = await UserModel
.find({})
.popluate("postlist(키 값)")
.populate("commentlist(키 값)").limit(10);
}
위 Populate 활용 에서 보듯이,
User - Post - Comment 로 연달아 이어지는 계층형 구조의 정보는
실제적으로 Schema 를 적어보면 다음과 같이 중복된 구조(무결성의 오류) 가 발생할 수 있습니다.
const userSchema = new Schema({
// 대충 user 전용 필드
posts: [{ type:ObjectId, ref: "Post" }],
comments: [{ type:ObjectId, ref: "Comment" }]
});
const postSchema = new Schema({
// 대충 post 전용 필드
owner: { type:ObjectId, ref: "User" },
comments: [{ type:ObjectId, ref: "Comment" }]
});
const commentSchema = new Schema({
// 대충 comment 전용 필드
owner: { type:ObjectId, ref: "User" },
ownerPost: { type:ObjectId, ref: "Post" }
});
이러한 경우 RDBMS 로 치면 외래키 참조에 의한 강한 결합 이라고 할 수 있을 것이나,
역시나 느슨한 결합 을 Virtual 이라는 메서드를 이용해서 구현할 수 있습니다/
postSchema 의 comments 를 해당 기능을 이용해 결합하면,
다음과 같이 구현할 수 있습니다.
const postSchema = new Schema({
// 대충 post 전용 필드
owner: { type:ObjectId, ref: "User" },
// comments: [{ type:ObjectId, ref: "Comment" }]
});
postSchema.virtual("comments", {
ref: "comment",
localField: "_id",
foreignField: "post"
});
postSchema.set("toObject", { virtual: true });
postSchema.set("toJSON", { virtual: true });
const Post = model("Post", postSchema");
export default Post;
더 자세한 내용은 Mongoose Virtuals 를 참고해야 할 것 같습니다.
Denomalize 는 반정규화라는 내용입니다.
DB 의 입출력 성능을 높이기 위해, 비즈니스 로직 상 유리하다고 판단되는 부분을 중복된 형태로 가공 저장 하는 것을 의미합니다. MongoDB 에서는 이것을 서로 다른 document 를 내장형태로 저장 하는 방법으로 구현됩니다.
const postSchema = new Schema({
postTitle: { type:String, reqruied:true, default:"제목 없음" },
postTexts: { type:String, reqruied:true, default:"내용 없음" },
comment: [{
commentTexts: { type:String, required:true, defualt:"내용 없음" },
commentOwner: { type:ObjectId }
}]
});
단,
이러한 Denomalize 의 경우에는 MongoDB 고유의 Document 크기 제한인 16MB 에 걸릴 수 있으므로,
별도의 후속 처리를 해야지 안정적으로 사용할 수 있습니다.
Nesting 은 Denomalize 되어 있는 배열 정보를 수정하기 위한 기능 으로서 $ 키워드 를 사용합니다.
const postSchema = new Schema({
postTitle: { type:String, reqruied:true, default:"제목 없음" },
postTexts: { type:String, reqruied:true, default:"내용 없음" },
comment: [{
commentTexts: { type:String, required:true, defualt:"내용 없음" },
commentOwner: { type:ObjectId }
}]
});
위와 같은 구조로 만들어진 post document 에는 comment 라고 하는 배열 정보가 담겨 있습니다.
이러한 comment 의 특정한 comment 만 수정하기 위해서는 다음의 2가지 방법이 가능합니다.
// 1번 방법
const patchCommentInPost = async (req, res) => {
const post = await postModel.findById(postId);
const targetIndex = undefined;
post.forEach((value, key) => {
if (value._id === commentId) return targetIndex = key;
});
post.comments[targetIndex] = content;
post.save();
// ... 이후 비즈니스 로직 실행
}
// 2번 방법
const patchCommentInPost = async (req, res) => {
await postModel.updateOne(
{ "comment._id" : commentId }, // 특정한 comment 1개를 찾고
{ "comments.$.content" : content } // $ 키워드로 그 1개에 의 content 를 수정
);
// ... 이후 비즈니스 로직 실행
}
사실,
여기서부터는 시기장조라고 생각하지만,
MongoDB 최적화 관련된 내용을 치면서 만난 색다른 포스트들이었기에 포함해보았습니다.