MovieApp 만들기 (회원가입, 로그인)

김재훈·2023년 10월 31일

MovieApp 만들기

목록 보기
6/8

회원가입과 로그인 기능을 구현하기 위해 Express와 MongoDB를 사용한다.

회원가입

// server/model/User.js
import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
  userId: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
    minlength: 6,
  },
  name: {
    type: String,
    required: true,
    unique: true,
  },
});

const User = mongoose.model("User", userSchema);

export default User;

회원가입 기능에 필요한 유저 모델 스키마를 만들어준다.

// server/router/User.js
import User from "../model/User.js";
import pkg from "bcryptjs";

const router = Router();
const { genSalt, hash } = pkg;

router.post("/signup", async (req, res) => {
  const { userId, password, name } = req.body;

  try {
    const user = new User({
      userId,
      password,
      name,
    });

    const salt = await genSalt(10);
    user.password = await hash(password, salt);

    await user.save();
    res.status(200).send("Success");
  } catch (err) {
    console.error(err.message);
    res.status(500).send("Server Error");
  }
});

클라이언트에서 아이디, 비밀번호, 이름을 받아와서 새 모델을 만들어 MongoDB에 저장한다.
이때 bcryptgenSalt, hash를 사용해서 비밀번호를 암호화해서 저장한다.

로그인

// server/model/User.js
import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
  userId: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
    minlength: 6,
  },
  name: {
    type: String,
    required: true,
    unique: true,
  },
  token: {
    type: String,
  },
});

로그인 성공시 토큰을 생성하기 위해 모델 스키마에 token을 추가한다.

비밀번호를 비교하는 메서드

import bcrypt from "bcryptjs";

userSchema.methods.comparePassword = function (pwd, cb) {
  const user = this;

  bcrypt.compare(pwd, user.password, function (err, isMatch) {
    if (err) return cb(err, false);
    cb(null, isMatch);
  });
};

비밀번호를 암호화해서 저장했기 때문에 입력된 비밀번호도 암호화한 뒤 비교해준다. 두 암호가 같다면 콜백으로 true를 반환한다.

토큰을 생성하는 메서드

import jwt from "jsonwebtoken";

userSchema.methods.generateToken = function (cb) {
  const user = this;

  const token = jwt.sign(user._id.toHexString(), "secretToken");
  user.token = token;

  user
    .save()
    .then(() => {
      cb(null, user);
    })
    .catch((err) => {
      cb(err, null);
    });
};

jsonwebtoken을 사용해 토큰을 생성해 콜백함수로 넘겨준다.

// server/router/User.js

router.post("/signin", (req, res) => {
  const { userId, password } = req.body;

  User.findOne({ userId })
    .then((docs) => {
      if (!docs) {
        return res.json({
          loginSuccess: false,
          messsage: "입력하신 ID에 해당하는 유저가 없습니다.",
        });
      }

      docs.comparePassword(password, (err, isMatch) => {
        if (!isMatch)
          return res.json({
            loginSuccess: false,
            messsage: "비밀번호가 틀렸습니다.",
          });

        docs.generateToken((err, user) => {
          if (err) return res.status(400).send(err);
          res.cookie("x_auth", user.token).status(200).send("Success");
        });
      });
    })
    .catch((err) => {
      return res.status(400).send(err);
    });
});

로그인 성공시 토큰을 생성해서 쿠키에 저장한다.

인가

로그인 성공시 로그인 상태를 유지하기 위해 토큰을 가지고 있는지 검사를 한다.

// server/middleware/auth.js
import User from "../model/User.js";

let auth = (req, res, next) => {
  let token = req.cookies.x_auth;

  User.findByToken(token)
    .then((user) => {
      if (!user) return res.json({ isAuth: false, error: true });
      req.body.token = token;
      req.body.user = user;
      next();
    })
    .catch((err) => {
      throw err;
    });
};

export default auth;

클라이언트 쿠키에서 토큰을 가져와 복호화한 후 유저를 찾는다.

토큰을 복호화하는 메서드

// server/models/User.js
import jwt from "jsonwebtoken";

userSchema.statics.findByToken = function (token) {
  let user = this;

  return jwt.verify(token, "secretToken", (err, decoded) => {
    return user
      .findOne({ _id: decoded, token: token })
      .then((user) => user)
      .catch((err) => err);
  });
};

유저 아이디를 이용해 유저를 찾은 다음 클라이언트에서 가져온 토큰과 db에 저장된 토큰이 일치한지 확인한다.

// server/router/User.js

router.get("/auth", auth, (req, res) => {
  res.status(200).json({
    _id: req.body.user._id,
    isAuth: true,
    id: req.body.user.id,
    name: req.body.user.name,
  });
});

auth 미들웨어를 통과하면 유저 정보와 isAuth를 true로 넘겨준다.

로그아웃

// server/router/User.js

router.get("/logout", auth, (req, res) => {
  User.findOneAndUpdate({ _id: req.body.user._id }, { token: "" })
    .then(() => {
      res.clearCookie("x_auth");
      return res.status(200).send({ success: true, logout: "로그아웃 완료" });
    })
    .catch((err) => {
      res.json({ success: false, err });
    });
});

로그아웃은 간단하게 유저 아이디에 해당하는 토큰을 없애준다.

profile
김재훈

0개의 댓글