과거 프로그래밍 자료들/프로젝트

프로젝트 수정 2 - node.js로만 구성된 프로젝트에 리액트 추가(서버 항목 : 로그인/인증)

평부 2022. 5. 13. 15:45

* 현재 진행상황

- 프로젝트에 CRUD만 만든 상황

- fetch로 로그인 했으나 auth로 로그인 및 react로 로그인 화면을 만드는 것이 중요하다고 판단

- node.js 화면단을 ejs로 만들었으나 react에서 js로 만들 예정

- 참고 강의 : 따라하면서 배우는 노드, 리액트(로그인 부분)

 

[로그인 만들기 - server 부분]

1. index.js에 router 만들기 

2. auth.ctrl.js에서 기능만들기

 - mongoDB에 저장한 users와 이메일이 맞는지

- 이메일이 맞다면 비밀번호가 같은지

- 비밀번호가 갖다면 토큰을 생성할 것

3. auth.ctrl에 들어간 기능들을 models/User.js에서 생성하기

 -  comparePassword : 저장된 비밀번호와 입력한 비밀번호가 일치하는지

 - generateToken : 비밀번호가 맞을 경우 토큰 생성하기

4. postman에서 확인하기

 

 

* cookie 사용 

- app.js 혹은 index.js(서버 가동하는 js 파일에) cookie-parser 설치할 것

- app.use(cookieParser()); 추가할 것

 

 

1. index.js에 router 만들기 - [routes/auth/index.js]

 

const express = require("express");
const router = express.Router();
const authCtrl = require("./auth.ctrl");

router.post("/api/user/register", authCtrl.process.register);
router.post("/api/user/login", authCtrl.process.login);

module.exports = router;

 

 

2. auth.ctrl.js에서 기능만들기 - [routes/auth/auth.ctrl.js]

① mongoDB에 저장한 users와 이메일이 맞는지

② 이메일이 맞다면 비밀번호가 같은지

③ 비밀번호가 갖다면 토큰을 생성할 것 //res.cookie("x_auth")는 인증부분에서도 사용됨

 

"use strict";
const { User } = require("../../models/home/user");

