예제

채희태·2022년 8월 11일
0

axios를 이용한 client(react)-server(node) 통신방법을 예제를 통해 익혀보았다.
여러 예제를 통한 통신방법의 숙달을 목표로 하였다.
John ahn의 노드 / 리액트 시리즈를 공부하며 작성하였다.

💎들어가기 전

axios란

Promis API를 이용한 http 비동기 통신 라이브러리이다.

  • get => Axios.get('url')
  • post => Axios.post('url', data 객체)
    post의 경우 두 번째 인자로 서버로 보낼 data 객체 리터럴을 입력한다.

스키마와 모델

MongoDB에서는 데이터가 차곡차곡 쌓일 때, 그 데이터 하나하나를 document라고 한다.

  • 스키마 : 해당 컬렉션의 document에 어떤 타입의 데이터가 들어갈지 정의하는 것.
  • 모델 : 스키마를 통해 인스턴스 객체를 만들어 이를 활용해 DB에서 실제 작업을 할 수 있게 해줌.

스키마 정의 옵션

스키마를 정의할 때 다음과 같이 옵션을 줄 수 있다.

  • timestamp => createdAt, updatedAt 필드를 자동 생성한다.
const schema = mongoose.Schema({ name: String }, { timestamp: true })
  • id => default로 _id 필드를 자동 생성하는데, 이를 취소할 수 있다.
const schema = mongoose.Schema({ name: String }, { id: false })

mongoDB relation

MongoDB 스키마를 만들다 보면, 다른 다큐먼트의 ObjectID를 쓰는 경우가 있다.

writer: {
  type: Schema.Types.ObjectId,
  ref: 'User',
}

다른 document들 끼리 서로 참조할 수 있도록 type옵션을 정의하고, ref옵션을 통해 참조할 모델을 정의한다.
위의 경우 User모델의 document를 참조할 수 있다.

.populate('writer')

populate 메소드를 이용해 'User' 도큐먼트를 가져올 수 있다.

mongoDB 메소드

  • exec
    • exec메서드를 통해 온전한 프로미스를 반환할 수 있다.
      에러가 발생했을 때 추적이 가능하므로 사용하는 것이 좋다.
  • find
    • .find() 모두 찾음.
    • .find({ name: 'kim' }) 해당 키 값을 가진 도큐먼트를 찾음.
    • .find({ $in: name }) name배열에 담긴 모든 값을 가진 도큐먼트를 찾음.

useParamas

router의 파라미터 정보를 가져와 사용하고 싶을 때 useParams 훅을 사용한다.

사용법
npm i react-router-dom 로 react-router-dom 설치

  • 경로가 URL/pathname/1 일때, 1이 params이다.
import { useParmas } from 'react-router-dom'
const { params } = useParams()
console.log(parmas) 

params 1이 출력된다.

localStorage

브라우저에 특정 데이터를 저장하기 위해 사용하는 웹 스토리지의 종류이다.
localStorage는 sessionStorage와 다르게 브라우저를 닫고, 새탭을 열어도 데이터가 그대로 남아있는 특징(영속성)이 있다.

//키, 값 데이터 저장
localStorage.setItem('key', 'value')
//키, 값 데이터 불러오기
localStorage.getItem('key')

e.preventDefault()

e.preventDefault()는 두 가지 기능이 있다.

  • a태그를 눌렀을 때, 새 창으로 넘어가지 않게 해준다.
  • submit버튼을 눌렀을 때, submit은 작동되지만 새로고침 되지 않게 해준다.
const onSubmit = e => e.preventDefault()

🪢 비디오 데이터

비디오를 웹에 업로드 하기 위해 비디오 데이터를 다룬다.

비디오 데이터 모델링

비디오 데이터를 mongoDB에 저장하기 위해 Video 모델을 생성한다.
writer에서 User(사용자 정보) 모델의 도큐먼트를 참조할 수 있도록 relation을 걸어준다.

