Backend ๐Ÿ“š/Node.js

[Node]Instagram Clone - 4. multer์™€ AWS S3๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒŒ์‹œ๋ฌผ ์—…๋กœ๋“œ API

leejaejae 2024. 11. 2. 16:37
  • multer์™€ AWS S3์„ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•œ ๊ฒŒ์‹œ๋ฌผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” API๋ฅผ ๋งŒ๋“ค์–ด ๋ณด์ž.
  • ์ด๋ฏธ์ง€๋Š” AWS S3์— ์ €์žฅ๋˜๊ณ , ์ €์žฅ๋œ ์ด๋ฏธ์ง€์˜ URL์€ MongoDB์— ํ•จ๊ป˜ ์ €์žฅ๋จ.
  • ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธ ํ›„ ๊ฒŒ์‹œ๋ฌผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Œ!
  • ์‚ฌ์šฉ์ž๋Š” ๊ฒŒ์‹œ๋ฌผ์„ ์—…๋กœ๋“œํ•  ๋•Œ ์ตœ์†Œ ํ•œ ์žฅ์˜ ์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•ด์•ผ ํ•จ(ํ…์ŠคํŠธ๋Š” ๋น„์–ด๋„ ๊ดœ์ฐฎ์Œ)!

1. ๊ฒŒ์‹œ๋ฌผ ์—…๋กœ๋“œ ๋ผ์šฐํŠธ

  • auth ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•ด ๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œํ•œํ•จ.
  • multer๋ฅผ ์ด์šฉํ•ด์„œ ์ตœ๋Œ€ 10๊ฐœ์˜ ์ด๋ฏธ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๊ณ  AWS S3 ๋ฒ„ํ‚ท์— ์—…๋กœ๋“œํ•จ.
  • ๊ฐ ์ด๋ฏธ์ง€๋Š” ๊ณ ์œ ํ•œ ํŒŒ์ผ ์ด๋ฆ„์„ ๊ฐ€์ง€๊ณ , S3์— ์ €์žฅ๋œ ํ›„ URL์ด ์ƒ์„ฑ๋จ.

1) ์ฝ”๋“œ

// uploadPost.js

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

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

const multer = require("multer");
const { PutObjectCommand } = require("@aws-sdk/client-s3");
const storage = multer.memoryStorage();
const upload = multer({ storage }).array("images", 10); // ์ตœ๋Œ€ 10์žฅ์˜ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
const s3 = require("../../config/s3"); // S3 ํด๋ผ์ด์–ธํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ

// ๊ฒŒ์‹œ๋ฌผ ์—…๋กœ๋“œ
router.post("/", auth, upload, async (req, res) => {
  const { text } = req.body;
  const userId = req.user._id; // ๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž ID ๊ฐ€์ ธ์˜ค๊ธฐ

  if (!userId) {
    return res.status(401).json({
      message: "๋กœ๊ทธ์ธ๋œ ์‚ฌ์šฉ์ž๋งŒ ๊ฒŒ์‹œ๋ฌผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
    });
  }

  // ์ด๋ฏธ์ง€๊ฐ€ ์ตœ์†Œ ํ•œ ์žฅ ์ด์ƒ ํฌํ•จ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
  if (!req.files || req.files.length === 0) {
    return res.status(400).json({
      message: "์ตœ์†Œ 1์žฅ์˜ ์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•ด์•ผ ๊ฒŒ์‹œ๋ฌผ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
    });
  }

  try {
    // S3 ์—…๋กœ๋“œํ•  ์ด๋ฏธ์ง€ URL ๋ฐฐ์—ด ์ƒ์„ฑ
    const imageUrls = [];

    for (let file of req.files) {
      const filename = `${Date.now()}_${file.originalname}`;
      const uploadParams = {
        Bucket: "post-jae",
        Key: filename,
        Body: file.buffer,
        ContentType: file.mimetype,
      };

      // S3์— ํŒŒ์ผ ์—…๋กœ๋“œ
      await s3.send(new PutObjectCommand(uploadParams));
      const imageUrl = `https://${uploadParams.Bucket}.s3.${process.env.AWS_REGION}.amazonaws.com/${filename}`;
      imageUrls.push(imageUrl);
    }

    // ์ƒˆ๋กœ์šด ๊ฒŒ์‹œ๋ฌผ ์ƒ์„ฑ ๋ฐ ์ €์žฅ
    const newPost = new Post({
      user: userId,
      text: text || "",
      images: imageUrls,
    });

    await newPost.save();

    return res.status(201).json({
      message: "๊ฒŒ์‹œ๋ฌผ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์—…๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
      post: newPost,
    });
  } catch (err) {
    return res.status(500).json({
      message: "๊ฒŒ์‹œ๋ฌผ ์—…๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.",
      error: err.message,
    });
  }
});

