ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Node.js] postman을 이용하여 회원가입 기능 구현하기
    웹/Node.js & React.js 2022. 2. 12. 01:00

    - 명령 프롬프트 창에 node 설치

    - node.js의 프레임워크인 express 설치

    - mongoDB, mongoose 설치

    - ssh 키 생성하여 github에 ssh키 설정

    - 서버올리기 : npm start run ( run은 package.json에서 내가 임의로 설정해줌)

    - nodemon(서버 변경상태 실시간 감지) : npm start backend ( backend는  package.json에서 내가 임의로 설정해줌)

     


    <기본 세팅>

    package.json

    - 설치된 패키지들을 관리

    - start시, index.js가 먼저 실행되게 함

    {
      "name": "boiler-plate",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "///",
      "license": "ISC",
      "dependencies": {
        "express": "^4.17.2",
        "mongoose": "^6.2.1"
      }
    }

     

    index.js - mongoDB 연결 부분

    -connect()부분에 해당 코드를 입력하면 db랑 연결됨

    -정상적으로 연결되면, 웹 브라우저에 localhost:5000/입력시 'hello world' 출력

    -console.log를 통해 콘솔창에도 출력됨

    const express = require('express')
    const app = express()
    const port = 5000
    
    const mongoose = require('mongoose')
    mongoose.connect('mongodb+srv://USERNAME:1234@boilerplate.abcmn.mongodb.net/myFirstDatabase?retryWrites=true&w=majority').then(() => console.log('MongoDB connected...'))
    .catch(err => console.log(err))
    
    
    app.get('/', (req,res) => res.send('Hello World'))
    app.listen(port, () => console.log(`Example app listening on port ${port}`))

     

    User.js

    - schema : 정보를 나타냄

    ex) 회원가입시, 필요한 정보들을 지정함 : name,email,pw ...

    - module : 만든 스키마를 모듈화함

    const User = mongoose.model('User', userSchema)
    module.exports = {User}

     

    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}

    <postman 이용>

    - postman?

    API 개발을 보다 빠르고 쉽게 구현 할 수 있도록 도와주며,

    개발된 API를 테스트하여 문서화 또는 공유 할 수 있도록 도와 주는 플랫폼

    쉽게말해서, url로 테스트하는데는 한계가 있으니까 편리하게 테스트하는데 필요한 인터페이스임!

     

    https://www.postman.com/downloads/

     

    Download Postman | Get Started for Free

    Try Postman for free! Join 17 million developers who rely on Postman, the collaboration platform for API development. Create better APIs—faster.

    www.postman.com

    - body-parser dependency 설치

    클라이언트(브라우저)가 보낸 자료들을 body-parser를 이용해서 서버에서 받음!

     

    index.js

     

    const express = require('express')
    const app = express()
    const port = 5000
    const bodyParser =require('body-parser')
    const { User } = require('./model/User')
    
    /*body-parser 옵션*/
    //application/x-www-form-urlencoded 처럼 생긴 데이터를 분석해서 가져올수 있게함
    app.use(bodyParser.urlencoded({extended:true}))
    
    //application/json 타입을 가져옴
    app.use(bodyParser.json())
    
    const mongoose = require('mongoose')
    mongoose.connect('mongodb+srv://username:1234@boilerplate.abcmn.mongodb.net/myFirstDatabase?retryWrites=true&w=majority').then(() => console.log('MongoDB connected...'))
    .catch(err => console.log(err))
    
    //url 이용
    app.get('/', (req,res) => res.send('Hello World'))
    
    //postman 이용: register route - 회원가입을 위한 register route
    app.post('/register', (req, res) => {
        //회원가입할때 필요한 정보들을 client에서 가져오면
        //그것들을 데이터 베이스에 넣어준다.
    
        
        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
            })
        }) //정보들이 user모델에 저장
    })
    app.listen(port, () => console.log(`Example app listening on port ${port}`))

    - db에 잘 넣어지면 json형식으로 true, 실패시 false가 postman에 출력됨

    - postman에 임의로 회원가입 양식을 json형식으로 입력함

    - /register : 앤드포인트


    <보안 설정>

    1. 비밀 설정 정보 관리

    - dev.js , prod.js로 나누기

    - 보통 서버개발 환경은  local - dev - staging - product 로 나뉨

    local : 내 pc

    dev : 개발자들이 local에서 만든 코드를 합쳐서 테스트 해보는 서버

    staging : 실제 운영 환경과 동일하게 만들어 테스트

    prod : 실제 서비스 운영 서버

    - 조건문을 통해 내 mongodb 정보를 가리기

     

    dev.js (개인정보)

    - .gitignore에 dev.js를 추가하여 github에 올릴때 dev.js 파일은 올리지 못하게 함

    module.exports = {
        mongoURI:'mongodb+srv://username:dddd@boilerplate.abcmn.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'
    }

    prod.js

    module.exports = {
        mongoURI: process.env.MONGO_URI
    }

    key.js

    - production(배포) 상태하면 prod.js 실행할 것 (임의로 설정한 것임)

    if(process.env.NODE_ENV === 'production') {
        module.exports = require('./prod')
    
    }else {
        module.exports = require('./dev')
    }

    index.js (추가)

    const config = require('./config/key')
    
    const mongoose = require('mongoose')
    mongoose.connect(config.mongoURI).then(() => console.log('MongoDB connected...'))
    .catch(err => console.log(err))

     

    2. bcrypt로 비밀번호 암호화

     

    - 비밀번호를 암호화하여 db에 저장하자

    해싱 vs 암호화

    : 암호화는 복호화 가능한 양방향의 방향성, 해싱은 복호화가 불가능한 단방향의 방향성

    : 따라서 비밀번호는 해싱을 이용해야 함

    - 해싱에다가 salt 기법을 추가하여 보안을 더 견고하게 만들 수 있음

    즉, 해싱값에 무작위 문자열을 더 추가한다(해싱에 소금치기...ㅎㅎ)

    - bcrypt 를 이용하자

     

    User.js(추가)

    - index.js의 save()함수 (db에 저장하는 메서드) 를 수행하기 전에 먼저 수행하는 함수

    - pre() : 성공적으로 실행되면 next()즉, save() 함수가 실행된다.

    - 비밀번호가 바뀌었을 때만 암호화 함(isModified)

    const bcrypt = require('bcrypt')
    const saltRounds =10 //임의 추가할 문자열의 개수
    
    //save()호출전에 시행 mongoose에서 가져온 메소드
    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()
    }
    });
    const User = mongoose.model('User', userSchema)
    module.exports = {User}

     

    index.js

    app.post('/register', (req, res) => {
        //회원가입할때 필요한 정보들을 client에서 가져오면
        //그것들을 데이터 베이스에 넣어준다.
    
        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
            })
        }) //정보들이 user모델에 저장
    })

    - db에 암호화 되어 저장된 모습


    <로그인>

    - 로그인후 토큰 생성

    User.js

    - 원래 상태의 비밀번호(암호화 전)와 암호화된 비밀번호를 비교하는 함수

    - 토큰 생성 

    userSchema.methods.generateToken = function(cb) {
        var user = this
        //jsonwebtoken을 이용해서 토큰을 생성하기
    
        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.methods.comparePassword = function(plainPassword, callbackfunction) {
        //plainpw 와 암호화된 비밀번호가 맞는 지 확인. plainpw를 암호화 해서 비교한다.
        bcrypt.compare(plainPassword, this.password, function(err, isMatch){
            if(err) return callbackfunction(err),
            callbackfunction(null, isMatch)
        })
    }

    index.js

    - 로그인 성공시 토큰 생성

    app.post('/login', (req, res) => {
        //요청된 이메일을 데이터베이스에서 있는지 찾는다
        User.findOne({  email: req.body.email   }, (err, user) => {
        	if(!user){
            	return res.json({
                	loginSucess: false,
                	message :"제공된 이메일에 해당하는 유저가 없습니다."
            	})
    		}   
        	//요청된 이메일이 데이터베이스에 있다면 비밀번호가 맞는 비밀번호 인지 확인
        	user.comparePassword(req.body.password , (err, isMatch) => {
            	if(!isMatch)
                	return res.json({loginSucess: false ,message: "비밀번호가 틀렸습니다."})
    
        	//비밀번호까지 맞다면 토큰을 생성하기
        	user.generateToken((err, user) => {
            	if(err) return res.status(400).send(err);
    
            // 토큰을 저장한다. 어디에? 쿠키, 로컬스토리지
            res.cookie("x_auth",user.token)
            .status(200)
            .json({ loginSucess:true, userId: user._id})
    
           
        
        })
        })
        })
    })

    <Auth 생성>

    - auth 란? 특정 권한을 가진 유저만 해당하는 페이지를 볼 수 있도록 제한하는 기능

    ex) 관리자 신분을 가진 user만 관리자 페이지 볼 수 있음, 로그인한 유저만 해당 페이지 볼 수 있음

    - client의 쿠키에 담겨져 있는 토큰을 복호화 한 후 server의 db에 담겨져 있는 토큰과 비교하여

    일치하면 auth 인증이 완료됨

     

    auth.js

    const {User} = require('../model/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(); // next가 없으면 미들웨어에 갇힘
            
    
        })
        // 유저가 있으면 인증 ok
        // 유저가 없으면 인증 no
    }
    
    module.exports = {auth}

    User.js

    - 토큰을 복호화 하는 method

    userSchema.statics.findByToken = function(token, cb){
    
        var user = this;
        //user._is +' ' =token
        //토큰을 decode한다.
        jwt.verify(token,'secretToken', function(err, decoded) {
            //유저 아이디를 이용해서 유저를 찾은 다음에
            //클라이언트에서 가져온 token과 db에 보관된  토큰이 일치하는지 확인
    
            user.findOne({"_id": decoded, "token": token}, function(err,user){
    
                if(err) return cb(err);
                cb(null, user)
            })
    
    
        })
    }

    index.js

    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.email,
            name:req.user.name,
            lastname: req.user.lastname,
            role :req.user.role,
            image: req.user.image
        })
    })

    <로그아웃>

    - 로그아웃 하려는 유저를 찾아서 server의 db에서 해당 유저의 토큰을 삭제함

     

    index.js

    - mongoose의 함수를 이용함(findoneandupdate)

    - token을 삭제함

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