일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 컴퓨터 구조론
- MongoDB
- 유튜브클론코딩
- JavaScript
- 자료구조
- 마이크로명령어
- Session
- 후위표기법변환
- Nodemon
- 연결자료구조
- 표현식과 문
- node.js
- cookie
- 컴퓨터 구조
- 자바스크립트 배열
- CPU
- react
- 제어유닛
- 리액트
- Express
- mongoose
- 깃허브 로그인
- pug
- 보조저장장치
- 후위표기법연산
- 유튜브 클론코딩
- 프론트엔드
- 자바스크립트
- 제어유니트
- 모던 자바스크립트 Deep Dive
- Today
- Total
909 Devlog
[유튜브 클론코딩] MongoDB 데이터베이스 모델 관계 연결 본문
지금 제가 만든 프로젝트에는 2가지 데이터 모델이 있습니다.
하나는 Video 모델이고, 하나는 User 모델입니다.
지금은 두 데이터베이스 모델들이 연결되어있지 않지만, 곧 구현할 기능을 위해서는 User에는 해당 'user'가 업로드한 'video'들의 정보가 필요하고, Video에는 해당 'video'를 업로드한 'user'의 정보가 필요하니 두 데이터베이스 모델끼리 연결해야 합니다.
따라서, User에는 해당 'user'가 업로드한 모든 'video'의 _id를 저장하고, Video에는 해당 'video'를 업로드한 'user'의 _id를 저장해 보겠습니다.
1. 데이터베이스 _id 공유
먼저 videoSchema에 'owner'를 추가해 보도록 하겠습니다.
const videoSchema = new mongoose.Schema({
title: { type: String, required: true },
fileUrl: { type: String, required: true },
description: { type: String, required: true },
createdAt: { type: Date, required: true, default: Date.now },
hashtags: [{ type: String }],
meta: {
views: { type: Number, default: 0, required: true },
rating: { type: Number, default: 0, required: true },
},
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
});
owner의 옵션을 보면, type이 mongoose.Schema.Types.ObjectId라고 되어 있는 것을 볼 수 있습니다.
owner의 type은 MongoDB에서 부여해준 ObjectId라는 type을 사용하는데, 기본 자바스크립트의 type은 ObjectId와 같은 type이 없으니, mongoose에서 만든 type을 사용합니다.
또, ref는 어떤 모델을 연결할지 설정하는 옵션으로, 'User' 모델을 사용한다고 설정해 주었습니다.
위 "User" String은 아래 사진에 빨간 줄로 표시해 놓은 String처럼 Model을 생성할 때 설정해 놓은 이름과 같은 이름을 사용해야 합니다.
이제 postUpload controller에서 영상을 업로드할 때, 사용자의 _id를 보내도록 코드를 작성하겠습니다.
export const postUpload = async (req, res) => {
const { _id } = req.session.user;
...
};
세션에 있는 user값에서 해당 user의 _id를 가져왔으니, 영상 정보를 데이터베이스에 저장할 때, owner도 함께 저장해 주도록 하겠습니다.
export const postUpload = async (req, res) => {
const { _id } = req.session.user;
...
try {
await VideoModel.create({
title: title,
description: description,
fileUrl: file.path,
owner: _id,
hashtags: VideoModel.formatHashtags(hashtags),
});
...
};
미리 생성해 놓은 계정으로, 영상을 새로 업로드했습니다.
이제 데이터베이스를 보면, 저장되어 있는 user의 _id와, video에 저장되어있는 owner의 _id가 같은 것을 볼 수 있습니다.
1.1 populate
video 모델에, 영상을 업로드한 owner의 정보를 저장해 놓았으니, 이제 영상을 보는 페이지에 영상의 owner 정보를 볼 수 있으면 좋을 것 같습니다.
기존에 했었던 방법인 아래 코드처럼,
const { id } = req.params;
const video = await VideoModel.findById(id);
const owner = await UserModel.findById(video.owner);
video 모델을 찾고, video 모델에 저장되어 있는 owner를 통해 user 모델을 찾아도 동작하지만, populate를 사용하면 더 간편하고 빠르게 동작하는 코드를 작성할 수 있습니다.
populate를 사용하는 방법은 아래 코드와 같습니다.
const { id } = req.params;
const video = await VideoModel.findById(id).populate("owner");
video 정보를 찾았던 코드 뒤에 .populate()를 붙여주기만 하면 됩니다.
populate가 어떤 역할을 하냐면,
아래처럼 _id만 저장되어 있는 video 데이터의 owner 부분을
통째로 아래처럼 해당 _id를 가진 데이터로 채워줍니다.
해당 _id가 어떤 모델에 저장되어 있는지 확인하기 위해 video 모델의 owner 옵션 부분에 ref를 설정해 주었던 것입니다.
populate에 대한 더 자세한 정보는 공식문서에서 확인하실 수 있습니다.
2. user 모델에 video들의 _id 저장
_id들을 저장하기 전에, 모델을 수정해야 하니 데이터베이스에 저장되어 있는 모든 데이터를 지우고 시작합니다.
영상은 단 한 명의 업로더만 있지만, 유저는 여러 개의 영상을 가질 수 있으니, video에 저장한 owner와는 다르게 user의 videos를 배열로 만들고, 유저가 업로드한 영상이 없을 수도 있으니, required를 작성하지 않았습니다.
const userschema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
avatarUrl: String,
socialnOnly: { type: Boolean, default: false },
username: { type: String, required: true, unique: true },
password: { type: String, required: false },
name: { type: String, required: true },
location: String,
videos: [{ type: mongoose.Schema.Types.ObjectId, ref: "Video" }],
});
이제 영상 업로드를 담당하는 postUpload controller를 다음과 같이 수정합니다.
export const postUpload = async (req, res) => {
const { _id } = req.session.user;
const file = req.file;
const { title, description, hashtags } = req.body;
try {
const newVideo = await VideoModel.create({
title: title,
description: description,
fileUrl: file.path,
owner: _id,
hashtags: VideoModel.formatHashtags(hashtags),
});
const user = await UserModel.findById(_id);
user.videos.push(newVideo._id);
user.save();
return res.redirect("/");
} catch (error) {
return res.status(400).render("upload", {
pageTitle: "Upload Video",
errorMessage: error._message,
});
}
};
위 코드에서 create() 메서드는 create()가 새로 만드는 모델을 return 합니다.
따라서 위 코드는 새로 만든 video 모델을 newVideo라고 저장하고, newVideo를 userschema에 만든 videos 배열에 추가하는 코드입니다.
다음으로 업로더가 올린 영상들을 보기 위해, 업로더의 정보를 볼 때 사용하는 컨트롤러인 see controller를 다음과 같이 수정합니다.
export const see = async (req, res) => {
const { id } = req.params;
const user = await UserModel.findById(id).populate("videos");
if (!user) {
return res.status(404).render("404", { pageTitle: "User not found" });
}
return res.render("profile", {
pageTitle: user.name,
user: user,
});
};
video에 populate를 사용했던 것과 비슷하게 user에 "videos"라고 저장되어 있는 부분을 populate 해서 모든 영상 정보들이 담긴 배열이 videos에 나타나게 되었습니다.
이제 profile.pug를 다음과 같이 수정하면,
extends base.pug
include mixins/video.pug
block content
each video in user.videos
+video(video)
else
span Sorry nothing found.
프로필 페이지에 들어갔을 때, 해당 유저가 올린 모든 영상을 볼 수 있게 되었습니다.
'토이 프로젝트 > 유튜브 클론코딩' 카테고리의 다른 글
[유튜브 클론코딩] API 만들기 (0) | 2023.08.08 |
---|---|
[유튜브 클론코딩] Webpack (0) | 2023.08.06 |
[유튜브 클론코딩] GitHub Login (0) | 2023.08.04 |
[유튜브 클론코딩] Sessions and Cookies (0) | 2023.08.01 |
[유튜브 클론코딩] mongoDB - CRUD (0) | 2023.07.26 |