server/models/video

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const videoSchema = mongoose.Schema({
  //writer 필드로 해당 User 모델의 다큐먼트를 참조할 수 있게 relation을 걸어준다.
  writer: {
    type: Schema.Types.ObjectId,
    ref: 'User'
  },
  title: String,
  description: String,
  filePath: String,
  category: String,
  views: {
    type: String,
    default: 0,
  }
  duration: String,
  thumbnail: String
}, { timestamps: true })

const Video = mongose.model('Video', videoSchema)

module.exports = { Video }

비디오 데이터 저장

client에서 axios를 통해 비디오 데이터를 전송한다.
localStorage에 저장된 userId 데이터를 getItem으로 불러온다.

client

import React, { userState } from 'react'
import { Axios } from 'axios'

const onSubmit = (e) => {
  //새로고침을 막아줌.
  e.preventDefault()
  
  const writer = localStorage.getItem('userId')
  const variables = {
    writer,
    //useState에 저장된 데이터들을 넣어준다.
    title,
    description,
    filePath,
    category,
    duration,
    thumbnail,
  }
  
  Axios.post('/api/video/uploadVideo', variables)
    .then(res => {
      if(res.body.success) {
        console.log(res.data.video)
      } else {
        console.log(res.data.err)
      }
    })
}

app.js에서 경로를 분기시켜준다.

server/app.js

app.use('/api/video', require('./routes/video'))

save() 함수를 이용하여 데이터를 mongoDB에 저장한다.

server/routes/video.js

import express from 'express'
const router = express.Router()

//Video 모델 불러오기.
import { Video } from '../models/Video'

router.post('/uploadVideo', (req, res, err) => {
  //새로운 도큐먼트 생성.
  const video = new Video(req.body)
  video.save((err, video) => {
    if(err) return res.json({ success: false, err })
    res.status(200).json({ success: true, video })
  }) 
})

비디오 데이터 불러오기

get http 메서드를 사용해 DB에 저장시킨 비디오 데이터를 불러온다.

client

Axios.get('/api/video/getVideo')
  .then(res => {
    if(res.data.success) {
      console.log(res.data.video)
    } else {
      console.log(res.data.err)
    }
  })

저장시킨 모든 비디오 도큐먼트 데이터들에 유저 도큐먼트를 참조하여 불러온 뒤 client에 보내준다.

server/routes/video.js

router.get('/getVideo', (res, req) => {
  Video
    //Video컬렉션의 전체 다큐먼트를 찾는다.
    .find()
    //User컬렉션의 다큐먼트를 참조한다.
    .populate('writer')
    .exec((err, videos) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, videos })
    })
  })

🥎비디오 디테일

비디오 디테일 불러오기.

videoId는 Video 모델에서 필드가 "_id"로 저장된 부분이며, params이다.

URL/pathname/:videoId
client

import { Axios } from 'axios'
import { useParams } from 'react-router-dom'

const { videoId } = useParams()

useEffect(() => {
  const variable = { videoId }
  Axios.post('/api/video/detailVideo', variable)
    .then(res => {
      if(res.data.success) {
        console.log(res.data.detailVideo)
      } else {
      	console.log(res.data.err)
      }
    })
}, [])

해당 Video 도큐먼트의 .populate('writer')를 사용해 User document를 참조형 불러온다.

server/api/routes/video.js

router.post('/detailVideo', (req, res) => {
  Video
    .findOne({ "_id": req.body.videoId })
    .populate('writer')
    .exec((err, detailVideo) => {
      if(err) return res.status(400).json({ success: false, err })
      res.satus(200).json({ success: true, detailVideo })
    })
})

🥨 구독기능

구독기능 모델링

구독기능 데이터를 mongoDB에 저장하기 위해 Subscribe 모델을 생성한다.
userTo와 userFrom에서 User 모델을 참조할 수 있도록 relation을 걸어준다.
userTo는 비디오를 올린 유저의 Id이고, useFrom은 내 Id이다.

