Backend πŸ“š/Node.js

[Node]Instagram Clone - 10. κ²Œμ‹œλ¬Ό μ’‹μ•„μš”, μ’‹μ•„μš” μ·¨μ†Œ API

leejaejae 2024. 11. 7. 14:07

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

  • 이번 ν¬μŠ€νŒ…μ—μ„œλŠ” κ²Œμ‹œλ¬Ό μ’‹μ•„μš” 및 μ’‹μ•„μš” μ·¨μ†Œ κΈ°λŠ₯을 κ΅¬ν˜„ν•¨.
  • λ˜ν•œ κ²Œμ‹œλ¬Ό 쑰회 μ‹œ μ’‹μ•„μš” 개수λ₯Ό λ³΄μ—¬μ£ΌλŠ” 방식도 ν•¨κ»˜ κ΅¬ν˜„ν•¨.

1) Like μŠ€ν‚€λ§ˆ μ •μ˜(Like.js)

  • like.js νŒŒμΌμ—μ„œλŠ” μ’‹μ•„μš” 데이터λ₯Ό μ €μž₯ν•˜λŠ” like μŠ€ν‚€λ§ˆλ₯Ό μ •μ˜ν–ˆμŒ.
  • 이 μŠ€ν‚€λ§ˆλŠ” user_id와, μ’‹μ•„μš”λ₯Ό λˆ„λ₯Έ λŒ€μƒ(post_id λ˜λŠ” comment_id)의 IDλ₯Ό μ €μž₯함.
// Like.js

const mongoose = require("mongoose");

const likeSchema = new mongoose.Schema({
  user_id: {
    type: String,
    required: true,
  },
  post_id: {
    type: String,
    required: false,
  },
  comment_id: {
    type: String,
    required: false,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

const Like = mongoose.model("Like", likeSchema);
module.exports = { Like };


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

  • likes ν•„λ“œλ₯Ό μΆ”κ°€ν•΄ νŠΉμ • κ²Œμ‹œλ¬Όμ— μ’‹μ•„μš”λ₯Ό λˆ„λ₯Έ μ‚¬μš©μž ID λͺ©λ‘μ„ μ €μž₯ν•˜λŠ” λ°°μ—΄λ‘œ 섀정함.
// post.js

const mongoose = require("mongoose");

const postSchema = new mongoose.Schema({
  ...
  likes: [
    {
      type: mongoose.Schema.Types.ObjectId,
      ref: "User",
    },
  ],
});

const Post = mongoose.model("Post", postSchema);
module.exports = { Post };


3) κ²Œμ‹œλ¬Ό μ’‹μ•„μš” 및 μ’‹μ•„μš” μ·¨μ†Œ API(likePost.js)

  • μ‚¬μš©μžκ°€ 이미 μ’‹μ•„μš”λ₯Ό λˆ„λ₯Έ 경우 μ·¨μ†Œν•˜κ³ , 그렇지 μ•ŠμœΌλ©΄ μ’‹μ•„μš”λ₯Ό μΆ”κ°€ν•˜λŠ” λ°©μ‹μœΌλ‘œ μž‘λ™
  • κΈ°λ³Έ 둜직:
    • μš”μ²­λœ postId λ₯Ό 기반으둜 κ²Œμ‹œλ¬Ό 검색
    • likes 배열에 μ‚¬μš©μžμ˜ IDκ°€ 이미 μ‘΄μž¬ν•˜λ©΄ λ°°μ—΄μ—μ„œ ν•΄λ‹Ή IDλ₯Ό μ œκ±°ν•˜κ³  "μ’‹μ•„μš” μ·¨μ†Œ" μ‹€ν–‰
    • μ‚¬μš©μžμ˜ IDκ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄ likes 배열에 IDλ₯Ό μΆ”κ°€ν•˜κ³  "μ’‹μ•„μš” μΆ”κ°€" μ‹€ν–‰ 
// likePost.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 { Post } = require("../../models/Post");

// κ²Œμ‹œλ¬Ό μ’‹μ•„μš” API
router.post("/:postId/like", auth, async (req, res) => {
  const { postId } = req.params;
  const userId = req.user.id;

  try {
    const post = await Post.findById(postId);

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

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

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

module.exports = router;


4) κΈ°μ‘΄ κ²Œμ‹œλ¬Ό 쑰회 API μˆ˜μ •(getPost.js)

  • νŠΉμ • κ²Œμ‹œλ¬Ό μ‘°νšŒν•  λ•Œ ν•΄λ‹Ή κ²Œμ‹œλ¬Όμ˜ μ’‹μ•„μš” κ°œμˆ˜λ„ λ°˜ν™˜ν•˜λ„λ‘ μ„€μ •.
  • 핡심 κΈ°λŠ₯:
    • post.likes.length λ₯Ό μ‚¬μš©ν•΄ μ’‹μ•„μš” 수 κ²Œμ‚°
    • post 객체와 μ’‹μ•„μš” 수λ₯Ό μ‘λ‹΅μœΌλ‘œ λ°˜ν™˜ 
// getPost.js

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

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

    const likesCount = post.likes.length;

    return res.status(200).json({
      post: {
        ...post.toObject(),
        likesCount,
      },
    });
  } catch (error) {
    return res.status(500).json({
      message: "κ²Œμ‹œλ¬Ό 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.",
      error: error.message,
    });
  }
});

module.exports = router;

 

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