Comment Section

김종민·2022년 11월 13일
0

Youtube

목록 보기
22/23

들어가기
비디오에 댓글달기를 해 봅시다


1. src/models/Comment.js

import mongoose from 'mongoose'
///맨처음 mongoose를 import한다.

const commentSchema = new mongoose.Schema({
  text: { type: String, required: true },
  owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  video: { type: mongoose.Schema.Types.ObjectId, ref: 'Video' },
  createdAt: { type: Date, required: true, default: Date.now },
})
///owner와 video를 ref해 준다는 점을 명심한다,
///User와 Video에서 Comment를 ref한다.


const Comment = mongoose.model('Comment', commentSchema)

export default Comment

2. src/models/Video.js

Comment 들어간 부분만 확인할것!!

import mongoose from 'mongoose'

// export const formatHashtags = (hashtags) =>
//   hashtags.split(',').map((word) => (word.startsWith('#') ? word : `#${word}`))

const videoSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    upppercase: true,
    trim: true,
    maxLength: 140,
  },
  fileUrl: { type: String, required: true },
  description: String,
  createdAt: { type: Date, required: true, default: Date.now },
  hashtags: [{ type: String, trim: true }],
  meta: {
    views: { type: Number, default: 0, required: true },
    rating: { type: Number, default: 0, required: true },
  },
  comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
  owner: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
})

3. src/models/User.js

Comment들어간 부분만 확인한다.

import mongoose from 'mongoose'
import bcrypt from 'bcrypt'

// export const formatHashtags = (hashtags) =>
//   hashtags.split(',').map((word) => (word.startsWith('#') ? word : `#${word}`))

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  avatarUrl: String,
  socialOnly: { type: Boolean, default: false },
  username: { type: String, required: true, unique: true },
  password: { type: String },
  name: { type: String },
  location: String,
  videos: [
    { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'Video' },
  ],
  comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
})

userSchema.pre('save', async function () {
  // console.log('111', this.password)
  if (this.isModified('password')) {
    this.password = await bcrypt.hash(this.password, 5)
  }
  // console.log('222', this.password)
})

const User = mongoose.model('User', userSchema)
export default User

4. watch.pug

extends base.pug

//- block head 
//-     title Watch | JmTube
block content
    div#videoContainer(data-id=video._id)
        video(src=video.fileUrl)
        div#videoControls.videoControls
            div.videoControls__play
                span#play.videoControls__playBtn
                    i.fas.fa-play
                div.videoControls__time
                    span#currenTime 00:00
                    span  / 
                    span#totalTime 00:00
            input(type="range",step="1", value="0", min="0")#timeline.videoControls__timeline
            div.videoControls__volume
                input(type="range",step="0.1", value=0.5, min="0", max="1")#volume
                span#mute
                    i.fas.fa-volume-up
            div
                span#fullScreen
                    i.fas.fa-expand
        
    div.video__data
        p.video__title=video.title
        small.video__owner Uploaded by
            a(href=`/users/${video.owner._id}`)=video.owner.name
        small.video__createdAt=new Date(video.createdAt).toLocaleDateString('ko-kr', {weekday:'long', year:'numeric', month:'long', day:'numeric'})
        if String(video.owner._id) === String(loggedInUser._id)
            a(href=`${video.id}/edit`) Edit Video →
            br
            a(href=`${video.id}/delete`) Delete Video →

---------loggedIn한 user만 댓글을 달 수 있게 한다.------
    if loggedIn 
        div.video__add-comments
            form.video__comment-form#commentForm
                textarea(col='100', rows='5', placeholder='Write a comment...')
                br
                button add Comment 
                ///댓글을 달 수 있는 form을 만든다.
    div
        ul.video__comments
            each comment in video.comments.reverse()
                li.video__comment(data-id=comment.id)
                    i.fas.fa-comment
                    span #{comment.text}
                    span ❌
                ///댓글이 뿌려지는 부분.
block scripts
    script(src="/static/js/videoPlayer.js") 
    if loggedIn
        script(src='/static/js/commentSection.js')
        ///사용될 js파일을 load해 주는데, loggedIn User에게만
        ///보이게 해야 error가 안난다.
        ///load하는 js는 compile된 assets폴더의 js를 불러준다.

5. src/client/js/commentSection.js

pug파일의 form에서 바로 server로 날리는 방법과
js파일로 argument를 날리는 방법을 잘 비교하면서 잘 봐둔다.

const videoContainer = document.getElementById('videoContainer')
const form = document.getElementById('commentForm')
const btn = form.querySelector('button')
const textarea = form.querySelector('textarea')
///videoContainer, form, btn, textarea를
///JS와 연결한다. 그리고 맨 아래에서 event를 등록한다.