server/models/subscribe.js

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const subscribeShema = mongoose.Schema({
  userTo: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
  userFrom: {
    type: Schema.types.ObjectId,
    ref: 'User'
  }
})

const Subscribe = mongoose.model('Subscribe', subscribeShema)

module.exports = { Subscribe }

구독 신청/취소

localStorage에 저장된 userId 데이터를 userFrom(내 Id)에 담아준다.
isSubscribe가 true면 구독을 취소하고, false면 구독을 신청을한다.

client

import React, { useState } from 'react'
import { Axios } from 'axios'

const { videoId } = useParams()

const [ isSubscribe, setIsSubscribe ] = useState(null)

const onSubscribe = () => {
  const userTo = videoId
  const userFrom = localStorage.getItem('userId')
  const variables = { userTo, userFrom }
  
  //구독 신청
  if(!subscribed) {
    Axios.post('/api/subscribe/regSubscribe', variables)
      .then(res => {
        if(res.data.success) {
          setIsSubscribe(true)
          console.log(res.data.subscribe)
        } else {
          console.log(res.data.err)
        }
      })
    
  //구독 취소
  } else {
    Axios.post('/api/subscribe/delSubscribe', variables)
      .then(res => {
        if(res.data.success) {
          setIsSubscribe(false)
          console.log(res.data.subscribe)
        } else {
          console.log(res.data)
        }
    }
  }
}  

app.js에서 routes해준다.

server/app.js

app.use('/api/subscribe', require('./routes/subscribe'))

server/routes/subscribe.js

//구독 신청
router.post('/regSubscribe', (req, res) => {
  const subscribe = new Subscribe(req.body)
  subscribe
    .save((err, subscribe) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, subscribe })
    })
})

//구독 취소
router.post('/delSubscribe', (req, res) => {
  Subscribe
    .findOneAndDelete({ "userTo": req.body.userTo, "userFrom": req.body.userFrom })
    .exec((err, subscribe) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, subscribe })
    })
})

구독자 수

userTo(구독 당한 유저의 _id) 데이터 갯수를 통해 구독자 수를 가져온다.

client

import React, { useState, useEffect } from 'react'
import { useParams } from 'react-router-dom'
import { Axios } from 'axios'

const { videoId } = useParams()

//구독자 수를 담을 state
const [ numSubscribe, setNumSubscribe ] = useState(0)

useEffect(() => {
  const variable = { userTo: videoId }
  Axios.post('api/subscribe/numSubscribe', variable)
    .then(res => {
      if(res.data.success) {
        setNumSubscribe(res.data.numSubscribe)
      } else {
        console.log(res.data.err)
      }
    })
}, [])

server/routes/subscribe.js

import { Subscribe } from '../models/subscribe'

router.post('/numSubscribe', (req, res) => {
  Subscribe
    .find({ userTo: req.body.userTo })
    .exec((err, subscribes) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, numSubscribe: subscribes.length })
    })
})

구독여부

userTo(구독 당한 유저의 _id)와 userFrom(내 _id)데이터를 함께 조회해 구독여부를 판단한다.

client

import { useParams } from 'react-router-dom'
import { Axios } from 'axios'

const { videoId } = useParams()

const [ isSubscribe, setIsSubscribe ] = useState(null)

useEffect(() => {
  const userTo = videoId
  const useFrom = localStorage.getItem('useId')
  const variables = { userTo, userFrom }
  Axios.post('/api/subscribe/isSubscribe', variables)
    .then(res => {
      if(res.data.success) {
        setIsSubscribe(res.data.isSubscribe)
      } else {
        console.log(res.data)
      }
    })
}, [])

server/routes/subscribe.js

import { Subscribe } from '../models/subscribe'

