[Node.js] 기초 노드 리액트 강의 정리(1)

eunbi·2022년 8월 10일

기초 노드 리액트 강의 정리

FE : React
BE : Node.js
DB : MongoDB

#2 NODE JS, EXPRESS JS 다운

  • express.js : node.js를 위한 웹 프레임워크.
  1. node.js 설치

  2. boiler-plate 디렉토리를 만든다.

  3. 해당 폴더 안에서 npm init (기본 설정을 따라간다. enter!)

    • package.json 에서 해당 설정을 볼 수 있다.
  4. express.js 설치 : boiler-plate $ npm install express --save

    • --save 구문은 package.json에 자동으로 dependency를 추가해준다.
  5. index.js 파일을 만든다. 그리고 package.json에 start를 수정한다.

    // index.js
    const express = require('express')
    const app = express()
    const port = 7777
    
    app.get('/', (req, res) => res.send('Hello World!'))
    app.listen(port, () => console.log(`Example app listening on port ${port}!`))
    // package.json
    "scripts": {
        "start": "node index.js", // <- 추가
        "test": ...
      },
  6. 이제 npm run start 로 시작할 수 있다.

#3 몽고DB 연결

  1. 몽고DB를 사용하기 위한 프레임워크인 mongoose를 설치 : npm install mongoose --save

  2. 몽고DB 홈페이지에서 클러스터 생성. 접속 userid, password를 만들어준다.

  3. user키를 복사후, index.js에 다음을 추가한다.

    // index.js
    const mongoose = require('mongoose')
    mongoose.connect('mongodb+srv://led:<password>@boilerplate.7zof7an.mongodb.net/?retryWrites=true&w=majority')
        .then(() => console.log('MongoDB Connect...'))
        .catch(err => console.log(err))

#4 MongoDB Model & Schema

  • 모델 : 스키마를 감싸주는 것.
  • 스키마 : 하나하나의 정보를 지정.
  1. 유저 모델을 생성할 것이다. models 폴더 생성. User.js를 생성하고 다음을 작성.

    // models/User.js
    const mongoose = require('mongoose');
    
    const userSchema = 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
        }
    })
    
    const User = mongoose.model('User', userSchema)
    
    module.exports = { User }

#7 BodyParser & PostMan & 회원

  • http 리퀘스트를 파서해주는 bodyparser 설치 : npm install body-parser --save --no-fund
    • 다만 요즘은, express에 기본적으로 포함되어 있음.

         // index.js에 해당코드 추가
         app.use(express.json());
         app.use(express.urlencode({extended: true}));
      // index.js
      const bodyParser = require('body-parser')
      const { User } = require("./models/User") // 유저 모델 가져오기
      ...
      // application/x-www-form-urlencoded 분석 가능
      app.use(bodyParser.urlencoded({extended: true}));
      // application/json 분석 가능
      app.use(bodyParser.json())
      ...
      app.post('/api/users/register', (req, res) => { // 클라이언트에서 가져온 정보를 데이터베이스에 넣는다.
          const user = new User(req.body) // 바디파서로 조각조각낸 정보
      
          user.save((err, userInfo) => {   // 유저 모델에 저장. +콜백함수
              if (err) return res.json({ success: false, err})
              return res.status(200).json({
                  success: true
              })
          })
      })

#8 Nodemon 설치

  • npm 재실행 없이 코드 새로고침 가능한 nodemon : npm install nodemon --save-dev
    • (배포환경X)개발 환경에서만 사용할 것이므로, -dev 옵션.
  • 패키지.json에 실행시키기 위한 약어 추가 (dev), 실행 : npm run dev
    // package.json
    "scripts": {
        "start": "node index.js",
        "dev": "nodemon index.js", // <- 추가.
        "test": "echo \"Error: no test specified\" && exit 1"
      },

#9 비밀 설정 정보 관리

  • 몽고DB의 유저 아이디, 비밀번호를 지키기 위해 필요.
    1. config 폴더에 해당 파일들을 생성.
      - key.js, dev.js, prod.js
    2. 환경 변수에 따라 무슨 모듈로 연결시킬지 설정하는 key.js . local 환경(개발환경)에서는 dev, 배포환경에서는 prod

 // key.js
 if(process.env.NODE_ENV === 'production') {
     module.exports = require('./prod');
 } else {
     module.exports = require('./dev');
 }
 
 // dev.js
 module.exports ={
     mongoURI: 'mongodb+srv://led:led1234@boilerplate.7zof7an.mongodb.net/?retryWrites=true&w=majority'
 }
 
 // prod.js
 module.exports = {
     mongoURI: process.env.MONGO_URI
 }
 // index.js
 const config = require('./config/key');
 ...
 mongoose.connect(config.mongoURI) // 변수로 교체!
     .then(() => console.log('MongoDB Connect...'))
     .catch(err => console.log(err))

