요구사항
- 공통
- 데이터 무결성 유지를 위한 미들웨어 추가.
Schema
User
// user.js
const userSchema = mongoose.Schema({
email: { type: String, required: true, lowercase: true, unique: true },
password: { type: String, required: true },
nickname: { type: String, minLength: 1, maxLength: 100, required: true },
kakaoId: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now }, // 작성 시간을 저장하는 필드 추가
isActive: { type: Boolean, default: 1 },
img: { type: String },
role: { type: String, default: "Normal" },
groups: [{ type: mongoose.Schema.Types.ObjectId, ref: "Group" }],
likedGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: "Group" }],
likedSongs: [{ type: mongoose.Schema.Types.ObjectId, ref: "Song" }],
likedComments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
likedReplies: [{ type: mongoose.Schema.Types.ObjectId, ref: "Reply" }],
})
// 비밀번호 검증 인스턴스 메소드 추가
userSchema.methods.isPasswordMatch = async function (password) {
const user = this
return await bcrypt.compare(password, user.password)
}
// 비밀번호 해싱을 위한 미들웨어
userSchema.pre("save", async function (next) {
const user = this
// isModified(field) : field 값이 변경 될 때 해싱 작업 처리
if (user.isModified("password")) {
user.password = await bcrypt.hash(user.password, 8)
}
next()
})
// User 스키마 (user.js)
userSchema.pre("remove", async function (next) {
const userId = this._id
await Comment.deleteMany({ author: userId })
await Like.deleteMany({ user: userId })
await Group.updateMany({}, { $pull: { users: userId } })
await Reply.deleteMany({ author: userId })
next()
})
const User = mongoose.model("User", userSchema)
module.exports = User
- 유저 정보를 저장하는 문서
- 주요 필드
- 사용자 이메일
- 비밀번호
- 카카오 고유 식별값
Group
const groupSchema = mongoose.Schema({
title: { type: String, required: true },
isPublic: { type: Boolean, default: false },
user: [{ type: mongoose.Schema.Types.ObjectId, required: true, ref: "User" }],
playlists: [{ type: mongoose.Schema.Types.ObjectId, ref: "Playlist" }],
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
createdAt: { type: Date, default: Date.now },
})
groupSchema.pre("remove", async function (next) {
const groupId = this._id
await Playlist.find({ group: groupId }).then((playlists) => playlists.forEach((playlist) => playlist.remove()))
await Comment.deleteMany({ group: groupId })
await Like.deleteMany({ target: groupId, targetType: "Group" })
await User.updateMany({}, { $pull: { groups: groupId } })
next()
})
const Group = mongoose.model("Group", groupSchema)
module.exports = Group
- 그룹 정보를 저장하는 문서
- 주요 필드
- 그룹 이름
- 공개 여부
- 참여 유저
- 소유하고 있는 플레이리스트
Playlist
const playlistSchema = mongoose.Schema({
title: { type: String, required: true },
group: { type: mongoose.Schema.Types.ObjectId, required: true, ref: "Group" },
songs: [{ type: mongoose.Schema.Types.ObjectId, ref: "PlaylistSong" }], // songs 필드를 playlistSongSchema로 변경
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
})
playlistSchema.pre("remove", async function (next) {
await PlaylistSong.find({ playlist: this._id }).then((songs) => songs.forEach((song) => song.remove()))
next()
})
const Playlist = mongoose.model("Playlist", playlistSchema)
module.exports = Playlist
- 플레이리스트 정보를 저장하는 문서
- 주요 필드
- 플레이리스트 이름
- 포함된 노래
- 소유 그룹
PlaylistSong
const playlistSongSchema = mongoose.Schema({
song: { type: mongoose.Schema.Types.ObjectId, ref: "Song", required: true },
tags: [{ type: mongoose.Schema.Types.ObjectId, ref: "Tag" }],
playlist: { type: mongoose.Schema.Types.ObjectId, ref: "Playlist" }, // 이 부분이 추가됨
})
playlistSongSchema.pre('remove', async function (next) {
const playlistSongId = this._id;
await Comment.deleteMany({ source: this._id });
await Like.deleteMany({ target: this._id, targetType: 'PlaylistSong' });
await Playlist.updateMany({}, { $pull: { playlists: playlistSongId } });
next();
});
module.exports = playlistSongSchema
- 플레이리스트와 노래의 연결 정보를 저장하는 문서
- 주요 필드
- 노래 데이터
- 태그 데이터
- 소유 플레이리스트
Song
const songSchema = mongoose.Schema({
vidId: { type: String, required: true, unique: true },
url: { type: String, required: true },
title: { type: String, required: true },
artist: { type: String, required: true },
thumb: [{ type: String, required: true }],
tags: [{ type: mongoose.Schema.Types.ObjectId, ref: "Tag" }],
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
})
// 태그 사용 횟수 업데이트 함수
songSchema.methods.updateTagUsage = async function () {
const song = this
// 현재 노래에 연결된 태그들을 가져옴
const songTags = song.tags
// 각 태그에 대해 사용 횟수를 1씩 증가
for (const tagId of songTags) {
await TagUsage.findOneAndUpdate({ tag: tagId }, { $inc: { count: 1 } }, { upsert: true })
}
}
songSchema.pre("remove", async function (next) {
const songId = this._id
// 노래에 직접 달린 댓글과 PlaylistSong과 관련된 댓글 삭제
await Comment.deleteMany({ song: songId })
await Comment.deleteMany({ source: songId })
// 노래와 관련된 좋아요 삭제
await Like.deleteMany({ target: songId, targetType: "Song" })
// 연결된 PlaylistSong 삭제
await PlaylistSong.deleteMany({ song: songId })
// 태그 사용 횟수 업데이트
const songTags = this.tags
for (const tagId of songTags) {
await TagUsage.findOneAndUpdate({ tag: tagId }, { $inc: { count: -1 } }, { upsert: true })
}
next()
})
const Song = mongoose.model("Song", songSchema)
module.exports = Song
- 노래 정보를 저장하는 문서
- 주요 필드
- 유튜브 동영상 Id
- 동영상 주소
- 노래 제목
- 가수 이름
Tag
const tagSchema = new mongoose.Schema({
name: { type: String, required: true, unique: true },
})
const Tag = mongoose.model("Tag", tagSchema)
module.exports = Tag
- 태그 정보를 저장하는 문서
TagUsage
const tagUsageSchema = new mongoose.Schema({
tag: { type: mongoose.Schema.Types.ObjectId, ref: "Tag", required: true },
count: { type: Number, default: 0 },
})
const TagUsage = mongoose.model("TagUsage", tagUsageSchema)
module.exports = TagUsage
- 태그 사용 횟수를 저장하는 문서
Comment
const commentSchema = new mongoose.Schema({
text: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
group: { type: mongoose.Schema.Types.ObjectId, ref: "Group" }, // 현재 그룹의 ObjectId
song: {
type: mongoose.Schema.Types.ObjectId,
ref: "Song",
}, // 노래에 직접 연결된 댓글을 나타내기 위한 필드
replies: [{ type: mongoose.Schema.Types.ObjectId, ref: "Reply" }],
createdAt: { type: Date, default: Date.now }, // 작성 시간을 저장하는 필드 추가
source: {
type: mongoose.Schema.Types.ObjectId,
ref: "PlaylistSong",
}, // 특정 플레이리스트 노래에 대한 댓글 조회하기 기능에 사용되는 필터링 로직을 구현하기 위한 필드
})
commentSchema.pre("remove", async function (next) {
await Like.deleteMany({ target: this._id, targetType: "Comment" })
await Reply.deleteMany({ parentComment: this._id }) // 대댓글 삭제
next()
})
const Comment = mongoose.model("Comment", commentSchema)
module.exports = Comment
- 댓글 정보를 저장하는 문서
- 주요 필드
- 작성자
- 답글
Reply
const replySchema = new mongoose.Schema({
text: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
parentComment: { type: mongoose.Schema.Types.ObjectId, ref: "Comment", required: true },
replies: [{ type: mongoose.Schema.Types.ObjectId, ref: "Reply" }], // 대댓글
createdAt: { type: Date, default: Date.now }, // 작성 시간을 저장하는 필드 추가
})
replySchema.pre("remove", async function (next) {
// 연관된 좋아요 삭제
await Like.deleteMany({ target: this._id, targetType: "Reply" })
// 대댓글의 대댓글 삭제 (재귀적으로 처리)
for (const replyId of this.replies) {
const subReply = await Reply.findById(replyId)
if (subReply) {
await subReply.remove()
}
}
next()
})
const Reply = mongoose.model("Reply", replySchema)
module.exports = Reply
- 답글 정보를 저장하는 문서
- 주요 필드
- 부모 댓글을 참조하는 필드
- 답글에 작성되는 답글을 참조하는 필드
Like
const likeSchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
target: {
type: mongoose.Schema.Types.ObjectId,
required: true,
refPath: "targetType",
},
targetType: {
type: String,
required: true,
enum: ["Group", "Song", "Comment", "Reply"],
},
createdAt: { type: Date, default: Date.now },
})
const Like = mongoose.model("Like", likeSchema)
module.exports = Like
- 좋아요 정보를 저장하는 문서
- 주요 필드
- targetType : 좋아요를 누를 수 있는 데이터들의 타입을 저장하고 있는 필드
- target : 좋아요를 누른 데이터의 id값을 저장하는 필드
Uploaded by N2T