///댓글을 realTime으로 만들어 주는것, 맨 나중에 확인하자.
const addComment = (text, id) => {
///handleSubmit함수 실행하면, 마지막에 addCommnet함수에,
///text와 newCommentId를 arg로 넣어서 실행시킴.
///text와 newCommentId를 text와 id로 받음

///여기서 부터의 작업은 댓글을 달고 나서, server에서 다시
///댓글을 load하는 것이 아니라, 화면에 바로 댓글을 그려주는
///작업임.
  const videoComments = document.querySelector('.video__comments')
  ///pug파일의 video__comments부분은 JS와 결합시킴.
  ///댓글이 뿌려지는 부분임.
  
  const newComment = document.createElement('li')
  ///newComment를 만드는데, li로 만들어줌.
  
  newComment.dataset.id = id;
  ///위의 newComment에 dataset으로 handleSubmit 함수에서
  ///보내준 id를 담아줌.
  
  newComment.className = 'video__comment'
  ///댓글 하나하나의 className을 pug파일의 video__comment와
  ///연결시켜줌.
  
  const icon = document.createElement('i')
  icon.className = 'fas fa-comment'
  ///pug파일의 icon과 연결함
  
  const span = document.createElement('span')
  span.innerText = ` ${text}`
  ///pug파일의 span의 text와 연결함.
  
  const span2 = document.createElement('span')
  span2.innerText = '✖'
  ///pug파일의 X 이모티콘과 연결해 줌.
  
  newComment.appendChild(icon)
  newComment.appendChild(span)
  newComment.appendChild(span2)
  ///위에서 만든것들을 차례차례 넣어줌
  //videoComments.appendChild(newComment) //댓글이 맨 밑에 생김.
  videoComments.prepend(newComment) //댓글이 맨위에 생김
  ///comment전체 박스에 개개의 comment를 넣어줌.
  
  !!!pug화면을 JS파일로 그려주는것이다..
  !!!JS를 복습할 수 있는 아주 좋은 기회다!!!
}

///form에서 add Comment버튼을 눌렀을때, 실행될 함수.
const handleSubmit = async (event) => {
  event.preventDefault()
  ///event발생시, default되는것 방지, 필수!!
  
  const text = textarea.value
  ///댓글내용을 받아온다.
  
  const videoId = videoContainer.dataset.id
  ///videoId를 pug파일의 videoContainer의 dataset으로
  ///videoId를 받아온다.
  
  console.log('videoId', videoId)

  if (text === '') {
    return
  }
  ///함수가 실행되고 나서는 댓글칸을 비워준다.

  const response = await fetch(`/api/videos/${videoId}/comment`, {
    method: 'POST',
    headers: {
      'Content-type': 'application/json',
    },
    body: JSON.stringify({ text }),
    //JS로 server에 data를 보내기 위해서는 이렇게 해야함.
    //req.body.text로 접근가능해짐.(서버에서)
    //middleware에서 express.json()도 해주어야함.
  })
  ///아래에서 만들 router와 videoController에서 만들
  ///createComment controller에 fetch를 날림,
  ///댓글 내용을 날려줌, 서버로!
  ///reponse로 받은 이유는 이후 처리를 위해서!
  ///response로 받아야 newCommentId 및, status
  ///를 받을 수 있다.
  
  if (response.status === 201) {
    const { newCommentId } = await response.json()
    addComment(text, newCommentId)
    textarea.value = ''
  }
  ///응답code가 201이면, response에서 받은, newCommentId와
  ///text를 argument로 주어서 맨 위에서 만든, addComment
  ///함수를 실행시킨다.
  
  //window.location.reload()
  ///자동적으로 f5키 새로고침이 작동하게 하는 명령어
  ///server의 노동을 줄이기 위해서 위의 addComment로
  ///realTime으로 댓글이 만들어지게 한다.
}

form.addEventListener('submit', handleSubmit)

6. src/routes/apiRouter.js

comment를 server에서 처리하는 부분.

import express from 'express'
import { createComment, registerView } from '../controllers/videoController'

const apiRouter = express.Router()

apiRouter.post('/videos/:id([0-9a-f]{24})/view', registerView)
apiRouter.post('/videos/:id([0-9a-f]{24})/comment', createComment)
///위와 같이 router를 만들어준다.
///post로 만들어 주고, controller는 videoController에 만들어준다.

export default apiRouter

7. src/controllers/videoController.js

watch.pug에서 commentSection.js에서 보낸 댓글을 server에서
처리하는 부분.

