룰루랄라 코딩기록장

테이블 설계 본문

YAMPLI

테이블 설계

Jeonnnng 2023. 12. 8. 16:59

요구사항

  1. 공통
    1. 데이터 무결성 유지를 위한 미들웨어 추가.


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
  1. 유저 정보를 저장하는 문서
  1. 주요 필드
    • 사용자 이메일
    • 비밀번호
    • 카카오 고유 식별값

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
  1. 그룹 정보를 저장하는 문서
  1. 주요 필드
    • 그룹 이름
    • 공개 여부
    • 참여 유저
    • 소유하고 있는 플레이리스트

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
  1. 플레이리스트 정보를 저장하는 문서
  1. 주요 필드
    • 플레이리스트 이름
    • 포함된 노래
    • 소유 그룹

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
  1. 플레이리스트와 노래의 연결 정보를 저장하는 문서
  1. 주요 필드
    • 노래 데이터
    • 태그 데이터
    • 소유 플레이리스트

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
  1. 노래 정보를 저장하는 문서
  1. 주요 필드
    • 유튜브 동영상 Id
    • 동영상 주소
    • 노래 제목
    • 가수 이름

Tag

const tagSchema = new mongoose.Schema({
    name: { type: String, required: true, unique: true },
})

const Tag = mongoose.model("Tag", tagSchema)

module.exports = Tag
  1. 태그 정보를 저장하는 문서

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
  1. 태그 사용 횟수를 저장하는 문서

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
  1. 댓글 정보를 저장하는 문서
  1. 주요 필드
    • 작성자
    • 답글

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
  1. 답글 정보를 저장하는 문서
  1. 주요 필드
    • 부모 댓글을 참조하는 필드
    • 답글에 작성되는 답글을 참조하는 필드

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
  1. 좋아요 정보를 저장하는 문서
  1. 주요 필드
    • targetType : 좋아요를 누를 수 있는 데이터들의 타입을 저장하고 있는 필드
    • target : 좋아요를 누른 데이터의 id값을 저장하는 필드

Uploaded by N2T

Comments