Backend ๐Ÿ“š/Node.js

[Node]Instagram Clone - 3. ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • API, ์ด๋ฉ”์ผ ์ธ์ฆ ์žฌ์š”์ฒญ API

leejaejae 2024. 10. 27. 18:48

1. ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •

- ํšŒ์›๊ฐ€์ž…์ด ์™„๋ฃŒ๋œ ์‚ฌ์šฉ์ž๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •์ด ๊ฐ€๋Šฅํ•จ.
- ํšŒ์›๊ฐ€์ž… ์‹œ ์ž…๋ ฅํ–ˆ๋˜ ์ด๋ฉ”์ผ์ผ๋กœ ์ธ์ฆ์ฝ”๋“œ๋ฅผ ์ „์†กํ•˜๊ณ , ์œ ํšจํ•œ ์ธ์ฆ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ–ˆ์„ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ.
- ์ดํ›„ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์•”ํ˜ธํ™” ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ , ์•”ํ˜ธํ™”๊ฐ€ ์ง„ํ–‰๋˜์—ˆ๋‹ค๋ฉด DB์— ์ €์žฅ๋จ(๋ฎํ˜€์“ฐ๋Š” ๊ฒƒ).

1) ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๋ผ์šฐํŠธ

// resetPassword.js

const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs"); // bcryptjs ์‚ฌ์šฉ
const { User } = require("../../models/User");

// ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๋ผ์šฐํŠธ
router.post("/reset-password", async (req, res) => {
  const { email, newPassword } = req.body;

  try {
    const user = await User.findOne({ email });

    if (!user) {
      return res
        .status(400)
        .json({ success: false, message: "์œ ํšจํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค." });
    }

    // ์ƒˆ๋กœ์šด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ bcryptjs๋กœ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์ €์žฅ
    const hashedPassword = await bcrypt.hash(newPassword, 10);
    console.log("์•”ํ˜ธํ™”๋œ ์ƒˆ๋กœ์šด ๋น„๋ฐ€๋ฒˆํ˜ธ:", hashedPassword);

    // ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ user ๊ฐ์ฒด์— ํ• ๋‹น
    user.password = hashedPassword;
    user.passwordResetCode = undefined; // ์ธ์ฆ์ฝ”๋“œ ์ œ๊ฑฐ
    user.passwordResetExpires = undefined; // ์ธ์ฆ์ฝ”๋“œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์ œ๊ฑฐ
    await user.save(); // ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ

    // ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ํ™•์ธ
    const savedUser = await User.findOne({ email });

    // ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Œ์„ ์‘๋‹ต
    res.status(200).json({
      success: true,
      message: "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
    });
  } catch (error) {
    console.error("๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์ค‘ ์˜ค๋ฅ˜:", error);
    res.status(500).json({
      success: false,
      message: "๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.",
    });
  }
});

module.exports = router;


2) ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ์š”์ฒญ ๋ผ์šฐํŠธ

// resetPasswordRequest.js

const express = require("express");
const router = express.Router();
const { User } = require("../../models/User");
const {
  sendPasswordResetEmail,
} = require("../../utils/sendPasswordResetEmail");
const crypto = require("crypto"); // ์ธ์ฆ์ฝ”๋“œ ์ƒ์„ฑ์— ์‚ฌ์šฉํ•  ๋ชจ๋“ˆ

// ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ์š”์ฒญ ๋ผ์šฐํŠธ
router.post("/request-reset-password", async (req, res) => {
  const { email } = req.body;

  try {
    const user = await User.findOne({ email });

    if (!user) {
      return res
        .status(400)
        .json({ success: false, message: "์œ ํšจํ•œ ์ด๋ฉ”์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค." });
    }

    // ์ธ์ฆ์ฝ”๋“œ ์ƒ์„ฑ
    const verificationCode = crypto.randomBytes(3).toString("hex");
    user.passwordResetCode = verificationCode;
    user.passwordResetExpires = Date.now() + 300000; // ์ธ์ฆ์ฝ”๋“œ ์œ ํšจ ๊ธฐ๊ฐ„ 5๋ถ„ ์„ค์ •
    await user.save();

    // ์ด๋ฉ”์ผ๋กœ ์ธ์ฆ์ฝ”๋“œ ์ „์†ก
    await sendPasswordResetEmail(email, verificationCode);

    res
      .status(200)
      .json({ success: true, message: "์ธ์ฆ์ฝ”๋“œ๊ฐ€ ์ด๋ฉ”์ผ๋กœ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค." });
  } catch (error) {
    res
      .status(500)
      .json({ success: false, message: "์„œ๋ฒ„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." });
  }
});

module.exports = router;


3) ์ธ์ฆ์ฝ”๋“œ ๊ฒ€์ฆ ๋ผ์šฐํŠธ

// verifyCode.js

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

