로그인 기능 / Token 생성 후 쿠키에 저장 / error 해결 Expected "payload" to be a plain object

holang-i·2021년 5월 29일
0

Node & React

목록 보기
9/11
post-thumbnail

Login Router의 큰 흐름

  1. DB에서 요청한 e-mail찾기
  2. DB에서 요청한 e-mail이 있다면 비밀번호가 같은지 확인
  3. 비밀번호까지 같다면 Token을 생성
// 로그인 router 만들기
app.post('/login', (req, res) => {
  // 요청된 e-mail을 데이터베이스에 있는지 확인

  // 현재 e-mail이 데이터베이스에 있다면, password도 맞는 지 확인

  // 비밀번호까지 맞을 경우, Token 생성

});

요청된 이메일이 데이터베이스에 있는 지 확인

요청된 이메일이 데이터 베이스에 있는지 찾기 위해서는 mongoose에 있는 findOne 메서드를 사용해서 찾아야 된다.

// 로그인 router 만들기
app.post('/login', (req, res) => {``
  // 1. 요청된 e-mail을 데이터베이스에 있는지 확인
  User.findOne({ email: req.body.email }, (err, userInfo) => {
    if(!userInfo) {
      return res.json({ 
        loginSuccess: false, 
        message: '제공된 이메일에 해당하는 사용자가 없습니다. :('
      });
    }
  });
});

이메일이 등록되어 있다면 비밀번호가 같은지 확인

사용자가 로그인 시 입력한 이메일이 데이터베이스에 있다면,
이제 비밀번호가 같은지 확인하는 작업이 필요하다.

비밀번호가 같은 지 확인하는 작업은 User 모델에서 메소드를 하나 만들어서 확인 할 것이다.

우선 indes.js에서는 아래와 같은 작업이 필요하다.

// 로그인 router 만들기
app.post('/login', (req, res) => {
  // 1. 요청된 e-mail을 데이터베이스에 있는지 확인
  User.findOne({ email: req.body.email }, (err, userInfo) => {
    if(!userInfo) {
      return res.json({ 
        loginSuccess: false, 
        message: '제공된 이메일에 해당하는 사용자가 없습니다. :('
      });
    }
    // 2. 현재 e-mail이 데이터베이스에 있다면, password도 맞는 지 확인
    userInfo.comparePassword(req.body.password, (err, isMatch) => {
      if(!isMatch) {
        // 비밀번호가 틀린 경우
        return res.json({
          loginSuccess: false, 
          message: '비밀번호가 틀렸습니다.'
        });
      } 
    });
  });
});

비밀번호가 같은지 확인하는 함수 만들기

비밀번호가 같은지 비교하는 함수를 User 모델에서 만들어 볼 것이다.

// comparePassword method
userSchema.methods.comparePassword = function(plainPassword, callback) {
  // 로그인 시 입력한 비밀번호랑 데이터베이스에 있는 암호화된 비밀번호랑 같은지 비교하는 작업이 필요
  // mongoose에서는 복호화하는 메서드가 없기 때문에 로그인 시 입력한 비밀번호를 암호화해서 데이터베이스에 있는 비밀번호와 먼저 같은 지 비교해야된다.
  bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
    if(err) return callback(err);

    // 비밀번호가 같다.
    callback(null, isMatch);
  });
}

사용자인증, Token 생성

이제 비밀번호까지 같다는 것을 확인했으면, 사용자를 위한 Token을 생성해야된다.

토큰을 생성하기 위해서는 JSONWEBTOKEN이라는 라이브러리를 이용해야 된다.

$ npm install jsonwebtoken --save

jsonwebtoken npm 참고

https://www.npmjs.com/package/jsonwebtoken

jsonwebtoken을 import한 뒤

var jwt = require('jsonwebtoken');

sign 메소드로 합쳐주면 토큰이 생성된다.

var token = jwt.sign({ foo: 'bar' }, 'shhhhh');


jsonwebtoken을 생성한 뒤 코드 작성

const jwt = require('jsonwebtoken');

  ...

// generateToken method
userSchema.methods.generateToken = function(callback)   {
  let user = this;

  // jsonwebtoken을 이용해서 Token 생성하기
  jwt.sign({ user._id})
};

user의 id를 가져오기 위해서 this로 user를 받아왔다.
그리고 id를 다룰 때는 DataBase에 있는 id를 다뤄야 되는데
mongoDB에서 id를 확인해 보니 컬럼명이 _id 이렇게 만들어져있었다.


Error: Expected "payload" to be a plain object

로그인 요청을 테스트하던 중 오류가 발생했다.

jwt.sign의 payload 부분에 user_.id를 넣었더니 아래와 같은 에러가 발생했다. 오류 문구는 아래의 사진과 같았다.

오류가 발생한 기존 코드

// generateToken method
userSchema.methods.generateToken = function(callback) {
  let user = this;

  // user._id + 'secretToken' --> Token을 만든다.
  // 그리고 토큰을 해석할 때 'secretToken'를 넣으면, suer._id가 나온다.
  const token = jwt.sign(user._id, 'secretToken');
  user.token = token; //userSchema의 token 필드에 생성한 토큰을 넣어준다.
  user.save(function(err, user) {
    if(err) return callback(err);

    callback(null, user);
  });
};

이것이 무슨 오류인지 검색해 보았는데 token을 만들 때 sign에서 받을 때,
plain object를 기대했는데 user._id는 그게 아니어서 발생한 에러였다.

plain object를 만드는 방법

해결 방법으로는

user._id.toJSON();
user._id.toHexString();
JSON.stringify(user);
Object.assign({}, user));

위와 같이 다양한 방법으로 해결 가능하다.

오류 해결 코드

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

  const token = jwt.sign(user._id.toHexString(), 'secretToken');
  user.token = token;
  user.save(function(err, user) {
    if(err) return callback(err);

    callback(null, user);
  });
};

token을 cookie에 저장하기

token을 저장한다. 어디에 저장할지는 여러군데에 할 수 있지만
token을 cookie or localStorage or session 에 저장 할 수 있는데 cookie에 token 저장해 볼 것이다.

cookie를 사용하기 위해서는 라이브러리를 추가해야 된다.

express에서 제공해주는 cookie-parser라는 것을 다운받아야 된다.
$ npm install cookie-parser --save

cookie에 token을 저장해 놓으면

아래의 cookie부분에 지정한 이름으로 token이 저장된다.

// 3. 비밀번호까지 맞을 경우, Token 생성
      userInfo.generateToken((err, user) => {
        if(err) return res.status(400).send(err);

        // token을 저장한다. 어디에 저장할지는 여러군데에 할 수 있지만
        // token을 cookie or localStorage or session 에 저장 할 수 있다.
        // cookie에 token 저장하기
        res.cookie("x_auth", user.token)
        .status(200)
        .json({ loginSuccess: true, userId: user._id});
      });

로그인 시 토큰 쿠키에 저장이 잘 되었는지 확인

로그인 확인, 암호화 된 userId도 확인


쿠키에 토큰잘 저장되었는지 확인







따라하며 배우는 노드, 리액트 시리즈를 통해 개념을 익히면서 실습하고 있습니다.
https://bit.ly/3wGxKGC

profile
🌿 주니어 프론트엔드 개발자입니다! 부족하거나 잘못된 정보가 있다면 알려주세요:)

0개의 댓글