router.post('/isSubscibe', (req, res) => {
  Subscribe
    .findOne({ userTo: req.body.userTo, userFrom: req.body.userFrom })
    .exec((err, subscribe) => {
      if(err) return res.status(400).json({ success: false, err })
      let isSubscribe = false
      if(subscribe.length !== 0) {
        isSubscribe = true
        res.status(200).json({ success: true, isSubscribe })
      } else {
        res.status(200).json({ success: true, isSubscribe })
      }
    }
  })

유저의 구독한 비디오 불러오기

유저의 구독한 Video document 데이터들을 불러온다.

userFrom으로 내가 구독한 비디오를 불러온다.

client

import React, { useEffect } from 'react'
import { Axios } from 'axios'

const [ videos, setVideos ] = useState([])

useEffect(() => {
  const userFrom = localStorage.getItem('userId')
  const variable = { userFrom }
  Axios.post('api/subscribe/getSubVideos', variable)
    .then(res => {
      if(res.data.success) {
        setVideos(res.data.videos)
      } else {
        console.log(res.data)
      }
    })
})

populate 메서드로 writer로 참조하고 있는 Video컬렉션의 document들을 불러올 수 있다.

server/routes/subscribe

router.post('/getSubVideos', (req, res) => {
  //pulisher배열에 useTo 필드를 담아준다.
  let publisher = []
  
  Subscribe
    .find({ "userFrom": req.body.userFrom })
    .exec((err, subscribes) => {
      if(err) res.status(400).json({ success: false, err })
      subscribes.map(subscribe => {
        publisher.push(subscribe.userTo)
      })
    })
  
  Video
    //Video모델의 writer 필드가 userTo 값을 담은 배열과 같은 도큐먼트를 찾는다.
    .find({ "writer": { $in: publisher } })
    //User모델의 도큐먼트를 참조한다.
    .populate('writer')
    .exec((err, videos) => { 
      if(err) res.status(400).json({ success: true, err })
      res.status(200).json({ success: true, videos })
    })
})

🪄 댓글기능

댓글기능 데이터 모델링

댓글기능 데이터를 mongoDB에 저장하기 위해 Comment 모델을 생성한다.
writer는 댓글을 쓴 사람의 User 도큐먼트를 참조한다.
videoId는 비디오를 생성한 사람의 User 도큐먼트를 참조한다.

server/models/comment.js

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const commentSchema = mongoose.Schema({
  writer: {
    type: Shema.types.ObjectId,
    ref: 'User'
  },
  videoId: {
    type: Shema.types.ObjectId,
    ref: 'User'
  },
  comment: String
})

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

module.exports = { Comment }

댓글 쓰기

userId와 videoId를 서버로 보내 댓글을 저장한다.

client

const React from 'react'
const { useParams } from 'react-router-dom'

const writer = localStorage.getItem('userId')
const { videoId } = useParams() 

const onSubmit = () => {
  const variables = { writer, videoId, comment }
  Axios.post('/api/comment/regComment', variables)
    .then(res => {
      if(res.data.success) {
        console.log(res.data.comment)
      } else {
        console.log(res.data.err)
      }
    })
} 

app.js에서 routes해준다.

server/app.js

app.use('/api/comment', require('/routes/comment'))

생성한 comment 도큐먼트에 User 도큐먼트를 참조하여 보내준다.

server/routes/comment.js

import express from 'express'
const router = express.Router()

import { Comment } from '../models/comment'

router.post('/regComment', (req, res) => {
  const comment = new Comment(req.body)
  comment.save((err, comment) => {
    if(err) return res.status(400).json({ success: false, err })
    Comment
      .find({ "_id": comment._id })
      //생성한 comment 도큐먼트의 User 도큐먼트를 참조한다.
      .populate('writer')
      .exec((err, comment) => {
        if(err) return res.status(400).json({ success: false, err })
        res.status(200).json({ success: true, comment })
      })
  })
})

🎀 좋아요, 싫어요 기능

userId, videoId, commentId 필드를 만든다.
userId는 내 Id 정보이다.
videoId가 존재할 땐 해당 비디오에 대한 좋아요 기능을,
commentId가 존재할 땐 해당 코멘트에 대한 좋아요 기능을 만든다.

좋아요, 싫어요 기능 데이터 모델링

좋아요, 싫어요 기능 데이터를 DB에 저장하기 위해 Like, Dislike모델을 생성한다.

server/models/like.js

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const likeSchema = mongoose.Schema({
  userId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
    videoId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
    commentId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
})

const Like = mongoose.model('Like', likeSchema)

exports.module = { Like }

server/models/Dislike.js

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const dislikeSchema = mongoose.Schema({
  userId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
    videoId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
    commentId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
})

const Dislike = mongoose.model('Dislike', dislikeSchema)

exports.module = { Dislike }

좋아요, 싫어요 데이터 가져오기

비디오에 대한 좋아요, 싫어요 기능 과 코멘트에 대한 좋아요, 싫어요 기능을 나누기

  • DB에 좋아요, 싫어요 갯수가 몇개인지
  • 좋아요, 싫어요 중 하나를 이미 내가 눌렀는지에 대한 데이터를 가져온다.

client

const LikeAndDislike = (props) => {
  //생략...
  
  const [numLike, setNumLike] = useState(0)
  const [isLiked, setIsLiked] = useState(false)
  const [numDislike, setNumDislike] = useState(0)
  const [isDisliked, setIsDisliked] = useState(false)
 
  
  useEffect(() => {
    const userId = localStorage.getItem('userId')
    
    //비디오와 코멘트의 좋아요, 싫어요 데이터 나누기
    let variable = {}
    if(props.videoId) {
      variables = { videoId: props.videoId }
    } else if(props.commentId) {
      variables = { commentId: props.commentId }
    }
    
    //좋아요 관련 데이터 불러오기
    Axios.post('/api/like/getLike', variables)
      .then(res => {
        if(res.data.success) {
          const { likes } = res.data
          //좋아요 갯수
          setNumLike(likes.length)
          //좋아요를 눌렀는지 여부
          likes.forEach(like => {
            if(like.userId === userId) return setIsLiked(true)
          })
        } else {
          console.log(res.data)
        }
      })
    
    //싫어요 관련 데이터
    Axios.post('/api/like/getDislike', variables)
      .then(res => {
        if(res.data.success) {
          const { dislikes } = res.data
          //싫어요 갯수
          setNumDislike(dislikes.length)
          //싫어요를 눌렀는지 여부
          dislikes.forEach(dislike => {
            if(dislike.userId === userId) return setIsDisliked(true)
          })
        } else {
          console.log(res.data)
        }
      })
  },[])
}

server/app.js

app.use('/app/like', require('./routes/like')

server/routes/like.js

import { Like } from '../../models/Like'
import { Dislike } from '../../models/Dislike'

router.post('/getLike', (req, res) => {
  //비디오와 코멘트의 좋아요 데이터 나누기
  let variable = {}
  if(req.body.videoId) {
    variable = { videoId: req.body.videoId }
  } else if(req.body.commentId) {
    variable = { commentId: req.body.commentId }
  }
  //좋아요 데이터 보내기
  Like
    .find(variable)
    .exec((err, likes) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, likes })
    }) 
})

