๐Ÿ‘ฉ‍๐Ÿ’ป ๋ฐฑ์—”๋“œ(Back-End)/Node js

[ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ API ์ธ์ฆํ•˜๊ธฐ] Node JS using JWT & MongoDB

์ง•์ง•์•ŒํŒŒ์นด 2023. 2. 25. 23:43
728x90
๋ฐ˜์‘ํ˜•

https://www.youtube.com/watch?v=ZEg03f1o_vQ&list=LL&index=6 

 

 

 

์•„~ ๋„˜ ์–ด๋ ต๋‹ค

 

โญ ํŒŒ์ผ ๊ตฌ์กฐ

 

โญ ์„ธ๋ถ€ ํŒŒ์ผ

๐ŸŒปconfig/db.config.js

module.exports = {
    db : "mongodb+srv://~~~~~~~"
}

 

๐ŸŒปcontrollers/user.controller.js

'use strict';

// ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์„œ ์ €์žฅํ•˜๊ธฐ
const bcryptjs = require("bcryptjs");
const userSesrvice = require("../services/user.services");

exports.register = (req, res, next) => {
    const {password} = req.body;
    // ์†”ํŠธ + ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ•ด์‹œ๋กœ ์•”ํ˜ธํ™”
    const salt = bcryptjs.genSaltSync(10);
    // ๋น„๋™๊ธฐ ๋ฐฉ์‹ ํŒŒ๋ผ๋ฏธํ„ฐ, ์•”ํ˜ธํ™”์— ์‚ฌ์šฉ๋˜๋Š” Salt
    req.body.password = bcryptjs.hashSync(password, salt);

    userSesrvice.register(req.body, (error, result) => {
        if(error) {
            return next(error);
        }
        return res.status(200).send({
            message : "Success",
            data : result,
        });
    });
};

exports.login = (req, res, next) => {
    const { username, password } = req.body;

    userSesrvice.login({ username, password }, (error, result) => {
        if(error) {
            return next(error);
        }
        return res.status(200).send({
            message : "Success",
            data : result,
        });
    })
}

exports.userProfile = (req, res, next) => {
    if (req.user) {
        res.send(req.user);
        next();
    } else {
       return res.status(401).json({ message: 'Invalid token' });
    }
};

 

๐ŸŒปmiddlewares/auth.js

'use strict';

const dotenv = require('dotenv');
// JWT(JSON Web Token - JSON ์›น ํ† ํฐ)์€ ๋‘ ๊ฐœ์ฒด ์‚ฌ์ด์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ํด๋ ˆ์ž„์„ ์ „๋‹ฌ(ํ‘œํ˜„)
// 1) HEADER(ํ—ค๋”) typ : ํ† ํฐ์˜ ํƒ€์ž…์„ ์ง€์ •(JWT), alg : ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง€์ •
// 2) PAYLOAD(๋‚ด์šฉ) - ์‚ฌ์šฉ๋˜๋Š” ์ •๋ณด์˜ ํ•œ ์กฐ๊ฐ์„ ํด๋ ˆ์ž„(claim)
// 3) VERIFY SIGNATURE(์„œ๋ช…) : header + payload ์ •๋ณด๋ฅผ ๋น„๋ฐ€ํ‚ค๋กœ ํ•ด์‰ฌ๋ฅผ ํ•˜์—ฌ ์ƒ์„ฑ!
const jwt = require("jsonwebtoken");
dotenv.config();
// ๋กœ๊ทธ์ธ ์‹œ ํ•ด๋‹น id, pw์™€ ์ผ์น˜ํ•˜๋Š” ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด access token๊ณผ refresh token์„ ๋ฐœ๊ธ‰
// ๋ฐœ๊ธ‰๋œ token์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋งˆ๋‹ค 
// headers์— authorization: 'bearer ' + accessToken ๋ฐฉ์‹์œผ๋กœ ๋„˜๊ฒจ๋ฐ›์•„ ํ•ด๋‹น ์š”์ฒญ์˜ token์ด ์œ ํšจํ•œ์ง€ ๊ฒ€์‚ฌ

