API
model 생성 (models/Favorite.js)
좋아요라는 것은 '어떤 게시글'에 대해 '특정 유저'가 누르는 것이다. 그러므로 좋아요 테이블은 postId, userId 컬럼을 가져야 한다.
userId, postId가 이미 생성해 둔 'User', 'Post' 모델을 참조하도록 해서, 데이터를 조회하는 로직에서 필요하다면 populate() 메소드로 JOIN을 할 수 있다. timestamps 속성을 주면 데이터가 기록될 때 자동으로 시간도 기록된다.
const mongoose = require('mongoose');
const favoriteSchema = mongoose.Schema({
userFrom: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
postId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
},
}, { timestamps: true });
const Favorite = mongoose.model('Favorite', favoriteSchema);
module.exports = { Favorite };
router (router/favorite.js)
const express = require('express');
const router = express.Router();
const { Favorite } = require('../models/Favorite');
router.post('/favoriteNumber', (req, res) => {
Favorite.find({ postId: req.body.postId })
.exec((err, info) => {
if(err) return res.status(400).send(err);
res.status(200).json({ success: true, favoriteNumber: info.length });
});
});
router.post('/favorited', (req, res) => {
// 이 user가 favorite을 눌렀는지 가져온다
Favorite.find({ postId: req.body.postId, 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 });
});
});
router.post('/addToFavorite', (req, res) => {
const favorite = new Favorite(req.body);
favorite.save((err, doc) => {
if(err) return res.status(400).send(err);
return res.status(200).json({ success: true });
})
});
router.post('/removeFromFavorite', (req, res) => {
Favorite.findOneAndDelete({ postId: req.body.postId, userFrom: req.body.userFrom })
.exec((err, doc) => {
if(err) return res.status(400).send(err);
return res.status(200).json({ success: true, doc });
});
});
router.post('/getFavoritePost', (req, res) => {
Favorite.find({ userFrom: req.body.userFrom })
.populate('postId')
.exec((err, favorites) => {
if(err) return res.status(400).send(err);
return res.status(200).json({ success: true, favorites });
});
});
module.exports = router;
'/getFavoritePost' 는 특정 유저가 favorite 누른 포스트를 모아서 조회할 때 사용하는 API이다. populate('postId')로 JOIN 연산을 했기 때문에 전송하는 결과에는 userId와 postId가 단순히 전송되는 것이 아니라 post의 제목, 내용 등의 정보가 함께 포함된다. 필요하다면 userId도 populate하는 식으로 활용할 수 있다.
React (Favorite.js)
Favorite라는 컴포넌트를 만든다. 컴포넌트가 JSX뿐만 아니라 API를 호출하는 등의 로직도 가졌기 때문에 Favorite 기능이 필요한 페이지가 있다면 간단하게 컴포넌트만 추가하면 된다.
여기서는 user 정보를 redux를 통해 가져왔지만 그냥 props로 전달받아도 된다.
과정
- redux나 props를 통해 userId와 postId를 가져온다. 이것을 API를 호출할 때 사용할 것이므로 variables 오브젝트로 저장해준다.
- '좋아요 숫자' (FavoriteNumber), '내가 좋아요 여부를 눌렀는지 여부' (Favorited)를 저장할 state를 준비한다. 좋아요 숫자의 기본값은 0, 좋아요 여부의 기본값은 false이다.
- useEffect()로 좋아요 숫자와 좋아요 여부를 가져오고 <button>에 보여준다. 좋아요를 누른 여부(true/false)에 따라 문구를 바꾸거나 스타일을 줄 수도 있다.
- onClick 함수를 만든다. 이때 로그인을 했는지를 검토한다. 여기서는 로그인했을 때 받은 인증 정보가 Redux에 존재하는지 여부로 로그인 여부를 파악했다. 만약 이미 좋아요가 되어있는 상태라면 remove API를, 눌려있지 않다면 add API를 호출하고 Favorited state를 toggle해준다.
import React, { useEffect, useState } from 'react'
import axios from 'axios';
import { useSelector } from 'react-redux';
function Favorite(props) {
const user = useSelector(state => state.user)
const postId = props.postId;
const userFrom = user.userData._id;
const [FavoriteNumber, setFavoriteNumber] = useState(0);
const [Favorited, setFavorited] = useState(false);
let variables = {
userFrom,
postId,
};
useEffect(() => {
axios.post('/api/favorite/favoriteNumber', variables)
.then(response => {
if(response.data.success) {
setFavoriteNumber(response.data.favoriteNumber);
} else {
alert("'좋아요' 숫자를 가져오는데 실패했습니다.");
}
});
axios.post('/api/favorite/favorited', variables)
.then(response => {
if(response.data.success) {
setFavorited(response.data.favorited);
} else {
alert("'좋아요' 여부를 가져오는데 실패했습니다.");
}
});
}, []);
const onClickFavorite = () => {
if(user.userData && !user.userData.isAuth) {
return alert('로그인을 해주세요');
}
if(Favorited) {
axios.post('/api/favorite/removeFromFavorite', variables)
.then(response => {
if(response.data.success) {
setFavoriteNumber(FavoriteNumber - 1);
setFavorited(!Favorited);
} else {
alert('Favorite 리스트에서 지우는 것을 실패했습니다.');
}
});
} else {
axios.post('/api/favorite/addToFavorite', variables)
.then(response => {
if(response.data.success) {
setFavoriteNumber(FavoriteNumber + 1);
setFavorited(!Favorited);
} else {
alert('Favorite 리스트에 추가하는 것을 실패했습니다.');
}
});
}
};
return (
<button onClick={onClickFavorite}>{ Favorited ? "Not Favorite" : "Add to Favorite" } {FavoriteNumber}</button>
)
}
export default Favorite
'UI 구현 연구일지' 카테고리의 다른 글
[React] Debounce(디바운스) 적용한 검색어 자동완성 구현기 (1) (0) | 2023.06.14 |
---|---|
[React 컴포넌트] 댓글 기능 구현하기 (0) | 2022.07.12 |
[React 컴포넌트] Like, Dislike (좋아요, 싫어요) 기능 만드는 법 (0) | 2022.07.11 |
[React 컴포넌트] 더 보기(Load More) 버튼 만드는 법 (0) | 2022.07.08 |
[JS 컴포넌트] 캐러셀 이미지 슬라이드 만들기 (0) | 2022.05.30 |