인프런 ==> https://www.inflearn.com/course/따라하며-배우는-노드-리액트-영화사이트-만들기#reviews
유튜브 강의 목록 : https://www.youtube.com/playlist?list=PL9a7QRYt5fqkowXUgTj_tbkFClsPhO5XV
강의 파일 : https://braverokmc79.github.io/react_movie_clone/movie_clone.html
Boiler Plate 소스 :
https://github.com/jaewonhimnae/boilerplate-mern-stack
https://github.com/braverokmc79/react_boiler_plate
완성본 소스 (John Ahn) : https://github.com/jaewonhimnae/react-movie-app-ko
소스 : https://github.com/braverokmc79/react_movie_clone
API 문서 : https://developers.themoviedb.org/3/search/search-movies
강의 목록
1. 소개 영상
2. Boiler-Plate & MongoDB 연결
3. The MovieDB API 설명
4. Landing Page 만들기 (1)
5. Grid Card Component
6. Load More Button 만들기
7. Movie Detail 페이지 만들기
8. 영화 출연진들 가져오기
9. Favorite 버튼 만들기 (1)
10. Favorite 버튼 만들기 (2)
11. Favorite Button 만들기 (3)
12. Favorite list에 추가 삭제
13. Favorite 페이지 만들기 (1)
14. Favorite 페이지 (2)
15. 강의 마무리
vscode 확장 패키지 추가
1. - Auto Import - ES6, TS, JSX, TSX
2. - Reactjs code snippets
3. - ESLint
4. - Prettier - Code formatter
Visual Studio Code 폴더/파일 아이콘 변경하기
리액트 프로젝트 생성
npx create-react-app 경로
예) npx create-react-app E:\react-app2
넥스트 프로젝트 생성
$ npx create-next-app nextjs-tutorial
9. Favorite 버튼 만들기 (1)
1) Nodejs
Favorite.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const favoriteSchema = Schema({ userFrom: { type: Schema.Types.ObjectId, ref: 'User' }, movieId: { type: String }, movieTitle: { type: String }, moviePost: { type: String }, movieRunTime: { type: String } }, { timestamps: true }); const Favorite = mongoose.model('Favorite', favoriteSchema); module.exports = { Favorite }
2) Reactjs
MovieDetail.js
~ {/* Body */} <div style={{ width: '85%', margin: '1rem auto' }}> <div style={{ display: 'flex', justifyContent: 'flex-end' }} > <Favorite movieInfo={Movie} movieId={movieId} userFrom={localStorage.getItem("userId")} /> </div> ~
src\components\views\MovieDetail\Sections\Favorite.js
import React, { useEffect } from 'react' import { Button } from 'antd'; import Axios from 'axios'; function Favorite(props) { const movieId = props.movieId; const userFrom = props.userFrom; const movieTitle = props.movieInfo.title; const moviePost = props.movieInfo.backdrop_path; const movieRunTime = props.movieInfo.runtime; useEffect(() => { let variables = { userFrom: userFrom, movieId: movieId, movieTitle: movieTitle, moviePost: moviePost, movieRunTime: movieRunTime } console.log("variables : ", variables); Axios.post('/api/favorite/favoriteNumber', variables) .then(res => { if (res.data.success) { console.log("res : ", res.data); } else { alert("숫자 정보를 가져오는데 실패 했습니다."); } }); }, []); return ( <div> <Button>Favorite</Button> </div> ) } export default Favorite
10. Favorite 버튼 만들기 (2)
1) Nodejs
index.js
app.use("/api/favorite", require("./routes/favorite"));
route/favorite.js
const express = require('express'); const router = express.Router(); const { Favorite } = require("../models/Favorite"); //'좋아요' 숫자 갯수 가져오기 router.post("/favoriteNumber", (req, res) => { //1.mongoDD 에서 movieId 에 대한 정보를 가져오기 Favorite.find({ "movieId": req.body.movieId }) .exec((err, info) => { if (err) return res.status(400).send(err); //2.좋아요를한 유저정보 길이 반환 res.status(200).json({ success: true, favoriteNumber: info.length }); }) }) module.exports = router;
2) React.js
Favorite.js
import React, { useEffect } from 'react' import { Button } from 'antd'; import Axios from 'axios'; function Favorite(props) { const movieId = props.movieId; const userFrom = props.userFrom; const movieTitle = props.movieInfo.title; const moviePost = props.movieInfo.backdrop_path; const movieRunTime = props.movieInfo.runtime; useEffect(() => { let variables = { // userFrom: userFrom, movieId: movieId // movieTitle: movieTitle, // moviePost: moviePost, // movieRunTime: movieRunTime } Axios.post('/api/favorite/favoriteNumber', variables) .then(res => { if (res.data.success) { console.log("res.data.favoriteNumber : ", res.data.favoriteNumber); } else { alert("숫자 정보를 가져오는데 실패 했습니다."); } }); }, []); return ( <div> <Button>Favorite</Button> </div> ) } export default Favorite
11. Favorite Button 만들기 (3)
1) Nodejs
favorite.js
//내가 좋아를 했는지 여부 가져오기 router.post("/favorited", (req, res) => { //1.내가 이 영화를 Favorite 리스트에 넣었는지 정보를 DB 에서 가져오기 Favorite.find({ "movieId": req.body.movieId, "userFrom": req.body.userFrom }) .exec((err, info) => { if (err) return res.status(400).send(err); let result = false; if (info.length !== 0) { result = true; } res.status(200).json({ success: true, favorited: result }); }); });
2) React.js
Favorite.js
import React, { useEffect, useState } from 'react' import { Button } from 'antd'; import Axios from 'axios'; function Favorite(props) { const movieId = props.movieId; const userFrom = props.userFrom; const movieTitle = props.movieInfo.title; const moviePost = props.movieInfo.backdrop_path; const movieRunTime = props.movieInfo.runtime; const [FavoriteNumber, setFavoriteNumber] = useState(0); const [Favorited, setFavorited] = useState(false); useEffect(() => { let variables = { userFrom: userFrom, movieId: movieId // movieTitle: movieTitle, // moviePost: moviePost, // movieRunTime: movieRunTime } Axios.post('/api/favorite/favoriteNumber', variables) .then(res => { if (res.data.success) { console.log("res.data.favoriteNumber : ", res.data.favoriteNumber); setFavoriteNumber(res.data.favoriteNumber); } else { alert("숫자 정보를 가져오는데 실패 했습니다."); } }); Axios.post('/api/favorite/favorited', variables) .then(res => { if (res.data.success) { console.log("res.data.favorited : ", res.data.favorited); setFavorited(res.data.favorited); } else { alert("정보를 가져오는데 실패 했습니다."); } }); }, []); return ( <div> <Button>{Favorited ? "Not Favorite" : "Add to Favorite"} {FavoriteNumber}</Button> </div> ) } export default Favorite
12. Favorite list에 추가 삭제
1) Nodejs
favorite.js
~ //좋아요 추가 router.post("/addToFavorite", (req, res) => { const favorite = new Favorite(req.body); favorite.save((err, doc) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true }) }) }); //좋아요 삭제 router.post("/removeFromFavorite", (req, res) => { Favorite.findOneAndDelete({ movieId: req.body.movieId, userFrom: req.body.userFrom }) .exec((err, doc) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, doc }); }) }); ~
2) React.js
Favorite.js
import React, { useEffect, useState } from 'react' import { Button } from 'antd'; import Axios from 'axios'; function Favorite(props) { const movieId = props.movieId; const userFrom = props.userFrom; const movieTitle = props.movieInfo.title; const moviePost = props.movieInfo.backdrop_path; const movieRunTime = props.movieInfo.runtime; const [FavoriteNumber, setFavoriteNumber] = useState(0); const [Favorited, setFavorited] = useState(false); let variables = { userFrom: userFrom, movieId: movieId, movieTitle: movieTitle, moviePost: moviePost, movieRunTime: movieRunTime } useEffect(() => { Axios.post('/api/favorite/favoriteNumber', variables) .then(res => { if (res.data.success) { console.log("res.data.favoriteNumber : ", res.data.favoriteNumber); setFavoriteNumber(res.data.favoriteNumber); } else { alert("숫자 정보를 가져오는데 실패 했습니다."); } }); Axios.post('/api/favorite/favorited', variables) .then(res => { if (res.data.success) { console.log("res.data.favorited : ", res.data.favorited); setFavorited(res.data.favorited); } else { alert("정보를 가져오는데 실패 했습니다."); } }); }, []); const onClickFavorite = () => { if (Favorited) { Axios.post('/api/favorite/removeFromFavorite', variables) .then(res => { if (res.data.success) { setFavoriteNumber(FavoriteNumber - 1); setFavorited(false); } else { alert('Favorite 리스트에서 지우는 것을 실패했습니다.'); } }); } else { Axios.post('/api/favorite/addToFavorite', variables) .then(res => { if (res.data.success) { setFavoriteNumber(FavoriteNumber + 1); setFavorited(true); } else { alert('Favorite 리스트에서 추가하는 것을 실패했습니다.'); } }); } } return ( <div> <Button onClick={onClickFavorite}>{Favorited ? "Not Favorite" : "Add to Favorite"} {FavoriteNumber}</Button> </div> ) } export default Favorite
13. Favorite 페이지 만들기 (1)
1) Nodejs
routes/favorite.js
~ //좋아하는 영화 가져오기 router.post('/getFavoritedMovie', (req, res) => { Favorite.find({ userFrom: req.body.userFrom }) .exec((err, favorites) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, favorites }); }) }) ~
2) React.js
App.js
~ const AuthFavoritePage = Auth(FavoritePage, true);//true : 로그인 한 사람만 가능 return ( ~ <Route path="/favorite" element={<AuthFavoritePage />} /> ~ ); } export default App;
src\components\views\FavoritePage\FavoritePage.css
table{ font-family: Arial, Helvetica, sans-serif; border-collapse: collapse; width: 100%; } td, th{ border: 1px solid #bebebe; text-align: left; padding: 8px; } tr:nth-child(even){ background-color: #dddddd; }
src\components\views\FavoritePage\FavoritePage.js
import React, { useEffect, useState } from 'react' import './FavoritePage.css'; import Axios from 'axios'; import { Button } from 'antd'; import { Link } from 'react-router-dom'; import { IMAGE_BASE_URL } from '../../Config'; function FavoritePage() { const [Favorites, setFavorites] = useState([]); useEffect(() => { Axios.post("/api/favorite/getFavoritedMovie", { userFrom: localStorage.getItem('userId') }) .then(res => { if (res.data.success) { console.log("좋아하는 영화 목록 :", res.data.favorites); setFavorites(res.data.favorites); } else { alert("영화 정보를 가져오는데 실패 했습니다."); } }).catch(err => { console.error("에러 :", err); }); }, []) return ( <div style={{ width: '85%', margin: '3rem auto' }}> <h2>좋아하는 영화</h2> <hr /> <table> <thead> <tr> <th>영화 썸네일</th> <th>영화 제목</th> <th>영화 상영 시간</th> <th>좋아하는 영화 삭제</th> </tr> </thead> {/* createdAt: "2022-09-10T08:57:10.377Z" movieId: "539681" moviePost: "/xfNHRI2f5kHGvogxLd0C5sB90L7.jpg" movieRunTime: "105" movieTitle: "DC League of Super-Pets" updatedAt: "2022-09-10T08:57:10.377Z" userFrom: "631b134dc2a05ff2c6388c32" __v: 0 _id: "631c5166f25fe1c926436a66" */} <tbody> { Favorites.map((favorite, index) => ( <tr key={index}> <td> <Link to={`/movie/${favorite.movieId}`}> <img src={`${IMAGE_BASE_URL}w200${favorite.moviePost}`} alt={favorite.movieTitle} /> </Link> </td> <td>{favorite.movieTitle}</td> <td>{favorite.movieRunTime}</td> <td><Button type="danger">삭제</Button></td> </tr> )) } </tbody> </table> </div> ) } export default FavoritePage
14. Favorite 페이지 (2)
1) Nodejs
routes/favorite.js
~ //좋아하는 영화 목록에서 삭제하기 router.post('/removeFromFavorite', (req, res) => { Favorite.findOneAndDelete({ movieId: req.body.movieId, userFrom: req.body.userFrom }) .exec((err, result) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true }); }) }) ~
2) React.js
src\components\views\FavoritePage\FavoritePage.css
import React, { useEffect, useState } from 'react' import './FavoritePage.css'; import Axios from 'axios'; import { Button, message, Popover } from 'antd'; import { Link } from 'react-router-dom'; import { IMAGE_BASE_URL } from '../../Config'; function FavoritePage() { const [Favorites, setFavorites] = useState([]); useEffect(() => { fetchFavoriteMovie(); }, []) const fetchFavoriteMovie = () => { Axios.post("/api/favorite/getFavoritedMovie", { userFrom: localStorage.getItem('userId') }) .then(res => { if (res.data.success) { console.log("좋아하는 영화 목록 :", res.data.favorites); setFavorites(res.data.favorites); } else { alert("영화 정보를 가져오는데 실패 했습니다."); } }).catch(err => { console.error("에러 :", err); }); } const onClickDelete = (movieId, userFrom) => { const variables = { movieId, userFrom } if (window.confirm("정말 삭제 하시겠습니까?")) { Axios.post("/api/favorite/removeFromFavorite", variables) .then(res => { if (res.data.success) { fetchFavoriteMovie(); } else { alert("리스트에서 지우는데 실패했습니다."); } }) } } const renderCards = Favorites.map((favorite, index) => { const content = ( <div> {favorite.moviePost ? <img src={`${IMAGE_BASE_URL}w500${favorite.moviePost}`} alt={favorite.movieTitle} /> : "no image" } </div> ) return <tr key={index}> <td style={{ cursor: "pointer" }}> <Popover content={content} title={favorite.movieTitle} > <img src={`${IMAGE_BASE_URL}w200${favorite.moviePost}`} alt={favorite.movieTitle} /> </Popover> </td> <td> <Link to={`/movie/${favorite.movieId}`}> {favorite.movieTitle} </Link> </td> <td>{favorite.movieRunTime}분</td> <td><Button type="danger" onClick={() => onClickDelete(favorite.movieId, favorite.userFrom)}>삭제</Button></td> </tr> }) return ( <div style={{ width: '85%', margin: '3rem auto' }}> <h2>좋아하는 영화</h2> <hr /> <table> <thead> <tr> <th>영화 썸네일</th> <th>영화 제목</th> <th>영화 상영 시간</th> <th>좋아하는 영화 삭제</th> </tr> </thead> {/* createdAt: "2022-09-10T08:57:10.377Z" movieId: "539681" moviePost: "/xfNHRI2f5kHGvogxLd0C5sB90L7.jpg" movieRunTime: "105" movieTitle: "DC League of Super-Pets" updatedAt: "2022-09-10T08:57:10.377Z" userFrom: "631b134dc2a05ff2c6388c32" __v: 0 _id: "631c5166f25fe1c926436a66" */} <tbody> {renderCards} </tbody> </table> </div > ) } export default FavoritePage
15. 강의 마무리
16. 댓글 기능 추가
1) Nodejs
1.model/Comment.js
const mongoose = require("mongoose"); const Schema = mongoose.Schema; const commentSchema = Schema({ writer: { type: Schema.Types.ObjectId, ref: "User" }, movieId: { type: String, }, responseTo: { type: Schema.Types.ObjectId, ref: "User" }, content: { type: String } }, { timestamps: true }); const Comment = mongoose.model('Comment', commentSchema); module.exports = { Comment }
2.routes/comment.js
const express = require('express'); const router = express.Router(); const { Comment } = require('../models/Comment'); //====================================== // Comment //====================================== //댓글 등록 처리 router.post('/saveComment', (req, res) => { const comment = new Comment(req.body); comment.save((err, doc) => { if (err) return res.status(400).send(err); //방금 등록한 글 반환처리 Comment.find({ '_id': doc._id }) .populate("writer") .exec((err, result) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, result }); }) }); }); //댓글 목록 가져오기 router.post('/getComments', (req, res) => { Comment.find({ "movieId": req.body.movieId }) .populate("writer") .exec((err, comments) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, comments }); }); }) module.exports = router;
3.index.js
~ app.use("/api/comment", require("./routes/comment")); ~
2) React.js
1. components/views/MovieDetail/MovieDetail.js
<Comment movieId={movieId} movie={Movie} /> 추가
~ ` {ActorToggle && <Row gutter={[16, 48]} > {Casts && Casts.map((cast, index) => ( <React.Fragment key={index}> {cast.profile_path && <GridCards path={cast.profile_path} image={cast.profile_path ? `${IMAGE_BASE_URL}w500${cast.profile_path}` : null} characterName={cast.name} /> } </React.Fragment> )) } </Row> } <Comment movieId={movieId} movie={Movie} /> </div> ~ ~
2. components/views/MovieDetail/Sections/Comment.js
import { Button } from 'antd' import React, { useEffect } from 'react' import { useState } from 'react'; import { useSelector } from 'react-redux'; import Axios from 'axios'; import SingleComment from './SingleComment'; import ReplyComment from './ReplyComment'; function Comment(props) { const user = useSelector(state => state.user); const [CommentValue, setCommentValue] = useState(); const [Comments, setComments] = useState([]); useEffect(() => { const variable = { movieId: props.movieId } //댓글 목록 가져오기 Axios.post("/api/comment/getComments", variable) .then(res => { if (res.data.success) { console.log("res.data.comments :", res.data.comments); setComments(res.data.comments); } else { alert("댓글 목록을 가져오는데 실패했습니다."); } }); }, []) const handleClick = (event) => { setCommentValue(event.currentTarget.value); } const refreshFunction = (newComment) => { setComments(Comments.concat(newComment)); } const onsubmit = (event) => { event.preventDefault(); if (!user.userData._id) { alert("댓글은 로그인 후 작성 가능합니다."); return; } const variable = { content: CommentValue, writer: user.userData._id, movieId: props.movieId } Axios.post("/api/comment/saveComment", variable) .then(res => { if (res.data.success) { setCommentValue(""); refreshFunction(res.data.result); } else { alert("댓글 저장에 실패했습니다."); } }) } return ( <div> <br /> <hr style={{ marginTop: 50 }} /> <h3 style={{ fontSize: "1.8rem" }}>{props.movie.title} 에 대한 의견을 공유해주세요.</h3> {/* Comment Lists */} { Comments.length === 0 && <div style={{ height: 150, position: "relative" }}><span style={{ top: "30%", position: "absolute", left: "40%", fontSize: "1.2rem" }} > 이 영화에 대한 당신의 생각을 가장 먼저 공유해 보세요.</span></div> } { Comments && Comments.map((comment, index) => ( (!comment.responseTo && <React.Fragment key={index}> <SingleComment comment={comment} refreshFunction={refreshFunction} /> <ReplyComment parentCommentId={comment._id} commentLists={Comments} refreshFunction={refreshFunction} /> </React.Fragment> ) )) } {/* Root Comment Form */} <form style={{ display: 'flex' }}> <textarea style={{ width: '100%', borderRadius: '5px' }} onChange={handleClick} value={CommentValue} placeholder='댓글을 작성해 주세요.'></textarea> <br /> <Button style={{ width: '20%', height: "52px" }} onClick={onsubmit}>댓글작성</Button> </form> </div > ) } export default Comment
3. components/views/MovieDetail/Sections/SingleComment.js
import React from 'react' import { Comment as AntdComment, Avatar, Button, Input } from 'antd'; import { useState } from 'react'; import Axios from 'axios'; import { useSelector } from 'react-redux'; function SingleComment(props) { const [OpenReply, setOpenReply] = useState(false); const user = useSelector(state => state.user); const [CommentValue, setCommentValue] = useState(); const onClickRplyOpen = () => { setOpenReply(!OpenReply); } const actions = [ <span style={{ fontSize: '12px', margin: 0, color: 'black', cursor: "pointer", fontWeight: "bold" }} onClick={onClickRplyOpen} key="comment-basic-reply-to">댓글작성</span> ] const onSubmit = (event) => { event.preventDefault(); if (!user.userData._id) { alert("댓글은 로그인 후 작성 가능합니다."); return; } const variable = { content: CommentValue, writer: user.userData._id, movieId: props.comment.movieId, responseTo: props.comment._id } Axios.post("/api/comment/saveComment", variable) .then(res => { if (res.data.success) { setCommentValue(""); props.refreshFunction(res.data.result); setOpenReply(false); } else { alert("댓글 저장에 실패했습니다."); } }) } return ( <div> <AntdComment actions={actions} author={props.comment.writer.name} avatar={<Avatar src={props.comment.writer.image} alt={props.comment.writer.name} />} content={<p>{props.comment.content}</p>} /> {OpenReply && <form style={{ display: 'flex', marginBottom: 30 }} onSubmit={onSubmit} > <textarea style={{ width: "100%", borderRadius: '5px' }} onChange={(e) => setCommentValue(e.target.value)} value={CommentValue} placeholder='코멘트를 작성해 주세요.' > </textarea> <br /> <Button style={{ width: '20%', height: '52px' }} onClick={onSubmit} >댓글작성하기</Button> </form> } </div > ) } export default SingleComment
4. components/views/MovieDetail/Sections/ReplyComment.js
import React from 'react' import { useState, useEffect } from 'react'; import SingleComment from './SingleComment'; function ReplyComment(props) { const [ChildCommentNumber, setChildCommentNumber] = useState(0); const [OpenReplyComments, setOpenReplyComments] = useState(false); useEffect(() => { let commentNumber = 0; props.commentLists.map((comment) => { if (comment.responseTo === props.parentCommentId) { commentNumber++; } }) setChildCommentNumber(commentNumber); }, [props.commentLists]) const renderReplyComment = (parentCommentId) => { return props.commentLists.map((comment, index) => ( <React.Fragment key={index}> { comment.responseTo === parentCommentId && <div style={{ width: "80%", marginLeft: '40px', padding: "10px" }}> <SingleComment refreshFunction={props.refreshFunction} comment={comment} /> {/* 댓글이 존재하면 ReplyComment 가 계속 반복 호출 처리 되어 진다. */} <ReplyComment parentCommentId={comment._id} commentLists={props.commentLists} refreshFunction={props.refreshFunction} /> </div> } </React.Fragment> )) } const onHandleChange = () => { setOpenReplyComments(!OpenReplyComments); } return ( <div> {ChildCommentNumber > 0 && <p style={{ fontSize: '14px', margin: 0, color: 'black', cursor: "pointer", fontWeight: "bold" }} onClick={onHandleChange}> {!OpenReplyComments ? '댓글보기' : '댓글숨기기'} ({ChildCommentNumber}) </p> } {OpenReplyComments && renderReplyComment(props.parentCommentId) } </div > ) } export default ReplyComment
17. 좋아요 기능 추가
1) Nodejs
1.models/Like.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const likeSchema = Schema({ userId: { type: Schema.Types.ObjectId, ref: "User" }, commentId: { type: Schema.Types.ObjectId, ref: "Comment" }, movieId: { type: Schema.Types.ObjectId, ref: "Video" } }, { timestamps: true }); const Like = mongoose.model("Like", likeSchema); module.exports = { Like }
2.models/Dislike.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const dislikeSchema = Schema({ userId: { type: Schema.Types.ObjectId, ref: "User" }, commentId: { type: Schema.Types.ObjectId, ref: "Comment" }, movieId: { type: Schema.Types.ObjectId, ref: "Video" } }, { timestamps: true }); const Dislike = mongoose.model("Dislike", dislikeSchema); module.exports = { Dislike }
3.routes/like.js
const express = require('express'); const router = express.Router(); const { Like } = require('../models/Like'); const { Dislike } = require('../models/Dislike'); //====================================== // Like //====================================== //좋아요 가져오기 router.post("/getLikes", (req, res) => { let variable = {} if (req.body.variable) { variable = { movieId: req.body.movieId } } else { variable = { commentId: req.body.commentId } } Like.find(variable) .exec((err, likes) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, likes }) }) }); //싫어요 가져오기 router.post("/getDisLikes", (req, res) => { let variable = {} if (req.body.variable) { variable = { movieId: req.body.movieId } } else { variable = { commentId: req.body.commentId } } Dislike.find(variable) .exec((err, dislikes) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, dislikes }) }) }); //좋아요 증가처리 router.post("/upLike", (req, res) => { let variable = {} if (req.body.movieId) { variable = { variable: req.body.movieId, userId: req.body.userId } } else { variable = { commentId: req.body.commentId, userId: req.body.userId } } const like = new Like(variable); //1.Like collection 좋아요 정보 추가 like.save((err, likeResult) => { if (err) return res.json({ success: false, err }); //2. 만약에 Dislike 가 이미 클릭 되어 있다면, Dislike 을 1 줄여 준다. //여기서는 삭제 처리 Dislike.findOneAndDelete(variable) .exec((err, disLikeResult) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true }); }) }) }) //좋아요 취소처리 router.post("/unLike", (req, res) => { let variable = {} if (req.body.movieId) { variable = { variable: req.body.movieId, userId: req.body.userId } } else { variable = { commentId: req.body.commentId, userId: req.body.userId } } Like.findOneAndDelete(variable) .exec((err, result) => { if (err) return res.status(400).json({ success: false, err }); res.status(200).json({ success: true }) }) }); //싫어요 감소 처리 router.post("/unDislike", (req, res) => { let variable = {} if (req.body.movieId) { variable = { variable: req.body.movieId, userId: req.body.userId } } else { variable = { commentId: req.body.commentId, userId: req.body.userId } } Dislike.findOneAndDelete(variable) .exec((err, result) => { if (err) return res.status(400).json({ success: false, err }); res.status(200).json({ success: true }) }); }); //싫어요 증가 처리 router.post("/upDislike", (req, res) => { let variable = {} if (req.body.movieId) { variable = { variable: req.body.movieId, userId: req.body.userId } } else { variable = { commentId: req.body.commentId, userId: req.body.userId } } const dislike = new Dislike(variable) //1.Dislike collection 에다가 클릭 정보를 넣는다. dislike.save((err, dislikeResult) => { if (err) return res.json({ success: false, err }); //2. 만약에 Like 가 이미 클릭이 되었다면, 해당 정보가 존재하면 삭제 처리 Like 1 감소 Like.findOneAndDelete(variable) .exec((err, likeResult) => { if (err) return res.status(400).json({ success: false, err }); res.status(200).json({ success: true }) }) }) }); module.exports = router;
4.index.js
~ app.use("/api/like", require("./routes/like")); ~
2) React.js
1.src\components\views\MovieDetail\Sections\LikeDislikes.js
import React, { useEffect } from 'react' import { Tooltip, Icon } from 'antd'; import Axios from 'axios'; import { useState } from 'react' import { useSelector } from 'react-redux'; function LikeDislikes(props) { const [Likes, setLikes] = useState(0); const [Dislikes, setDislikes] = useState(0); const [LikeAction, setLikeAction] = useState(null); const [DislikeAction, setDislikeAction] = useState(null); const user = useSelector(state => state.user); let variable = ""; if (props.movieId) { variable = { movieId: props.movieId, userId: props.userId } } else { variable = { commentId: props.commentId, userId: props.userId } } useEffect(() => { Axios.post("/api/like/getLikes", variable) .then(res => { if (res.data.success) { //얼마나 많은 좋아요를 받았는지 setLikes(res.data.likes.length); //내가 이미 그 좋아요를 눌렀는지 res.data.likes.map((like) => { if (like.userId === props.userId) { setLikeAction('liked'); } }); } else { alert("Likes 정보를 가져오지 못했습니다."); } }); Axios.post("/api/like/getDisLikes", variable) .then(res => { if (res.data.success) { //얼마나 많은 싫어요를 받았는지 setDislikes(res.data.dislikes.length); //내가 이미 그 싫어요를 눌렀는지 res.data.dislikes.map((dislikes) => { if (dislikes.userId === props.userId) { setDislikeAction('disliked'); } }); } else { alert("DisLikes 정보를 가져오지 못했습니다."); } }); }) const onLike = () => { //console.log(" variable : ", variable); if (!user.userData.isAuth) { alert("로그인 후 이용 가능합니다."); return } if (LikeAction === null) { Axios.post('/api/like/upLike', variable) .then(res => { if (res.data.success) { setLikes(Likes + 1); setLikeAction("liked"); if (DislikeAction !== null) { setDislikeAction(null); setDislikes(Dislikes - 1); } } else { alert("Like 를 올리지 못하였습니다.") } }) } else { Axios.post('/api/like/unLike', variable) .then(res => { if (res.data.success) { setLikes(Likes - 1); setLikeAction(null); } else { alert("Like를 내리지 못하였습니다."); } }) } } const onDislike = () => { if (!user.userData.isAuth) { alert("로그인 후 이용 가능합니다."); return } if (DislikeAction !== null) { Axios.post("/api/like/unDislike", variable) .then(res => { if (res.data.success) { setDislikes(Dislikes - 1); setDislikeAction(null); } else { alert("dislike 를 지우지 못하였습니다."); } }) } else { Axios.post("/api/like/upDislike", variable) .then(res => { if (res.data.success) { setDislikes(Dislikes + 1); setDislikeAction("disliked"); if (LikeAction !== null) { setLikeAction(null); setLikes(Likes - 1); } } else { alert("DisLike 를 올리지 못하였습니다.") } }) } } return ( <div> <span className="comment-basic-like"> <Tooltip title="좋아요"> <Icon type="like" theme={LikeAction === "liked" ? 'filled' : 'outlined'} onClick={onLike} /> </Tooltip> <span style={{ padding: 8, cursor: "auto" }}>{Likes}</span> </span> <span className="comment-basic-like"> <Tooltip title="싫어요"> <Icon type="dislike" theme={DislikeAction === "disliked" ? 'filled' : 'outlined'} onClick={onDislike} /> </Tooltip> <span style={{ padding: 8, cursor: "auto" }}>{Dislikes}</span> </span> </div > ) } export default LikeDislikes
2.src\components\views\MovieDetail\MovieDetail.js
~ <List.Item style={{ justifyContent: "center" }} actions={[<LikeDislikes video userId={localStorage.getItem("userId")} movieId={movieId} />]}> </List.Item> ~
3.src\components\views\MovieDetail\SingleComment.js
~ const actions = [ <LikeDislikes userId={localStorage.getItem("userId")} commentId={props.comment._id} />, <span style={{ fontSize: '12px', margin: 0, color: 'black', cursor: "pointer", fontWeight: "bold" }} onClick={onClickRplyOpen} key="comment-basic-reply-to">댓글작성</span> ] ~
댓글 ( 4)
댓글 남기기