#10 Bcrypt로 비밀번호 암호화 하기

  1. 비밀번호 암호화를 위해 bcrypt 설치 : npm install bcrypt --save

  2. user.js에 bcrypt 추가

    // user.js
    const bcrypt = require('bcrypt');
    const saltRounds = 10; // salt를 이용해 비밀번호를 암호화. (saltRounds = 몇글자인지)
  3. 비밀번호 변경 시 암호화해서 저장해주는 함수 추가.

    // user.js
    userSchema.pre('save', function( next ) {
        var user = this;
    
        if (user.isModified('password')) {  // 패스워드를 바꿀 때만 해당 함수 실행 (이메일 등등 해당X)
            // 비밀번호 암호화
            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()
        }
        
    }) // 몽구스를 이용. save 전에 해당 함수를 실행. 끝난 후 next로 돌아감.

#11, 12 로그인 기능 with Bcrypt & 토큰 생성 with jsonwebtoken

  1. jsonwebtoken 추가 : npm install jsonwebtoken --save

    • 유저가 입력한 패스워드를 비교하는 comparePassword. plainpw를 받아오고, 유저의 비밀번호와 bcrypt.compare로 비교한다.
    • 로그인 토큰을 생성하는 generateToken.
    // user.js
    const jwt = require('jsonwebtoken');
    ...
    userSchema.methods.comparePassword = function(plainPassword, cb) {
        bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
            if(err) return cb(err);
            cb(null, isMatch);
        })
    }
    
    userSchema.methods.generateToken = function(cb) {
        var user = this;
        // jsonwebtoken 이용하여 토큰 생성.
        var token = jwt.sign(user._id.toHexString(), 'secretToken')
        // user._id + 'secretToken' = token ... 'secretToken' -> user._id . => 따라서 이때 만든 토큰도 기억해줘야 함.
        user.token = token
        user.save(function (err, user) {
            if(err) return cb(err)
            cb(null, user)
        })
    
    }
  2. 쿠키파서 설치 : npm install cookie-parser --save

    1. 이메일이 데이터베이스에 있는지
    2. 있다면, 비밀번호가 맞는지 comparePassword
    3. 둘 다 맞다면, 로그인 토큰 만들기 generateToken
    // index.js
    const cookieParser = require('cookie-parser')
    ...
    app.use(cookieParser())
    ...
    app.post('/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 })
                })
            })
        
        })
    
        
    })

#13 auth 기능 만들기

: 로그인이 되어있는지, 권한이 부여되어 있는지를 확인하기 위해 필요한 기능. 토큰을 가져와 유저 토큰과 비교!

  1. index.js에 해당 라우터 추가.
// index.js
const { auth } = require("./middleware/auth")
...
app.get('/api/users/auth', auth, (req, res) => {
    // 미들웨어가 있으므로, 여기까지 온거면, authentication이 true인것.
    res.status(200).json({
        _id: req.user._id,
        isAdmin: req.user.role === 0 ? false : true,
        isAuth: true,
        email: req.user.name,
        lastname: req.user.lastname,
        role: req.user.role,
        image: req.user.image
    })
})
  1. get 미들웨어로 auth가 있음. middleware 폴더를 만들고 auth.js 만들기
// auth.js
const { User } = require("../models/User");

let auth = (req, res, next) => {
    // 클라이언트 쿠키에서 토큰을 가져오기
    let token = req.cookies.x_auth;
    // 토큰 복호화 후, 유저 찾기
    User.findByToken(token, (err, user) => {
        if (err) throw err;
        if (!user) return res.json({ isAuth: false, error: true})
        req.token = token;
        req.user = user;
        next();
    })

    // 유저O -> 인증O
}   // 인증 처리 하기.

module.exports = { auth };
// User.js
userSchema.statics.findByToken = function(token, cb) {
    var user = this;
    // 토큰 디코드
    jwt.verify(token, 'secretToken', function(err, decoded) {
        // 복호화하여 나온 유저 아이디를 찾는다.
        user.findOne({"_id": decoded, "token": token}, function(err, user) {
            if (err) return cb(err);
            cb(null, user);
        })
    })
}

#14 로그아웃 기능

로그인할때 부여한 토큰을 지워 로그아웃한다.

// index.js
app.get('/api/users/logout', auth, (req, res) => {
    User.findByIdAndUpdate({ _id: req.user._id},
        { token: "" }
        , (err, user) => {
            if (err) return res.json({ success: false, err });
            return res.status(200).send({
                success: true
            })
        })
})


0개의 댓글