router.post('/getDislike', (req, res) => {
  //비디오와 코멘트의 좋아요 데이터 나누기
  let variable = { }
  if(req.body.videoId) {
    variable = { videoId: req.body.videoId }
  } else if(req.body.commentId) {
    variable = { commentId: req.body.commentId }
  }
  //싫어요 데이터 보내기
  Dislike
    .find(variable)
    .exec((err, dislikes) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, dislikes })
    }) 
})

좋아요, 싫어요 누르기

비디오에 대한 좋아요, 싫어요 기능 과 코멘트에 대한 좋아요, 싫어요 기능을 나누기

  • 좋아요가 되어있을 때, 싫어요를 누르면 => 좋아요 취소, 싫어요 체크
  • 싫어요가 되어있을 때, 좋아요를 누르면 => 싫어요 취소, 좋아요 체크
  • 좋아요가 되어있을 때, 좋아요를 누르면 좋아요 취소
  • 싫어요가 되어있을 때, 싫어요를 누르면 싫어요 취소

client

//좋아요 누르기
const onClickLike = () => {
  //비디오와 코멘트의 좋아요 데이터 나누기
  let variables = {}
  if(props.videoId) {
    variables = { userId, videoId: props.videoId }
  } else if(props.commentId) {
    variables = { userId, commentId: props.commentId }
  }
  
  //좋아요가 안눌려져 있을 때 좋아요 데이터 등록
  if(isLiked === false) {
    Axios.post('/api/like/regLike', variables)
    .then(res => {
      if(res.data.success) {
        //좋아요 여부를 true로, 좋아요 갯수를 1 추가.
        setIsLiked(true)
        setNumLike(numLike + 1)
        if(isDisliked === true) {
          setIsDisliked(false)
          setNumDislike(numDislike - 1)
        }
      } else {
        console.log(res.data)
      }
    })
  }
  
  //좋아요가 눌려져 있을 때 좋아요 데이터 삭제
  if(isLiked === true) {
    Axios.post('/api/like/delLike', variables)
    .then(res => {
      if(res.data.success) {
        //좋아요 여부를 false로, 좋아요 갯수를 1 감소.
        setIsLiked(false)
        setNumLike(numLike - 1)
      } else {
        console.log(res.data)
      }
    })
  }
}

