MERN - 로그인 라우터 추가, JWT

bkboy·2022년 7월 21일
0

웹 개발

목록 보기
24/26
post-thumbnail

로그인

로그인은 크게 3단계로 나눠서 생각할 수 있다.

  1. 요청된 이메일을 db에서 찾는다.
  2. 요청된 이메일을 찾으면 비밀번호가 맞는지 비교한다.
  3. 비밀번호가 갖다면 토큰을 생성한다.

Token 인증

클라이언트가 서버에 접속을 하여 로그인하면 서버에서 해당 클라이언트에게 인증되었다는 의미로 토큰을 부여한다. 이 토큰은 유일하며 토큰을 발급받은 클라이언트는 또 다시 서버에 요청을 보낼 때 요청 헤더에 토큰을 심어서 보내게 된다. 그러면 서버는 클라이언트로 받은 토큰을 서버에서 제공한 토큰과 일치 여부를 체크하여 인증 과정을 처리하게 된다.

JWT(JSON Web Token)

jwt는 인증에 필요한 정보들을 암호화시킨 json토큰이다.

코드

로그인과정 순서대로 코드를 작성해보자.

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

앞서 언급한 3가지 절차는 /login을 엔드포인트로 갖는 라우터 안에 있어야하는 코드들이다.

  1. 요청된 이메일을 db에서 찾는다.
User.findOne({ email: req.body.email }, (err, userInfo) => {
        if (!userInfo) {
            return res.json({
                loginSuccess: false,
                message: '제공된 이메일에  해당하는 유저가 없습니다.',
            });
        } 
  ...

데이터 베이스에서 email 값이 요청된 이메일과 같다면 가져오고 없다면 실패 메세지를 json으로 출력한다.

  1. 이메일이 있다면 비밀번호가 일치하는지 확인한다.
userInfo.comparePassword(req.body.password, (err, isMatch) => {
            if (!isMatch)
                return res.json({
                    loginSuccess: false,
                    message: '비밀번호가 틀렸습니다.',
                });
  ...

userInfo는 이메일을 찾아냈으니 해당 유저정보를 담고있다.
comparePassword User모델에서 사용할 수 있는 함수이고 직접 정의해야한다.

// user.js
userSchema.methods.comparPassword = function (plainPassword, callback) {

    bcrypt.compare(plainPassword, this.password, function (err, isMatch) {
        if (err) return callback(err);
        callback(null, isMatch);
    });
};

프로토타입에 함수를 정의할 때와 비슷하게 생각하며 된다.
bcrypt의 compare함수로 암호화되기 전과 암호화된 후의 비밀번호를 비교하고 다르면 callback 함수에 err넣어서 보낸다.

  1. 비밀번호가 일치하면 토큰을 생성한다.

토큰은 클라이언트에 저장되기 때문에 저장할 방법을 알아야한다. 쿠키에 저장하거나 로컬스토리지에 저장하는 것이 대표적이다. 나는 쿠키에 저장하는 방법을 택했다.

npm install cookie-parser --save

쿠키를 쉽게 다룰수 있는 미들웨어인 cookie-parser를 다운받는다.

// index.js
...
const cookieParser = require('cookie-parser');
..
app.use(cookieParser());
...
app.post('/login', (req, res) => {
  ...
  userInfo.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 });
            });
  ...
});

cookie-parser를 사용하기위한 준비를 마치고, login라우트 안에 토큰을 생성하는 함수를 넣는다. 이것도 물론 user.js에서 직접 만든 함수이다.

// user.js
...
const jwt = require('jsonwebtoken');
...
userSchema.methods.generateToken = function (callback) {
    // jsonwebtoken 이용해서 token 생성
    let user = this;

    let token = jwt.sign(user._id.toHexString(), 'secretToken');
    user.token = token;
    user.save(function (err, user) {
        if (err) return callback(err);
        callback(null, user); // 토큰이 저장된 유저를 반환
    });
};

user.js엔 토큰을 만드는 jwt를 추가해준다.

npm install jsonwebtoken --save

원래 빈문자열이였던 토큰을 제대로된 값을 넣어주고 저장한다. 실패시 err를 반환한다.

정상적으로 토큰이 생성이 되면 쿠키에 저장을 하고 로그인 성공 메세지를 보여준다.

❗❗ 포인트

generateToken 함수는 token을 데이터베이스에 저장하는 로직을 로그인 라우트는 generateToken 함수를 통과하고 나온 user데이터 안의 토큰을 쿠키(클라이언트)에 저장하는 로직을 각각 담고있다!

이것이 중요한 이유는 후에 auth 라우터에서 쿠키에 저장된 토큰과 데이터베이스에 저장된 토큰을 비교하여 인증을 진행하기 때문이다!

참고

profile
음악하는 개발자

0개의 댓글