안녕하세요
게시물 좋아요 기능 구현 방법 본문
게시물 좋아요 구현 방법
첫번째, 게시물에 좋아요를 클릭한 유저들을 저장한다.
두번째, 좋아요 collection을 만들어 유저들은 게시물별로 저장한다.
ex) likes: ["유저1", "유저2", "유저3"]
좋아요 누를 사람이 많아지면 그때 나누는게 나을것 같다.
mongodb free tier는 콜렉션 수에 제한이 있어서 많이 못 만들기 때문이다.
1) 좋아요를 누른 유저 목록에 현재 로그인한 유저가 포함되어 있는지 확인
ex) likes: ["유저1", "유저2", "유저3"]
포함되어있다 ? "하트" : "빈 하트"
const [isLiked, setIsLiked] = useState<boolean>(userEmail ? likes.includes(userEmail) : true); // 로그인 여부에 따라 heart 모양 변경
return <> {isLiked ? "❤" : "🤍"} {likeCount} <>
2) 게시물 좋아요 버튼을 누르면 좋아요 요청
좋아요 버튼은 컴포넌트화하여 게시물 카드에서도 동작하도록 할 수 있다.
const handleClickLikePostButton = async () => {
try {
// 게시물 좋아요 api
const res: number = await postLikeCountData(postId, likeCount);
handleSuccess(res); // 요청 성공 시 실행
} catch (err) {
console.error(err);
window.alert("서버에 문제가 발생하였습니다. 잠시 후에 시도해주세요.");
}
};
3. 좋아요 권한 검사
좋아요를 누르는 유저를 email 로그인 한 유저로 한정하고 싶다.
token을 검사하여 인증되지 않은 유저는 동작하지 않도록 선리턴 한다.
// 게시물 좋아요 버튼 API 입니다.
export const PATCH = async (req: NextRequest, { params }: Params) => {
const token: JWT | null = await getToken({ req }); // 유저 정보
const { postId } = params;
// 로그인 유저를 검사합니다.
if (!token) {
return NextResponse.json({ message: "게시물 좋아요: 로그인 된 유저가 아닙니다." }, { status: 400 });
}
// 이메일 인증된 유저가 아니면 에러 반환
if (!token.email) {
return NextResponse.json(
{ message: "게시물 좋아요: 이메일이 인증 된 유저가 아닙니다." },
{ status: 400 }
);
}
//...
};
로그인 된 유저인가?
>no. 로그인 된 유저가 아닐 경우에는 "로그인 유저만 가능합니다." 라는 경고를 띄운다.
>yes. 다음으로
4. 서버에서 이미 좋아요를 누른 유저인지 확인
좋아요를 누른 게시물이 서버 상에서는 이미 삭제되어 있을 수 있다.
이 경우에는 DB에서 에러가 발생하므로 다음으로 넘어가지 않고,
좋아요를 누르려는 게시물을 찾을 수 없다는 경고를 띄운다.
// 좋아요 업데이트
const db = (await connectDB).db("blog");
const postsCollection = db.collection<Post>("posts");
const foundPostResult = await postsCollection.findOne({ id: Number(postId) });
// 게시물이 존재하지 않으면 에러 반환
if (!foundPostResult) {
return NextResponse.json(
{ message: "게시물 좋아요: 게시물을 찾을 수 없습니다." },
{ status: 404 }
);
}
다른 유저가 좋아요를 눌렀을 수도 있기 때문에 좋아요를 누른 시점에서는 실제 게시물의 좋아요 수와 다를 수 있다.
따라서 좋아요를 서버 상의 데이터로 반영하기 위해서는 현재 숫자에서 +1, -1를 하는 것이 아니라
DB에서 가져온 데이터에 +1, -1 한 결과를 반영해주어야 한다.
// 좋아요 누른 시점의 숫자 결과 반환
const postLikeUsers = foundPostResult?.likes;
let count: number = postLikeUsers.length;
// 좋아요를 이미 눌렀다면 배열에서 email 제외
if (postLikeUsers?.includes(token.email)) {
await postsCollection.updateOne({ id: Number(postId) }, { $pull: { likes: token.email } });
count--;
} else {
// 그렇지 않다면 배열에 email추가
await postsCollection.updateOne({ id: Number(postId) }, { $push: { likes: token.email } });
count++;
}
return NextResponse.json({ count }, { status: 200 }); // 응답에 게시물 id를 포함하여 redirect할 수 있도록 합니다.
이미 좋아요를 누른 유저라면 좋아요 배열에 유저 정보가 포함되어 있기 때문에 pull로 제거하고,
그렇지 않으면 push로 추가한다.
if (postLikes?.includes(token.email)) { // 이미 좋아요를 눌렀는가?
>no. likes 배열에서 유저 정보를 제거한다.
await postsCollection.updateOne({ id: Number(postId) }, { $push: { likes: token.email } });
>yes. likes 배열에 유저 정보를 삽입한다.
await postsCollection.updateOne({ id: Number(postId) }, { $pull: { likes: token.email } });
5. 클라이언트 컴포넌트에서 좋아요 숫자를 반영한다.
const handleClickLikePostButton = async () => {
const res: number = await postLikeCountData(postId, likeCount);
handleSuccess(res); // 요청 성공 시 실행
};
// 좋아요 요청 성공 시 이벤트
const handleSuccess = (likeCount: number) => {
// like 숫자 변경
setPostLike(likeCount);
// card 페이지에서는 로그인 여부와 관계 없이 heart 표시를 합니다.
if (userEmail) {
setIsLiked(!isLiked);
}
};
요청 성공 시 서버에서 받아 온 좋아요를 클라이언트 컴포넌트에 반영해준다.
'Next13 블로그 프로젝트' 카테고리의 다른 글
티스토리 Loading 컴포넌트 커스텀 훅을 이용하여 구현하는 방법 (0) | 2023.10.11 |
---|---|
Next.js 프로젝트 Vercel 배포하는 방법 (+마주하는 에러 해결 세가지) (0) | 2023.10.05 |
카테고리(2) - 카테고리로 포스트 검색 기능 구현 방법 (0) | 2023.10.01 |
Next13 동적 라우트 params, body, 응답, nextauth (기존 vs Next13 비교) (0) | 2023.09.26 |
댓글 기능구현(2): 수정, 삭제 권한 분기(유저/게스트/관리자) (0) | 2023.09.24 |