본문 바로가기

끄적대기

[Chapter 1] NodeJS

728x90
반응형

˙Install Package

▷ npm install express --save

   - NodeJS Framework

   - const express = require('express')

 

npm install mongoose --save

   - body 데이터를 분석하여 req.body 출력

   - const mongoose = require('mongoose')

 

npm install body-parser --save

   - MongoDB DataBase

   - const body-parser = require('body-parser')

 

npm install nodemon --save

   - Server 종료하지 않고 코드 변화에 따라 Server 자동 재시작

   - 'scripts' : {'start': 'nodemon index.js'}

 

npm install bcrypt --save

   - bcrypt salt 항목을 이용하여 비밀번호를 암호화

   - const saltRounds = 10 (salt 자리 수)

 

npm install jsonwebtoken --save

   - 토큰 생성 (특정 데이터 조합을 통한 토큰 생성)

   - const body-parser = require('jsonwebtoken')

 

npm install cookie-parser --save

   - 생성된 토큰을 쿠키(cookie)에 저장

   - const body-parser = require('cookie-parser ')

 

npm install --save option

   Node Package Manager(npm)은 프로젝트에 필요한 Library를 다운로드 또는 관리하도록 하는 프로그램으로, 해당 구문 사용 시, package.json 파일을 생성하여 관리하고, npm init 명령어를 통하여 기본 설정이 가능하다. 더불어, --save 옵션을 사용함으로써, dependency 항목에 모듈을 추가하고, 이를 통하여 모듈의 의존성 관리가 가능하다(모듈의 의존성 관리는, package.json의 dependencies 항목을 통하여 npm이 자동으로 프로젝트에 의존된 모듈을 참조(다운로드)하지만, npm5 버전부터는 해당 옵션을 사용하지 않아도 자동으로 추가할 수 있도록 지원).

 

   ★ 추가적으로 관련된 옵션을 통하여 dependency의 저장 방식을 설정

 

    ☆ -P or --save-pord

       - package.json의 dependencies에 package를 등록

       - 개발/운영서버 분리 시, 운영서버 개념(DEFAULT)

 

     ☆ -D or --save-dev

       - package.json의 devDependencies에 package를 등록

       - 개발서버 개념(develop vs production)

 

    ☆ -O or --save-optional

       - package.json의 optionalDependencies에 package를 등록

       - 사용을 원하는 모듈이지만, 설치가 실패하거나 참조할 수 없어도 npm package 설치 과정에 중단되지 않도록 할 때 사용

 

    ☆ --no-save

       - dependencies에 package를 등록하지 않음

 

˙Server Start (Documents Directory)

 npm run start

   - package.json → 'scripts' : {'start': 'node index.js'}

 

 

∴ CODE LOGIC

 

const express = require('express')
const app = express()
const port = 5000  // port 설정
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const {User} = require('./models/User')
const {auth} = require('./middleware/auth')
const config = require('./config/key')
 
// application/x-www-form-urlencoded type 형태를 분석하여 가져오기
app.use(bodyParser.urlencoded({extended: true}));
// application/json 파일을 가져오기
app.use(bodyParser.json());
 
// mongoose를 통한 DataBase 연결 => console.log('')를 통한 DataBase 연결 확인
// Ver6.0 이후부터는 별도의 설정 값 불필요(자동 설정 되어있으므로, 설정 시 에러 발생)
const mongoose = require('mongoose')
mongoose.connect(config.mongoURI)
.then(() => console.log('MongoDB Connected...'))
.catch(err => console.log(err))
 
app.get('/', (req, res) => {
  res.send('Hello World!')
})
 
app.post('/api/users/register', (req, res) => {
    // 회원가입 시 필요한 정보들을 Client Request에서 받아오면 DataBase 저장
    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})
    })
})
 
app.post('/api/users/login', (req, res) => {
    // 요청된 정보(email)를 DataBase에서 찾기(조회)
    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);
                // 토큰 정보를 쿠키(cookie)에 저장(local, web storage에도 가능)
                // ★★★ http://wiki.duzon.com:8080/display/SmartA/%5B5-2%5D+Cookie+vs+Web+Storage
                res.cookie('x_auth', user.token).status(200)
                .json({localSuccess: true, userId: user._id})
            })
        })
    })
})
 
app.get('/api/users/auth', auth, (req, res) => {
    // 해당 시점까지 유효성 검증을 통과하였으면, 토큰 검증이 완료
    res.status(200).json({
        _id: req.user._id,
        isAdmin: req.user.role === 0 ? false : true,
        isAuth: true,
        email: req.user.email,
        name: req.user.name,
        lastname: req.user.lastname,
        role: req.user.role,
        image: req.user.image
    })
})
 
app.get('/api/users/logout', auth, (req, res) => {
    // DataBase에서 해당 user의 토큰 정보를 지워줌으로써, 인증이 되지 않아 로그인 기능을 풀어지도록 처리
    User.findOneAndUpdate({_id: req.user._id}, {token: ''}, (err, user) => {
    if (err) return res.json({success: false, err});
    return res.status(200).send({success: true})
    })
})
 
app.listen(port, () => {
  console.log('Example app listening!')
})
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const saltRounds = 10
 
 
// Schema 생성(DataBase)
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
    }
})
 
// save 전에 진행할 function
userSchema.pre('save', function(next){
    var user = this;
 
    // password 항목 변경 시에만 실행되도록 설정
    if (user.isModified('password')) {
        // password 암호화
        bcrypt.genSalt(saltRounds, function(err, salt) {
            if (err) return next(err)
            // 실제 password와 암호화 된(hash) password
            bcrypt.hash(user.password, salt, function(err, hash) {
                if (err) return next(err)
                user.password = hash
                next()
            })
        })
    } else {
        next()
    }
})
 
userSchema.methods.comparePassword = function(plainPassword, cb) {
    // plainPassword - 암호화 된 password 비교
    bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
        if (err) return cb(err)
        cb(null, isMatch)
    })
}
 
userSchema.methods.generateToken = function(cb) {
    var user = this;
    // Json Web Token을 이용하여 토큰 생성(발급)
    // user._id와 'secretToken' 항목을 조합하여 토큰을 생성(발급)하고 해당 항목('secretToken')을 통하여 user._id 조회
    var token = jwt.sign(user._id.toHexString(), 'secretToken')
    user.token = token
    user.save(function(err, user) {
        if (err) return cb(err)
        cb(null, user)
    })
}
 
userSchema.static.findByToken = function(token, cb) {
    var user = this;
    // decoded(decode 된) 토큰
    jwt.verify(token, 'secretToken', function(err, decoded) {
        // user._id를 이용하여 토큰 정보에서 user 정보를 조회
        // Client Token 정보와 DataBase Token 정보 비교(유효성 검증)
        user.findOne({'_id': decoded, 'token': token}, function(err, user) {
            if (err) return cb(err)
            cb(null, user)
        })
    })
}
 
const User = mongoose.model('User', userSchema)
// 다른 파일에서도 사용할 수 있도록 export
module.exports = {User}
const {User} = require('../models/User')
 
 
let auth = (req, res, next) => {
    // 인증 처리를 위하여 Client Token 조회
    let token = req.cookies.x_auth;
    // 토큰 정보를 복호화 하여 user 정보를 조회
    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();
    })
}
 
// 다른 파일에서도 사용할 수 있도록 export
module.exports = {auth}
728x90
반응형