[노드, 리액트 기초 | node.js] #9. 로그인 기능 구현

ppmyor·2022년 7월 10일
1

노드, 리액트 기초

목록 보기
9/26
post-thumbnail

대망의 기능 구현이다. 이를 위해 달려왔다..

로그인 기능은 다음과 같이 작동한다.

  1. 유저가 요청한 email(or ID)가 DB에 존재하는지 찾는다.
  2. 요청한 email(or ID)가 DB에 있다면 유저가 입력한 비밀번호가 맞는 비밀번호인지 확인한다.
  3. 비밀번호까지 같다면 token을 생성한다.

💁‍♀️ 1. index.js에 login router 만들기

app.post("/login", (req, res) => {});

우선, 로그인 기능을 구현 할 라우터를 생성해준다.

💁‍♀️ 2. 요청된 이메일 찾기

🙅‍♀️ 2-1. 유저가 없을 시

  User.findOne({ email: req.body.email }, (err, user) => {
    if (!user) {
      return res.json({
        loginSuccess: false,
        message: "제공된 이메일에 해당하는 유저가 없습니다.",
      });
    }

mongoDB에서 제공하는 findOne이라는 메소드를 활용해 user.email을 찾는다. 만약 요청한 email이 user 콜렉션 안에 없다면 user가 없기 때문에 이메일에 해당하는 유저가 없다는 메세지와 함께 loginSuccess에 false 값을 할당해준다.

🙋‍♀️ 2-2. 유저가 있을 시 비밀번호 검증

  • index.js
    user.comparePassword(req.body.password, (err, isMatch) => {
      if (!isMatch) return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다." });

요청한 이메일이 DB에 있다면 유저가 입력한 비밀번호가 맞는지 검증을 한다.
comparePassword라는 메소드로 요청한 비밀번호와 DB에 존재하는 비밀번호의 match 여부를 확인하는 코드를 작성한다. req.body.password(유저가 요청하는 비밀번호)와 callback function을 argument로 부여하고, 에러가 나면 비밀번호가 틀렸다는 메세지와 함께 loginSuccess에 false 값을 할당해준다.

  • User.js
userSchema.methods.comparePassword = function (plainPassword, cb) {
  bcrypt.compare(plainPassword, this.password, function (err, isMatch) {
    if (err) return cb(err);
    cb(null, isMatch);
  });
};

comparePassword라는 메소드는 User Model에서 만든다. 원리는 plainPassword와 암호화된 비밀번호를 같은지 체크하는데 암호화된 비밀번호를 다시 복호화할 수 없으니 plainPassword를 암호화해서 암호화 된 비밀번호끼리 같은지 확인을 한다.
만약에 암호화된 비밀번호끼리 같지 않다면 callback에 error를 넣고,
같다면 error는 없고(null) true가 할당된(같으므로 isMatch에 true가 할당됨) isMatch를 callback함수에 넣어 호출한다.

🙆‍♀️ 2-3. 비밀번호까지 맞다면 token을 생성

우선 토큰을 생성하기 위해서는 jsonwebtoken이라는 라이브러리가 필요하다.

  1. jsonwebtoken 설치
npm install jsonwebtoken --save

해당 명령어로 jsonwebtoken을 설치해준다.

  1. User Model에 jsonwebtoken 세팅
const jwt = require("jsonwebtoken");

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

  // jsonwebtoken을 이용해서 token을 생성하기
  let token = jwt.sign(user._id.toHexString(), "secretToken");
  user.token = token;
  user.save(function (err, user) {
    if (err) return cb(err);
    cb(null, user);
  });
};

generateToken이라는 메소드를 User Model에 생성한다. jwt.sign은 user._id와 "secretToken"을 합쳐서 token을 만들어준다. 후에, token을 해석할 때 secretToken을 넣으면 user._id가 나오므로 누구인지도 알 수 있다. userSchema 필드의 토큰에 토큰을 저장한 후 에러가 났다면 callback으로 error를 전달하고, 나지 않았다면 user 정보를 전달한다.

  1. cookie parser 설치
npm install cookie-parser --save

토큰은 cookie나 localStorage 등 여러 곳에 저장할 수 있는데, 해당 강좌에서는 cookie에 토큰을 저장했다.

  1. cookie parser 세팅
const cookieParser = require("cookie-parser");
app.use(cookieParser());
  1. generateToken 메소드 호출
      user.generateToken((err, user) => {
        if (err) return res.status(400).send(err);

        res.cookie("x_auth", user.token).status(200).json({ loginSuccess: true, userId: user._id });
      });
    });

User Model에서 생선한 generateToken 메소드를 호출한다. 만약 400 에러가 난다면 에러를 넘겨주고, 나지 않는다면 토큰을 cookie에 저장하는데, 해당 코드에서는 이름 x_auth로 cookie가 저장되게된다.

정리 코드

  • user.js
const jwt = require("jsonwebtoken");

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

  // jsonwebtoken을 이용해서 token을 생성하기
  let token = jwt.sign(user._id.toHexString(), "secretToken");
  user.token = token;
  user.save(function (err, user) {
    if (err) return cb(err);
    cb(null, user);
  });
};
  • index.js
const cookieParser = require("cookie-parser");
app.use(cookieParser());

app.post("/api/users/login", (req, res) => {
  User.findOne({ email: req.body.email }, (err, user) => {
    if (!user) {
      return res.json({
        loginSuccess: false,
        message: "제공된 이메일에 해당하는 유저가 없습니다.",
      });
    }
    user.comparePassword(req.body.password, (err, isMatch) => {
      if (!isMatch) return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다." });

      user.generateToken((err, user) => {
        if (err) return res.status(400).send(err);

        res.cookie("x_auth", user.token).status(200).json({ loginSuccess: true, userId: user._id });
      });
    });
  });
});

너무 왔다갔다해서 정리해서 코드를 작성해보았다.
로그인을 누가 간단한 기능이라고 무시했는가.. 세상 어렵고 백오십번 복습해야겠다.🥲

➕ 참고

따라하며 배우는 노드, 리액트 시리즈 - 기본 강의 를 공부하며 작성한 글입니다.

profile
유영하는 개발자

0개의 댓글