//싫어요 누르기
const onClickDislike = () => {
  //비디오와 코멘트의 싫어요 데이터 나누기
  let variables = {}
  if(props.videoId) {
    variables = { userId, videoId: props.videoId }
  } else if(props.commentId) {
    variables = { userId, commentId: props.commentId }
  }
  
  //싫어요가 안눌려져 있을 때 싫어요 데이터 등록
  if(isDisliked === false) {
    Axios.post('/api/like/regDislike', variables)
    .then(res => {
      if(res.data.success) {
        //좋아요 여부를 true로, 좋아요 갯수를 1 추가.
        setIsDisliked(true)
        setNumDislike(numDislike + 1)
        if(isLiked === true) {
          setIsLiked(false)
          setNumLike(numLike - 1)
        }
      } else {
        console.log(res.data)
      }
    })
  }
  
  //싫어요가 눌려져 있을 때 싫어요 데이터 삭제
  if(isDisliked === true) {
    Axios.post('/api/like/delDislike', variables)
    .then(res => {
      if(res.data.success) {
        //좋아요 여부를 false로, 좋아요 갯수를 1 감소.
        setIsDisliked(false)
        setNumDislike(numDislike - 1)
      } else {
        console.log(res.data)
      }
    })
  }
}

server/routes/like.js

import { Like } from '../../models/Like'
import { Dislike } from '../../models/Dislike'

//좋아요 데이터 등록
router.post('/regLike', (req, res) => {
  //비디오와 코멘트의 좋아요 데이터 나누기
  let variables = {}
  if(req.body.videoId) {
    variables = { userId: req.body.userId, videoId: req.body.videoId }
  } else if(req.body.commentId) {
    variables = { userId: req.body.userId, commentId: req.body.commentId }
  }
  //좋아요 데이터 저장
  const like = new Like(variables)
  like
    .save(err => {
      if(err) return res.status(400).json({ success: false, err })
      //싫어요 데이터가 있다면 삭제함.
      Dislike
        .findByIdAndDelete(variables)
        .exec(err => {
          if(err) return res.status(400).json({ success: false, err })
          res.status(200).json({ success: true })
        })
    })
})

