Backend πŸ“š/Node.js

[Node]Instagram Clone - 11. λŒ“κΈ€ μ’‹μ•„μš”, μ’‹μ•„μš” μ·¨μ†Œ API

leejaejae 2024. 11. 8. 15:21

1. λŒ“κΈ€ μ’‹μ•„μš” 및 μ’‹μ•„μš” μ·¨μ†Œ κΈ°λŠ₯ κ΅¬ν˜„ν•˜κΈ°

  • 이번 ν¬μŠ€νŠΈμ—μ„œλŠ” λŒ“κΈ€ μ’‹μ•„μš” κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κ³ , κ²Œμ‹œλ¬Ό 쑰회 μ‹œ λŒ“κΈ€ μ’‹μ•„μš” 수 ν‘œμ‹œλ₯Ό κ΅¬ν˜„ν•˜κ² μŒ.

1) 기쑴의 Comment μŠ€ν‚€λ§ˆ μˆ˜μ •(Comment.js)

  • Comment μŠ€ν‚€λ§ˆμ— μ’‹μ•„μš” λˆ„λ₯Έ μ‚¬μš©μž ID μ €μž₯ν•˜λŠ” likes ν•„λ“œ μΆ”κ°€.
  • likes ν•„λ“œλŠ” User μŠ€ν‚€λ§ˆ λͺ¨λΈμ˜ ObjecctId λ°°μ—΄λ‘œ μ„€μ •ν•΄ 각 λŒ“κΈ€μ˜ μ’‹μ•„μš” μ‚¬μš©μž 정보λ₯Ό μ €μž₯함.
// comment.js

const mongoose = require("mongoose");

const commentSchema = new mongoose.Schema({
  ...
  likes: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: "User", // μ’‹μ•„μš”λ₯Ό λˆ„λ₯Έ μ‚¬μš©μž ID
    },
  ],
});

const Comment = mongoose.model("Comment", commentSchema);
module.exports = { Comment };

 


2) λŒ“κΈ€ μ’‹μ•„μš” 및 μ’‹μ•„μš” μ·¨μ†Œ API

  • λŒ“κΈ€μ˜ μ’‹μ•„μš”μ™€ μ’‹μ•„μš” μ·¨μ†Œ κΈ°λŠ₯ κ΅¬ν˜„(λ§€μ»€λ‹ˆμ¦˜μ€ κ²Œμ‹œλ¬Ό μ’‹μ•„μš” 및 μ’‹μ•„μš” μ·¨μ†Œμ™€ 동일)

  • μš”μ²­ 경둜의 commentIdλ₯Ό 가져와 ν•΄λ‹Ή λŒ“κΈ€μ— μ ‘κ·Όν•˜λ©°, likes λ°°μ—΄μ—μ„œ 이미 μ’‹μ•„μš”κ°€ λˆŒλ ΈλŠ”μ§€ 확인함.
    • μ’‹μ•„μš”κ°€ 눌린 경우: likes λ°°μ—΄μ—μ„œ μ‚¬μš©μž IDλ₯Ό μ œκ±°ν•˜μ—¬ μ’‹μ•„μš”λ₯Ό μ·¨μ†Œν•˜κ³  μƒνƒœ μ½”λ“œλ₯Ό 200으둜 λ°˜ν™˜.
    • μ’‹μ•„μš”κ°€ μ—†λŠ” 경우: μ‚¬μš©μž IDλ₯Ό likes λ°°μ—΄μ— μΆ”κ°€ν•˜κ³  μƒνƒœ μ½”λ“œλ₯Ό 201둜 λ°˜ν™˜.
// likeComment.js

const express = require("express");
const router = express.Router();
router.use(express.json());

const { auth } = require("../auth");
const cookieParser = require("cookie-parser");
router.use(cookieParser());

const { Comment } = require("../../models/Comment");

router.post("/:commentId/like", auth, async (req, res) => {
  const { commentId } = req.params;
  const userId = req.user.id;

  try {
    const comment = await Comment.findById(commentId);

    if (!comment) {
      return res.status(404).json({ message: "λŒ“κΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€." });
    }

    const existingLikeIndex = comment.likes.indexOf(userId);

    if (existingLikeIndex !== -1) {
      // 이미 μ’‹μ•„μš”κ°€ 눌린 경우 μ’‹μ•„μš” μ·¨μ†Œ
      comment.likes.splice(existingLikeIndex, 1);
      await comment.save();
      return res.status(200).json({ message: "λŒ“κΈ€ μ’‹μ•„μš”κ°€ μ·¨μ†Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€." });
    } else {
      // μ’‹μ•„μš” μΆ”κ°€
      comment.likes.push(userId);
      await comment.save();
      return res.status(201).json({ message: "λŒ“κΈ€ μ’‹μ•„μš”κ°€ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€." });
    }
  } catch (error) {
    console.error(error);
    return res.status(500).json({ message: "μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€." });
  }
});

module.exports = router;

 


3) κ²Œμ‹œλ¬Ό 쑰회 μ‹œ λŒ“κΈ€μ˜ μ’‹μ•„μš” 수 ν‘œμ‹œ

  • getPost.js μ—μ„œ κ²Œμ‹œλ¬΄ 단건 쑰회 μ‹œ 각 λŒ“κΈ€μ˜ μ’‹μ•„μš” 수 λ₯Ό ν•¨κ»˜ ν‘œμ‹œν•˜λ„λ‘ μˆ˜μ •ν•¨.
  • commentsWithLikeCount λ°°μ—΄μ„ 톡해 각 λŒ“κΈ€μ˜ μ’‹μ•„μš” 수λ₯Ό ν¬ν•¨ν•œ 정보λ₯Ό κ°€μ Έμ˜€κ³ , 
  • λŒ“κΈ€ likes λ°°μ—΄μ˜ 길이λ₯Ό κ³„μ‚°ν•˜μ—¬ likesCountλ₯Ό μΆ”κ°€ν•œ ν›„, μ΅œμ’… μ‘λ‹΅μ—μ„œ post κ°μ²΄ μ•ˆμ— likesCount와 ν•¨κ»˜ 포함됨.
// getPost.js

const express = require("express");
const router = express.Router();
const { Post } = require("../../models/Post");

router.get("/:id", async (req, res) => {
  const { id } = req.params;

  try {
    const post = await Post.findById(id)
      .populate({
        path: "comments",
        populate: { path: "user", select: "username" }, // λŒ“κΈ€ μž‘μ„±μž 정보 포함
      })
      .populate("user", "username");

    if (!post) {
      return res.status(404).json({ message: "κ²Œμ‹œλ¬Όμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€." });
    }

    ...

    // 각 λŒ“κΈ€μ— λŒ€ν•œ μ’‹μ•„μš” 수 μΆ”κ°€
    const commentsWithLikeCount = post.comments.map((comment) => ({
      ...comment.toObject(),
      likesCount: comment.likes.length, // λŒ“κΈ€μ˜ μ’‹μ•„μš” 수
    }));

    return res.status(200).json({
      post: {
        ...post.toObject(),
        likesCount,
        comments: commentsWithLikeCount,
      },
    });
  ...
});

module.exports = router;

 

2. κ²°κ³Ό πŸŽ‰