const process = {
  register: (req, res) => {//회원가입 부분}
  
  login: (req, res) => {
    //① mongoDB에 저장한 users와 이메일이 맞는지
    User.findOne({ email: req.body.email }, (err, user) => {
      if (!user) {
        return res.json({
          loginSuccess: false,
          message: "제공된 이메일에 해당하는 유저가 없습니다.",
        });
      }
      //② 이메일이 맞다면 비밀번호가 같은지
      //comparePassword와 isMatch는 user.js에서 만들어짐
      user.comparePassword(req.body.password, (err, isMatch) => {
        if (!isMatch) {
          //false
          return res.json({
            loginSuccess: false,
            message: "비밀번호가 틀렸습니다.",
          });
        }
        //③ 비밀번호가 갖다면 토큰을 생성할 것
        //generateToken 또한 user.js에서 만들어짐
        user.generateToken((err, user) => {
          if (err) return res.status(400).send(err);

          //토큰을 저장한다 어디에? 1. 쿠키, 2. 로컬스토리지
          res
            .cookie("x_auth", user.token)
            .status(200)
            .json({ loginSuccess: true, userId: user._id });
        });
      });
    });
  },
};

module.exports = {
  process,
};

 

 

3. auth.ctrl에 들어간 기능들을 생성하기 - [models/user.js]

 

 

① 비밀번호가 일치하는지 : comparePassword와 isMatch

 

const mongoose = require("mongoose");
const bcrypt = require("bcrypt"); //암호화 하기 위함
const saltRounds = 10;
const jwt = require("jsonwebtoken"); //토큰 생성하기 위함

const userSchema = new mongoose.Schema({ // 스키마 });

userSchema.pre("save", function (next) { // 회원가입 });

userSchema.methods.comparePassword = function (plainPassword, cb) {
  //this가 가리키는 대상, userSchema / this 사용 시 function 사용할 것
  var user = this; 
  //plainPassword : 암호화 전 비밀번호 cb : 암호화된 비밀번호
  bcrypt.compare(plainPassword, user.password, function (err, isMatch) {
    if (err) return cb(err);
    cb(null, isMatch);
  });
};

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

module.exports = { User };

 

 

* schema 다음 .methods 붙이는 이유 

* 출처 : (https://mongoosejs.com/docs/guide.html)

 

 

* bcrypt에서 비밀번호 비교하는 방법 - 1 

* 출처 : (https://www.npmjs.com/package/bcrypt)

 

 

* bcrypt에서 비밀번호 비교하는 방법 - 2

- isMatch는 예시의 result라고 생각하기

 

 

② 토큰을 생성하기 : generateToken 

- 토큰의 이름은 'secretToken'으로 정의 -> 인증 부분에 필요한 것 

 

const mongoose = require("mongoose");
const bcrypt = require("bcrypt"); //암호화 하기 위함
const saltRounds = 10;
const jwt = require("jsonwebtoken"); //토큰 생성하기 위함

const userSchema = new mongoose.Schema({ // 스키마 });

userSchema.pre("save", function (next) { // 회원가입 });

userSchema.methods.comparePassword = function (plainPassword, cb) {
  //비밀번호 비교하기
};

userSchema.methods.generateToken = function (cb) {
  //user값 가져오기
  var user = this;

  //jsonwebtoken을 이용해 token 생성
  var token = jwt.sign(user._id.toHexString(), "secretToken");

  user.token = token;
  user.save(function (err, user) {
    if (err) return cb(err);
    cb(null, user);
  });
};

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

module.exports = { User };

 

* token을 사용하는 방법

- 출처 : (https://www.npmjs.com/package/jsonwebtoken)

* user._id.toHexString() 사용 이유 

- 예시의 {foo: 'bar'}의 타입은 object , 하지만 user._id는 String값이 아니므로 toHexString() 메서드를 이용 

- 출처 : (https://velog.io/@bigbrothershin/Backend-MongoDB-%EB%A9%94%EC%84%9C%EB%93%9C-ObjectID.toHexString)

 

 

 

[models/user.js] 전체 코드

 

const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10;
const jwt = require("jsonwebtoken");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    maxlength: 50,
  },
  email: {
    type: String,
    trim: true,
    unique: 1,
  },
  password: {
    type: String,
    minlength: 5,
  },
  lastname: {
    type: String,
    maxlength: 50,
  },
  role: {
    type: Number,
    default: 0,
  },
  image: String,
  token: {
    type: String,
  },
  tokenExp: {
    type: Number,
  },
});

//user.save 전에 작동
userSchema.pre("save", function (next) {
  //위의 user 가져오기
  var user = this;
  //비밀번호 바꿀 때 조건
  if (user.isModified("password")) {
    //비밀번호 암호화
    bcrypt.genSalt(saltRounds, function (err, salt) {
      if (err) return next(err);
      bcrypt.hash(user.password, salt, function (err, hash) {
        if (err) return next(err);
        user.password = hash;
        next();
      });
    });
  } else {
    //비밀번호가 바뀌지 않을 때 next()가 바로 시작됨
    next();
  }
});

userSchema.methods.comparePassword = function (plainPassword, cb) {
  //위의 user 가져오기
  var user = this;
  //plainPassword : 암호화 전 비밀번호 cb : 암호화된 비밀번호
  bcrypt.compare(plainPassword, user.password, function (err, isMatch) {
    if (err) return cb(err);
    cb(null, isMatch);
  });
};

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

  //jsonwebtoken을 이용해 token 생성
  var token = jwt.sign(user._id.toHexString(), "secretToken");

  user.token = token;
  user.save(function (err, user) {
    if (err) return cb(err);
    cb(null, user);
  });
};

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

module.exports = { User };

 

 

4. postman에서 확인하기

로그인이 맞는 경우
이메일이 틀린 경우(즉 DB에 없는 경우)
비밀번호가 틀린 경우