function authenticateToken(req, res, next) {
    // access token์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[0];

    if (token == null) {
        console.log("์ž˜๋ชป๋œ token์ด๋‹ค!!!")
        return res.sendStatus(401);
    }
    // verify๋ฅผ ํ†ตํ•ด ๊ฐ’ decode
    jwt.verify(token, "Snippet_SecretKEY", (err, user) => {
        if(err) return res.sendStatus(403);
        req.user = user;
        next();
    });
};

function generateAccessToken(username) {
    // sign ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด access token ๋ฐœ๊ธ‰ (process.env.ACCESS_TOKEN_SECRET)
    return jwt.sign({data : username},"Snippet_SecretKEY", {
        expiresIn : "1h"
    });
};

module.exports = {
    authenticateToken,
    generateAccessToken,
};

 

๐ŸŒปmiddlewares/errors.js

function errorHandler(err, req, res, next) {
    if(typeof err === "string") {
        // 400(์ž˜๋ชป๋œ ์š”์ฒญ): ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์˜ ๊ตฌ๋ฌธ์„ ์ธ์‹ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค
        return res.status(400).json({message : err});
    }
    if(typeof err === "ValidationError") {
        return res.status(400).json({message : err.message});
    }
    if(typeof err === "UnauthorizedError") {
        // 401(๊ถŒํ•œ ์—†์Œ): ์ด ์š”์ฒญ์€ ์ธ์ฆ์ด ํ•„์š”
        return res.status(401).json({message : err.message});
    }
    // 500 (๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜): ์„œ๋ฒ„์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋‹ค
    return res.status(500).json({message : err.message});
};

module.exports = {
    errorHandler,
};

 

๐ŸŒปmodels/user.mode.js

const mongoose = require("mongoose");
const {Schema} = mongoose;
// Mongoose ์Šคํ‚ค๋งˆ ๋‚ด์˜ ๊ณ ์œ ํ•œ ํ•„๋“œ์— ๋Œ€ํ•œ ์‚ฌ์ „ ์ €์žฅ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ
const uniqueValidator = require("mongoose-unique-validator");

const userSchema = new Schema ({
    username : {
        type : String,
        required : true
    },
    email : {
        type : String,
        required : true,
        unique : true
    },
    password : {
        type : String,
        required : true
    },
    date : {
        type : Date,
        default : Date.now()
    }
});

userSchema.set("toJSON", {
    transform : (document, returnedObject) => {
        returnedObject.id = returnedObject._id.toString();
        // _id, _v, password ์ „์—ญ ์Šคํ‚ค๋งˆ ์„ค์ •์„ ๋“ฑ๋กํ•˜์—ฌ ์‚ญ์ œ
        delete returnedObject._id;
        delete returnedObject.__v;
        delete returnedObject.password;
    },
});

// unique์˜ ์กด์žฌ ์œ ๋ฌด๋„ ํŒŒ์•…
userSchema.plugin(uniqueValidator, { message : "Email ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘"});

// schema๋ฅผ ์‚ฌ์šฉํ•˜๋Š” model
const User = mongoose.model("user", userSchema);
module.exports = User;

 

๐ŸŒปmodels/user.mode.js

'use strict';

const userController = require("../controllers/user.controller");

const express = require("express");
const router = express.Router();

router.post("/register", userController.register);
router.post("/login", userController.login);
router.post("/userProfile", userController.userProfile);

module.exports = router;

 

๐ŸŒปservices/user.services.js

'use strict';


const User = require("../models/user.model");
const bcrypt = require("bcryptjs");
const auth = require("../middlewares/auth.js")