// ์ธ์ฆ์ฝ”๋“œ ๊ฒ€์ฆ ๋ผ์šฐํŠธ
router.post("/verify-reset-code", async (req, res) => {
  const { email, verificationCode } = req.body;

  try {
    const user = await User.findOne({ email });

    if (
      !user ||
      user.passwordResetCode !== verificationCode ||
      Date.now() > user.passwordResetExpires
    ) {
      return res
        .status(400)
        .json({ success: false, message: "์ธ์ฆ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค." });
    }

    // ์ธ์ฆ ์„ฑ๊ณต ์‹œ
    res.status(200).json({ success: true, message: "์ธ์ฆ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค." });
  } catch (error) {
    res
      .status(500)
      .json({ success: false, message: "์„œ๋ฒ„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." });
  }
});

module.exports = router;

 

2. ํšŒ์› ๊ฐ€์ž… ์‹œ ์ด๋ฉ”์ผ ์ธ์ฆ ์žฌ์š”์ฒญ

- ๊ธฐ์กด์˜ ์ฝ”๋“œ๋Š” ํ•œ๋ฒˆ๋งŒ ์ธ์ฆ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ์—ˆ์Œ.
- ์ฆ‰, ์ฝ”๋“œ ์ „์†ก ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด(ํ”„๋ก ํŠธ์—์„œ ๊ตฌํ˜„ ์˜ˆ์ •) ๋ฒ„ํŠผ์„ ๋˜ ๋ˆ„๋ฅด๊ฒŒ ๋˜๋„ ์ฒ˜์Œ ์ „์†ก๋ฌ๋˜ ์ธ์ฆ์ฝ”๋“œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•จ.
- ๋”ฐ๋ผ์„œ ์ด๋ฉ”์ผ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์ „์†กํ•˜๊ณ , ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์ „์†ก๋œ ์ธ์ฆ ์ฝ”๋“œ๋ฅผ ์œ ํšจํ•œ ์ฝ”๋“œ๋กœ ๊ฐ„์ฃผํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ฝ”๋“œ ์ˆ˜์ •ํ•จ.

// signUp.js

// ํšŒ์›๊ฐ€์ž… ์ฒ˜๋ฆฌ ๋ฐ ์ด๋ฉ”์ผ ์ธ์ฆ ์ฝ”๋“œ ๋ฐœ์†ก
router.post("/", async (req, res) => {
  const { email } = req.body;

  try {
    // ์ด๋ฉ”์ผ์ด ์ด๋ฏธ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      // ์ด๋ฉ”์ผ ์ธ์ฆ์ด ์ด๋ฏธ ์™„๋ฃŒ๋œ ์‚ฌ์šฉ์ž๋ผ๋ฉด ํšŒ์›๊ฐ€์ž…์„ ๋ง‰์Œ
      if (existingUser.isEmailVerified) {
        return res
          .status(400)
          .json({ success: false, message: "์ด๋ฏธ ๊ฐ€์ž…๋œ ์‚ฌ์šฉ์ž์ž…๋‹ˆ๋‹ค." });
      }
      // ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ์ฝ”๋“œ๋ฅผ ์žฌ์š”์ฒญํ•œ ๊ฒฝ์šฐ, ์ธ์ฆ์ฝ”๋“œ ์žฌ์ „์†ก ์ฒ˜๋ฆฌ
      const emailVerificationCode = crypto.randomBytes(3).toString("hex"); // 6์ž๋ฆฌ ์ฝ”๋“œ ์ƒ์„ฑ
      existingUser.emailVerificationCode = emailVerificationCode;
      existingUser.emailVerificationCodeExpires = Date.now() + 300000; // ์ธ์ฆ ์ฝ”๋“œ ์œ ํšจ์‹œ๊ฐ„ 5๋ถ„

      ...
    }

    // ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ธ์ฆ์ฝ”๋“œ ์ƒ์„ฑ ๋ฐ ์ด๋ฉ”์ผ ์ „์†ก
    const emailVerificationCode = crypto.randomBytes(3).toString("hex"); // 6์ž๋ฆฌ ์ฝ”๋“œ ์ƒ์„ฑ

    const user = new User({
      ...req.body,
      emailVerificationCode, // ์ƒ์„ฑ๋œ ์ธ์ฆ์ฝ”๋“œ ์ €์žฅ
      emailVerificationCodeExpires: Date.now() + 300000, // ์ธ์ฆ ์ฝ”๋“œ ์œ ํšจ์‹œ๊ฐ„ 5๋ถ„
    });

    ...
});

 

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

- ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •

- ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์ „

- ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์ธ์ฆ์ฝ”๋“œ ์ „์†ก

- ์ธ์ฆ์ฝ”๋“œ ์ž…๋ ฅ ๋ฐ ์„ฑ๊ณต

- ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