본 정리는 인프런 John Ahn 따라하며 배우는 노드, 리액트 시리즈 - 기본 강의를 참고하였습니다.

  • 전체 흐름도

    스크린샷 2022-12-28 오후 8 31 25

  1. 요청된 이메일을 데이터베이스에서 찾기

     //몽고DB에서 제공하는 find함수 사용
     // 1. 요청된 이메일을 데이터베이스에서 찾기
       User.findOne({email: req.body.email}, (err, user)=>{
         if(!user){
           return res.json({
             loginSuccess : false,
             massage : "제공된 이메일에 해당하는 유저가 없음"
           })
         }
    
  2. 요청된 이메일이 있다면 비밀번호 체크

    새로운 함수 생성(Model/User.js)파일 수정

     // Model/User.js 
     userSchema.methods.comparePassword = function(plainPassword,callbackfunk)
     {
         // plainPassword = 1234
         // database 암호 = 암호화된 비밀번호 
        
         bcrypt.compare(plainPassword, this.password, function(err, isMatch){
             if(err){
                 return callbackfunk(err);
             }
             else
                 callbackfunk(null, isMatch);
         })
     }
    
     // index.js파일 수정
     // 2. 요청된 이메일이 있다면 비밀번호 체크
         user.comparePassword(req.body.password, (err,isMatch)=>{
           if(!isMatch){
             return res.json({
               loginSuccess : false,
               massage : "비밀번호가 틀립니다"
             })
           }
    
  3. 위 조건을 모두 만족하면 Token 생성

    JsonWebToken 라이브러리 설치

    토큰 저장용 cookie-parser 라이브러리 설치

     npm install jsonwebtoken --save
     npm install cookie-parser --save
    

    새로운 함수 생성(Model/User.js)파일 수정

     // Model/User.js 
     const jwt = require('jsonwebtoken');
        
     userSchema.methods.generateToken = function(callbackfunk){
         //jsonwebtoken을 이용하여 토큰 생성
         var user = this;
         var token = jwt.sign(user._id.toHexString(), 'secretToken')
         // user._id + 'secretToken' = token
         // user.id + secretToken 을 이용하여 토큰을 생성하고 나중에 secretToken을 이용하여 user.id 확인 가능
         user.token = token
         user.save(function(err, user){
             if(err) return callbackfunk(err)
             callbackfunk(null, user)
         })
     }
    
     // index.js파일 수정
     const cookieParser = require('cookie-parser');
     app.use(cookieParser());
        
     // 3. 위 조건을 모두 만족하면 Token 생성
           user.generateToken((err,user)=>{
             if(err) return res.status(400).send(err)
             // 현재 user에는 토큰이 있음 토큰을 쿠키에 저장
             res.cookie("x_auth",user.token).status(200).json({
               loginSuccess : true,
               userID : user._id 
             })        
           })
    
    • 전체 코드 (User.js)

        const mongoose = require('mongoose');
        const bcrypt = require('bcrypt');
        const saltRounds = 10; // 10자리를 이용하여 생성
        const jwt = require('jsonwebtoken');
              
        const userSchema = mongoose.Schema({
            name:{
                type : String,
                maxlength : 50,
            },
            email:{
                type : String,
                trim : true, // space를 없애주는 역할
                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,
            },
        })
              
        userSchema.pre('save', function(next){
        	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()
            }
        })
              
        userSchema.methods.comparePassword = function(plainPassword,callbackfunk)
        {
            // plainPassword = 1234
            // database 암호 = 암호화된 비밀번호 
              
            bcrypt.compare(plainPassword, this.password, function(err, isMatch){
                if(err){
                    return callbackfunk(err);
                }
                else
                    callbackfunk(null, isMatch);
            })
        }
        userSchema.methods.generateToken = function(callbackfunk){
            //jsonwebtoken을 이용하여 토큰 생성
            var user = this;
            var token = jwt.sign(user._id.toHexString(), 'secretToken')
            // user._id + 'secretToken' = token
            // user.id + secretToken 을 이용하여 토큰을 생성하고 나중에 secretToken을 이용하여 user.id 확인 가능
            user.token = token
            user.save(function(err, user){
                if(err) return callbackfunk(err)
                callbackfunk(null, user)
            })
        }
              
        const User = mongoose.model('User', userSchema) //스키마를 모델로 감싸줌
        module.exports = {User} //다른곳에서 사용할 수 있게 하기위해
      
    • 전체 코드 (index.js)

        const express = require('express');
        const app = express();
        const port = 3000
              
        const mongoose = require('mongoose');
        const {User} = require("./Models/User");// 미리 정의했던 모델 가져오기
        const bodyParser = require('body-parser');
        const config = require("./config/key");
        const e = require('express');
        const cookieParser = require('cookie-parser');
              
        // 데이터 분석을 위한 추가 설정
        app.use(cookieParser());
        app.use(bodyParser.urlencoded({extended:true}));  
        app.use(bodyParser.json());
              
        mongoose.set('strictQuery',true)
        mongoose.connect(config.mongoURI,
        {
            useNewUrlParser: true, useUnifiedTopology: true 
        }).then(() => console.log('Successfully connected to mongodb'))
        .catch(e => console.error(e));
              
        app.get('/', (req, res) => {
          res.send('Hello World!')
        })
              
        app.post('/api/users/register', (req,res) =>{
            // 회원 가입할 때 필요한 정보들을 클라이언트로부터 받으면 데이터베이스에 정보 저장
            // 미리 정의했던 모델을 가져와야 함
            const user = new User(req.body);
              
            user.save((err, userInfo) =>{// user모델에 정보들 저장
                //만약 에러가 발생 시 json형식으로 에러와 에러메시지 전달
                if(err) return res.json({success:false, err})
                return res.status(200).json({
                    success:true
                })
            })
        })
              
        app.post('/api/users/login', (req, res) =>
        {
          // 1. 요청된 이메일을 데이터베이스에서 찾기
          User.findOne({email: req.body.email}, (err, user)=>{
            if(!user){
              return res.json({
                loginSuccess : false,
                massage : "제공된 이메일에 해당하는 유저가 없음"
              })
            }
          // 2. 요청된 이메일이 있다면 비밀번호 체크
            user.comparePassword(req.body.password, (err,isMatch)=>{
              if(!isMatch){
                return res.json({
                  loginSuccess : false,
                  massage : "비밀번호가 틀립니다"
                })
              }
              // 3. 위 조건을 모두 만족하면 Token 생성
              user.generateToken((err,user)=>{
                if(err) return res.status(400).send(err)
                // 현재 user에는 토큰이 있음 토큰을 쿠키에 저장
                res.cookie("x_auth",user.token).status(200).json({
                  loginSuccess : true,
                  userID : user._id 
                })        
              })
            })
          })
        })
              
        app.listen(port, () => {
          console.log(`Example app listening on port ${port}`)
        })
      

    로그인 테스트

    포스트맨을 이용하여 로그인 확인

    • 정보를 잘못 입력했을 때

      스크린샷 2022-12-28 오후 9 12 17

    • 정보를 제대로 입력했을 때

      스크린샷 2022-12-28 오후 9 13 16

    제대로 나오는걸 확인할 수 있다.