async function login({ username, password }, callback) {
    const user = await User.findOne({ username });

    if (user != null) {
        // client๊ฐ€ ์ž…๋ ฅํ•œ password์™€ user.passward์™€ ์ผ์น˜ํ•œ์ง€ ํ™•์ธ
        if (bcrypt.compareSync(password, user.password)) {
            // accessToken ์ƒ์„ฑ๊ธฐ (middleware์—์„œ ๋ฐœํ–‰)
            const token = auth.generateAccessToken(username);
            return callback(null, {...user.toJSON(), token});
        }
        else {
            return callback({
                message : "์ผ์น˜ํ•˜์ง€ ์•Š์€ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ์ž…๋‹ˆ๋‹ค.",
            });
        }
    } else {
        return callback({
            message : "์ผ์น˜ํ•˜์ง€ ์•Š์€ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ์ž…๋‹ˆ๋‹ค.",
        });
    }
}

async function register(params, callback) {
    const user = new User(params);

    if(params.username === undefined) {
        return callback({message : "์•„์ด๋”” ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"});
    }

    
    user.save()
        .then((response) => {
            return callback(null, response);
        })
        .catch((error) => {
            return callback(error);
        });
}

module.exports = {
    login,
    register,
};

 

๐ŸŒปapp.js

'use strict';

const express = require("express");
const mongoose = require("mongoose");
const dbConfig = require("./config/db.config");

const dotenv = require('dotenv');
dotenv.config();

const auth = require("./middlewares/auth");
const errors = require("./middlewares/errors");

const {unless} = require("express-unless");

const app = express();

// ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์š”์ฒญ
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db, {
    // useNewUrlParser, useUnifiedTopology ์˜ต์…˜์€ ์„œ๋ฒ„ ์‹คํ–‰ ์‹œ ๋ฐœ์ƒ๋˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์ œ๊ฑฐ
    useNewUrlParser : true,
    useUnifiedTopology : true
}).then(
    () => {
        console.log("db ์—ฐ๊ฒฐ ์„ฑ๊ณต")
    },
    (error) => {
        console.log("db ์—ฐ๊ฒฐ ์•ˆ๋จ : " + error);
    } 
);

// authenticateToken : ๋ฏธ๋“ค์›จ์–ด - ๋ณดํ˜ธ๋œ ๊ฒฝ๋กœ์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์ „์— ํ† ํฐ์„ ์ธ์ฆ

auth.authenticateToken.unless = unless
console.log(auth.authenticateToken.unless);
app.use(auth.authenticateToken.unless({
    path: [
        { url: '/users/login', methods: ['POST']},
        { url: '/users/register', methods: ['POST']}
    ]
}));

// ํด๋ผ์ด์–ธํŠธ๋กœ ๋ถ€ํ„ฐ ๋ฐ›์€ http ์š”์ฒญ ๋ฉ”์‹œ์ง€ ํ˜•์‹์—์„œ body๋ฐ์ดํ„ฐ๋ฅผ ํ•ด์„
app.use(express.json());

app.use("/users", require("./routes/user.routes"));

app.use(errors.errorHandler);

app.listen(process.env.PORT || 4000, function() {
    console.log("๐Ÿš€ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ!")
})

 

๐ŸŒปpackage.json

{
  "name": "mangement",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "express-unless": "^2.1.3",
    "jsonwebtoken": "^9.0.0",
    "mongoose": "^6.9.2",
    "mongoose-unique-validator": "^3.1.0"
  }
}

 


โญ ๊ฒฐ๊ณผ

๐ŸŒปregister ํšŒ์›๊ฐ€์ž…ํ•˜๊ธฐ

 

๐ŸŒปlogin ๋กœ๊ทธ์ธ ํ•˜๊ธฐ

 

๐ŸŒปuser ํ™•์ธํ•˜๊ธฐ

: login ํ–ˆ์„ ๋•Œ token์„ authorization์˜ value ๊ฐ’์— ๋„ฃ์–ด์„œ ํ™•์ธํ•˜๊ธฐ!!!

728x90
๋ฐ˜์‘ํ˜•