//좋아요 데이터 삭제
router.post('/delLike', (req, res) => {
  //비디오와 코멘트의 좋아요 데이터 나누기
  let variables = {}
  if(req.body.videoId) {
    variables = { userId: req.body.userId, videoId: req.body.videoId }
  } else if(req.body.commentId) {
    variables = { userId: req.body.userId, commentId: req.body.commentId }
  }
  //좋아요 데이터 삭제
  Dislike
    .findByIdAndDelete(variables)
    .exec((err, dislike) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true })
    })
})


//싫어요 데이터 등록
router.post('/regDislike', (req, res) => {
  //비디오와 코멘트의 싫어요 데이터 나누기
  let variables = {}
  if(req.body.videoId) {
    variables = { userId: req.body.userId, videoId: req.body.videoId }
  } else if(req.body.commentId) {
    variables = { userId: req.body.userId, commentId: req.body.commentId }
  }
  //싫어요 데이터 저장
  const dislike = new Dislike(variables)
  dislike
    .save(err => {
      if(err) return res.status(400).json({ success: false, err })
      //좋아요 데이터가 있다면 삭제함.
      Like
        .findByIdAndDelete(variables)
        .exec(err => {
          if(err) return res.status(400).json({ success: false, err })
          res.status(200).json({ success: true })
        })
    })
})

//싫어요 데이터 삭제
router.post('/delDislike', (req, res) => {
  //비디오와 코멘트의 좋아요 데이터 나누기
  let variables = {}
  if(req.body.videoId) {
    variables = { userId: req.body.userId, videoId: req.body.videoId }
  } else if(req.body.commentId) {
    variables = { userId: req.body.userId, commentId: req.body.commentId }
  }
  //싫어요 데이터 삭제
  Dislike
    .findByIdAndDelete(variables)
    .exec((err, dislike) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true })
    })
})

🛒 찜하기 기능

찜하기 기능을 찜하기 버튼이 눌려져 있을 때와 아닐 때를 나눠서 구현한다.

  • 찜하기 버튼이 안 눌려져 있을 때 찜하기 버튼 클릭 => 해당 데이터 찜하기 등록
  • 찜하기 버튼이 눌려져 있을 때 찜하기 버튼 클릭 => 해당 데이터 찜하기 삭제

찜하기 기능 데이터 모델링

찜하기 기능 데이터를 DB에 저장하기 위해 Favorite모델을 생성한다.

server/models/favorite.js

import mongoose from 'mongoose'
const Schema = mongoose.Schema

const favoriteSchema = mongoose.Schema({
  userId: {
    type: Schema.types.ObjectId,
    ref: 'User'
  },
  movieId: {
    type: Schema.types.ObjectId,
    ref: 'Movie'
  }
}, { timeStamp: true })

const Favorite = mongoose.model('favorite', favoriteSchema)

module.exports = { Favorite }

찜하기 관련 데이터 가져오기

  • DB에 해당 무비의 찜하기 갯수가 몇개인지에 대한 데이터 가져오기.
  • 찜하기를 이미 내가 눌렀는지에 대한 데이터 가져오기.

client

//해당 데이터를 State에 저장한다.
import React, { useEffect, useState } from 'react'
import { Axios } from 'axios'
import { useParams } from 'react-router-dom'

const [numFavorite, setNumFavorite] = useState(0)
const [isFavorite, setIsFavorite] = useState(null)

useEffect(() => {
  const userId = localStorage.getItem({ "userId" })
  const { movieId } = useParamas()
  const variables = { userId, movieId }
  
  //해당 무비의 찜하기 갯수 데이터 가져와 state에 저장한다.
  Axios.post('/api/favorite/numFavorite', variables)
    .then(res => {
      if(res.data.success) {
        setNumFavorite(res.data.numFavorite)
      } else {
        console.log(res.data)
      }
    })
  
  //해당 무비를 내가 이미 찜하기 했는지에 대한 데이터를 가져와 state에 저장한다.
  Axios.post('/api/favorite/isFavorite', variables) 
    .then(res => {
      if(res.data.success) {
        setIsFavorite(res.data.isFavorite)
      } else {
        console.log(res.data)
      }
    })

}, [])