module.exports = router;
// auth.js

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

let auth = (req, res, next) => {
  // ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์ฟ ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ
  let token = req.cookies.x_auth;
  console.log("Received token:", token); // ํ† ํฐ ๋กœ๊ทธ

  // ํ† ํฐ ๋ณตํ˜ธํ™” ํ›„ user ์ฐพ๊ธฐ
  User.findByToken(token)
    .then((user) => {
      if (!user) {
        throw new Error("์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค."); // ์‚ฌ์šฉ์ž ์—†์Œ
      }
      console.log("Authenticated user:", user); // ์‚ฌ์šฉ์ž ๋กœ๊ทธ
      req.token = token;
      req.user = user;
      return next();
    })
    .catch((err) => {
      console.error("Authentication error:", err.message); // ์—๋Ÿฌ ๋กœ๊ทธ
      return res.status(401).json({
        isAuth: false,
        message:
          err.message === "์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค."
            ? "๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
            : err.message,
      });
    });
};

module.exports = { auth };

 

2) ๊ธฐ๋Šฅ ์„ธ๋ถ€ ํ๋ฆ„

  1. multer์™€ memoryStorage๋ฅผ ์ด์šฉํ•ด ์ด๋ฏธ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๊ณ , ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ํŒŒ์ผ ๋ฐ›์Œ.
  2. ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํ•™์ธํ•˜๊ธฐ ์œ„ํ•ด auth ๋ฏธ๋“ค์›จ์–ด ํ˜ธ์ถœํ•จ.
  3. ์‚ฌ์šฉ์ž๊ฐ€ ์—…๋กœ๋“œํ•œ ๊ฐ ํŒŒ์ผ์— ๋Œ€ํ•ด ๊ณ ์œ  ํŒŒ์ผ ์ด๋ฆ„์„ ์ƒ์„ฑํ•˜๊ณ , PutObjectCommand๋ฅผ ํ†ตํ•ด S3์— ์—…๋กœ๋“œํ•จ
  4. S3 ์—…๋กœ๋“œ ํ›„ ๊ฐ ์ด๋ฏธ์ง€ URL์„ ์ˆ˜์ง‘ํ•ด MongoDB Post ์ปฌ๋ ‰์…˜์— ํ•จ๊ป˜ ์ €์žฅํ•จ.

3) ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ AWS S3 ์—ฐ๋™

  • MongoDB์— ์ €์žฅ๋˜๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
    • user: ๊ฒŒ์‹œ๋ฌผ ์ž‘์„ฑ์ž์˜ ID
    • text: ๊ฒŒ์‹œ๋ฌผ ๋‚ด์šฉ
    • images: S3์— ์—…๋กœ๋“œ๋œ ๊ฐ ์ด๋ฏธ์ง€์˜ URL ๋ฐฐ์—ด
    • createdAt: ์ƒ์„ฑ ์ผ์‹œ (์ž๋™ ์ƒ์„ฑ)
# mongoDB์— ์ €์žฅ๋œ ๊ฒŒ์‹œ๋ฌผ ๊ตฌ์กฐ

{
  "_id": "๊ฒŒ์‹œ๋ฌผ ID",
  "user": "์‚ฌ์šฉ์ž ID", 
  "text": "๊ฒŒ์‹œ๋ฌผ ๋‚ด์šฉ", 
  "images": [
    "https://post-jae.s3.ap-northeast-2.amazonaws.com/test.jpg"
  ],
  "createdAt": "2024-11-02T06:59:29.107+00:00",  # ๊ฒŒ์‹œ๋ฌผ ์ž‘์„ฑ ์‹œ๊ฐ„
}

 

2. ๊ฒฐ๊ณผ ๐ŸŽ‰

  • Postman์œผ๋กœ ๋กœ๊ทธ์ธ ํ›„, POST ๋ณด๋‚ด๊ธฐ

1) POST ์‹คํŒจ

  • ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์ง€ ์•Š์„ ๊ฒฝ์šฐ

  • ์ด๋ฏธ์ง€๊ฐ€ ์—†๋Š” ํ…์ŠคํŠธ ๊ฒŒ์‹œ๋ฌผ์„ ์—…๋กœ๋“œํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ฒฝ์šฐ


2) POST ์„ฑ๊ณต

  • MongoDB

  • AWS S3 ๋ฒ„ํ‚ท