export const createComment = async (req, res) => {
  // console.log(req.body)
  // console.log(req.params)
  // console.log(req.session)
  // return res.end()
  ///위와 같은 코딩으로 server에서 client에서 보낸 req정보들을
  ///확인할 수 있다.
  const {
    session: { user },
    body: { text },
    params: { id },
  } = req

  const video = await Video.findById(id).populate('comments')
  ///router의 params로 id를 따 와서, video를 찾는다.
  ///populate로 반드시 comments를 같이 불러주어야한다.
  
  if (!video) {
    return res.sendStatus(404)
  }
  ///비디오없으면, sendStatus로 404를 날려준다.
  
  const comment = await Comment.create({
    text,
    owner: user._id,
    video: id,
  })
  video.comments.push(comment._id)
  video.save()
  console.log('comments', video.comments)
  console.log(video.comments.map((comment) => comment.createdAt))
  return res.status(201).json({ newCommentId: comment._id })
}
///Comment를 create한 후에, video.comments에 반드시 push를
///해 주고 save를 해줘야한다..꼭!!
///res를 날려줄때는 반드시 json으로 newCommentId에 새로 만들어진
/// comment의 id를 담아서 보내준다.

///   /videos/:id([0-9a-f]{24})/comment', 이 path로 req가
///  왔을때 처리되는 함수죠

8. server.js

import 'dotenv/config'
import './db'
import Video from './models/Video'
import User from './models/User'
import Comment from './models/Comment'
import flash from 'express-flash'
import express from 'express'
import session from 'express-session'
import MongoStore from 'connect-mongo'
import morgan from 'morgan'
import rootRouter from './routes/rootRouter'
import userRouter from './routes/userRouter'
import videoRouter from './routes/videoRouter'
import { localsMiddleware } from './middlewares'
import apiRouter from './routes/apiRouter'

const PORT = process.env.PORT || 4000
const app = express()
const logger = morgan('dev')

app.set('view engine', 'pug')
app.set('views', process.cwd() + '/src/views')
app.use(logger)
app.use(express.urlencoded({ extended: true }))
///pug파일의 form에서 보내는 data를 server가 받게 해줌.(req.body.. 등)
app.use(express.json())
////client에서 보내준 data(여기서는 댓글)을 알아보게
///받을려면 반드시 express.json()을 해주어야 한다.

app.use(
  session({
    secret: process.env.COOKIE_SECRET,
    resave: false, //true하면 req요청때마다, cookie만듬, false는 login떄만 만듬
    saveUninitialized: false,
    cookie: {
      maxAge: 200000000000,
    },
    store: MongoStore.create({ mongoUrl: process.env.DB_URL }),
  })
)

// app.use((req, res, next) => {
//   res.locals.sexy = 'you' //pug파일에서 sexy로 접근가능
//   res.locals.siteName = 'jmTube' //pug파일에서 siteName으로 접근가능
//   console.log(res)
//   res.sessionStore.all((error, sessions) => {
//     console.log(sessions)
//     next()
//   })
// })

app.use(flash())
app.use(localsMiddleware)
app.use('/uploads', express.static('uploads')) //'/uploads' path로 uploads폴더에 접근허락해조
app.use('/static', express.static('assets')) //'/assets' path로 assets폴더에 접근허락해조
app.use('/', rootRouter)
app.use('/videos', videoRouter)
app.use('/users', userRouter)
app.use('/api', apiRouter)

app.listen(PORT, () => console.log(`Server start in ${PORT}`))

!!!엄청 기나긴 작업이었지만 정리는 반드시 필요하다는것을
명심명심명심 할것@@@@@

profile
코딩하는초딩쌤

8개의 댓글

comment-user-thumbnail
2023년 2월 14일

이 기사는 매우 훌륭하고 도움이 됩니다. 기사에 인쇄된 정보가 독자에게 도움이 될 것이라고 생각합니다. 나는 그것을 공유해 주셔서 감사합니다. contexto 에서 새로운 게임도 할 수 있습니다.

답글 달기
comment-user-thumbnail
2023년 5월 17일

In this guide, we will take a closer look at car shipping in Diaz and provide you with some tips on how to choose the right car transport company. https://www.youtube.com/watch?v=I1RUbGHAAFc

답글 달기
comment-user-thumbnail
2023년 6월 23일

이 콘텐츠를 읽는 것은 즐겁습니다. 많은 도움과 즐거움을 받고 있습니다 drift boss

답글 달기
comment-user-thumbnail
2023년 7월 13일
답글 달기
comment-user-thumbnail
2023년 7월 13일
답글 달기
comment-user-thumbnail
2023년 7월 19일

Chennai Call Girls is one thing that many people crave. In the fast-paced modern-day life in Chennai, people are so busy as to lack time for fun and entertainment. However, nothing can be worse than that for maintaining sound physical and mental health.
https://www.sanakhan.in/chennai-call-girls.html

답글 달기
comment-user-thumbnail
2023년 10월 16일

We'll examine auto shipping in Diaz in more detail and offer some advice on selecting the best auto transport provider in this io games post.

답글 달기
comment-user-thumbnail
2024년 2월 19일

It assumes the use of MongoDB with Palworld Breeding Calculator Mongoose as the ODM (Object Data Modeling) library.

답글 달기