app.js에서 routes해준다.

server/app.js

app.use('/api/favorite', require('./routes/favorite'))

server/routes/favorite.js

import express from 'express'
const router = express.Router()

//해당 무비의 찜하기 갯수를 보내줌.
router.post('/numFavorite', (req, res) => {
  Favorite
    .find({ "movieId": req.body.movieId })
    .exec((err, favorites) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, numFavorite: favorites.length })
    })
})

//해당 무비의 찜 여부를 보내줌.
router.post('/isFavorite', (req, res) => {
  Favorite
    //userId와 movieId를 동시에 가지고 있는 document의 여부
    .find({ "userId": req.body.userId, "movieId": req.body.movieId })
    .exec((err, favorite) => {
      if(err) return res.status(400).json({ success: false, err })
      let isFavorite = false
      if(favorite.length !== 0) {
        isFavorite = true
        res.status(200).json({ success: true, isFavorite })
      } else {
        res.status(200).json({ success: true, isFavorite })
      }
    })

})

찜하기 추가/삭제

  • 찜하기 버튼이 안 눌려져 있을 때 찜하기 버튼 클릭 => 해당 데이터 찜하기 등록
  • 찜하기 버튼이 눌려져 있을 때 찜하기 버튼 클릭 => 해당 데이터 찜하기 삭제

client

const [numFavorite, setNumFavorite] = useState(0)
const [isFavorite, setIsFavorite] = useState(null)

const onClickFavorite = () => {
  const userId = localStorage.getItem({ userId })
  const { movieId } = useParmas()
  const variables = { userId, movieId }
  
  //찜하기 버튼이 안눌려져 있을 때 => 찜하기 등록
  if(!isFavorite) {
    Axios.post('/api/favorite/regFavorite', variables)
      .then(res => {
        if(res.data.success) {
          setIsFavorite(true)
          setNumFavorite(numFavorite + 1)
        } else {
          console.log(res.data)
        }
      })
    
  //찜하기 버튼이 눌려져 있을 때 => 찜하기 삭제
  } else {
    Axios.post('/api/favorite/delFavorite', variables)
      .then(res => {
          setIsFavorite(false)
          setNumFavorite(numFavorite - 1)    
      })
  }
}

server/routes/favorite.js

//Favorite컬렉션에 해당 DB document등록 
router.post('/regFavorite', (req, res) => {
  const favorite = new Favorite(req.body)
  fovorite
    .save(err => {
      if(err) return res.status(400).json({ success: false, err })
    })
})

//Favorite컬렉션에 해당 DB document삭제
router.post('/delFavorite', (req, res) => {
  Favorite
    .findOneAndDelete({ 
      "userId": req.body.userId, "movieId": req.body.movieId 
    })
    .exec(err => {
      if(err) return res.status(400).json({ success: false, err })
    })
})

찜한 무비 데이터 가져오기

내(userId)가 찜한 무비 데이터를 가져와서 상태 값(state)에 넣어준다.

client

const [favoriteData, setFavoriteData] = useState([])

useEffect(() => {
  const userId = localStorage.getItem('userId')
  const variable = { userId }
  Axios.post('/api/favorite/getFavorite', variable)
    .then(res => {
      if(res.data.success) {
        setFavoriteData(res.data.favoriteData)  
      } else {
        console.log(res.data)
      }
    })
}, [])

server/routes/favorite.js

//populate("movieId")로 해당 userId document 필드에 저장된 movieId의 Movie document를 불러온다.
router.post('/getFavorite', (req, res) => {
  Favorite
    .find({ "userId": req.body.userId })
    .populate("movieId")
    .exec((err, favoriteData) => {
      if(err) return res.status(400).json({ success: false, err })
      res.status(200).json({ success: true, favoriteData })
    })
})
profile
기록, 공부, 활용

0개의 댓글