Nodejs

 

생활 코딩 강의 목록 :  https://opentutorials.org/module/2026/12063

인프런 강의 목록 https://www.inflearn.com/course/node-js-활용/unit/3586?tab=curriculum

 

 

 

1. 강좌소개

서버측 자바스크립트 기술,  Node.js 를 소개하고 사용법을 학습해 간단한 웹 어플리케이션을 만들어 보는 nodejs 강좌입니다. 기본 javascript 와 nodejs  를 이용하기 때문에 타 언어 (PHP 나 JSP) 의 도움없이 오직 javascript 기술로만 웹 어플리케이션을 구현할 수 있는 지식을 전달해줍니다. 앞선 두 강좌에서 학습한 내용과 실습예제들을 통합해 게시물과 로그인 기능이 있는 웹어플리케이션을 직접 만들어 볼수 있는 강좌입니다.

  • 자바스크립트의 기초적인 문법은 설명하지 않습니다. 여러 자바스크립트에 대한 다른 수업은 >>여기<< 에서 학습 가능하세요.

 

 

 

 

 

프로젝트 준비

1) Take-Nodejs 디렉토리 생성

2) $ cd Take-Nodejs

3) $ express .

4) 넌적스 설치  및 vscode  툴에 Nunjucks Snippets  확장패키지 설치

$ npm i nunjucks --save

 

 

5)nodemon  설치 및  package.json  변경

  "scripts": {
    "start": "node ./bin/www",
    "dev": "nodemon  ./bin/www",
    "debug": "DEBUG=myapp:* yarn start"
  },

 

6)app.js 변경

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
const nunjucks = require('nunjucks');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
//app.set('view engine', 'jade');

app.set('view engine', 'html'); // 확장자를 html 로도 사용이 가능함.
nunjucks.configure('views', { // views폴더가 넌적스파일의 위치가 됨
  express: app,
  watch: true,
});


~

 

 

7)넌적스 레이아웃 만들기

view/layout.html

<!DOCTYPE html>
<html lang="ko">
  <head>
    <title>{{title}}</title>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
    <link rel="stylesheet" href="/stylesheets/style.css" />
  </head>
  <body>
    <header>
      {% block header %}        
      {% endblock %}
    </header>

    {% block content %}
    {% endblock %}

    <footer>
      {% block footer %}        
      {% endblock %}
    </footer>
  </body>
</html>  
  

 

routes/index.js

var express = require('express');
var router = express.Router();

/* GET home page. */
// router.get('/', function (req, res, next) {
//   res.render('index', { title: 'Express' });
// });

router.get('/', function (req, res, next) {
  res.render('index', { title: 'Nunjucks' }); // index.html에 title이라는 변수를 전달
});

module.exports = router;

 

 

views/index.html

{% extends 'layout.html' %}

{% block content %}
<h1>{{title}}</h1>
<p>Welcome to {{title}}</p>
{% endblock %}

 

 

8)실행

$ npm run dev

 

 

Nunjucks

https://mozilla.github.io/nunjucks/

https://mozilla.github.io/nunjucks/templating.html#sort-arr-reverse-casesens-attr

 

 

소스 : https://github.com/braverokmc79/Take-Nodejs

 

 

 

 

비밀번호 보안 (Security Password)

 

 

18. Security Password 1 

 

 

 

 

 

19. Security Password 2 

 

 

 

https://www.npmjs.com/package/md5

 

$ npm i md5

 

API

md5(message)

  • message -- String, Buffer, Array or Uint8Array
  • returns String

Usage

var md5 = require('md5');

 

console.log(md5('message'));

This will print the following

78e731027d8fd50ed642340b7c9a63b3

 

var fs = require('fs');

var md5 = require('md5');



fs.readFile('example.txt', function(err, buf) {

  console.log(md5(buf));

});

 

 

 

 

 

 

 

 

 

20. Security Password 3 - salt

 

https://www.npmjs.com/package/sha256

 

const express = require('express');
const router = express.Router();
const md5 = require('md5');
const sha256 = require('sha256');
~


router.post('/auth/login', function (req, res, next) {
    let user = [
        {
            username: 'egoing',
            password: 'a6c44b2d30bf9a5893c099bacb94606ca119b38e1454614dc1c069e8ffa958e1',
            displayName: 'Egoing',
            salt: "@#@#$SDA%#a213"
        },
        {
            username: 'test1',
            password: 
'079ec662a574122c1b10d91ab3be9ae4d9cc56e8bae1deadd3c7ee105195f027',
            displayName: '홍길동',
            salt: "#@fsa3%#@f5232"
        },
    ]

    const uname = req.body.username;
    const pwd = req.body.password;
    //password :1111
    console.log("req.body : ", sha256(pwd + user[1].salt));


    if (uname === user[0].username && sha256(pwd + user[0].salt) === user[0].password) {
        //세션 저장
        req.session.displayName = user[0].displayName;
        req.session.save(function () {
            res.redirect("/session/welcome");
        })
    } else {
        res.render("session/login", { error: "아이디 또는 비밀번호가 일치하지 않습니다." });
    }
})

 

 

 

 

 

21. Security Password 4 - crypto.pbkdf2

 

 

pbkdf2-password  는 구 

 

현재 nodejs express 에서 기본적으로 제공하는  crypto.pbkdf2 사용하면 된다.

이것은 passportjs 에서도  사용하고 있다.

 

https://www.tutorialspoint.com/crypto-pbkdf2-method-in-node-js

https://www.passportjs.org/tutorials/google/

 

특별히 설치할 라이브러리 는 없으며 crypto 를 rqueire 하면 된다.

const crypto = require('crypto');

 

    crypto.pbkdf2(pwd, user[1].salt, 100000, 64, 'sha512', (err, derivedKey) => {
        if (err) throw err;
        // Printing the derived key
        console.log(" derivedKey : ", derivedKey);
        //출력결과  <Buffer 50 c1 cc 27 d8 31 10 d7 bf 94 db d9 b6 f8 d5 a3 b7 a4 e2 09 fb 0d 98 bc 27 ef ad c1 2e c7 ee d6 f9 af 42 1a 49 dd 25 bc 77 00 f8 55 2d cd b3 5f df f5
        console.log("Key Derived: ", derivedKey.toString('hex'));
        //해싱함수를 hex 변환 출력 결과 => 50c1cc27d83110d7bf94dbd9b6f8d5a3b7a4e209fb0d98bc27efadc12ec7eed6f9af421a49dd25bc7700f8552dcdb35fdff57077d5074f74a60fcf60c258fdaa


        if (uname === user[0].username && derivedKey.toString('hex') === user[0].password) {
            //세션 저장
            req.session.displayName = user[0].displayName;
            req.session.save(function () {
                res.redirect("/session/welcome");
            })
        } else {
            res.render("session/login", { error: "아이디 또는 비밀번호가 일치하지 않습니다." });
        }

    });

 

 

 

 

 

 

22. Security Password 5

 

 

 

23. Security Password 6 - register

 

schema.sql

CREATE TABLE `member` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30)  unique NOT NULL ,
  `password` varchar(250) DEFAULT NULL,
  `salt` varchar(250) DEFAULT NULL,
  PRIMARY KEY (`id`)
)ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;

 

 

lib/db.js

const mysql = require('mysql');
const db = mysql.createConnection({
    host: 'localhost',
    user: 'opentutorials',
    password: '1111',
    database: 'opentutorials'
});
db.connect();

module.exports = db;

 

app.js

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const nunjucks = require('nunjucks');
const flash = require('connect-flash');
const app = express();


const session = require('express-session');
const MySQLStore = require('express-mysql-session')(session);
const MongoDBStore = require('connect-mongodb-session')(session);


const FileStore = require('session-file-store')(session);

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const cookieRouter = require('./routes/cookies/index');
const sessionRouter = require('./routes/session/index');


//1. FileStore 설정
// app.use(session({
//   secret: 'secret key',	// 암호화
//   resave: false,
//   saveUninitialized: true,
//   cookie: {
//     httpOnly: true,
//   },
//   store: new FileStore() // 세션 객체에 세션스토어를 적용
// }));


//2. MySQLStore 설정
// session DB 저장 방식 - session 테이블이 자동 생성되고  세션이 passport의해 저장 된다.
app.use(session({
  secret: '12312dajfj23rj2po4$#%@#',
  resave: false,
  saveUninitialized: true,
  store: new MySQLStore({
    host: 'localhost',
    port: 3306,
    user: 'opentutorials',
    password: '1111',
    database: 'opentutorials'
  })
}));


//3. 몽고 MongoDBStore  설정
// app.use(session({
//   secret: '12312dajfj23rj2po4$#%@#',
//   resave: false,
//   saveUninitialized: true,
//   store: new MongoDBStore({
//     uri: 'mongodb://localhost:27017/take-ndoejs',
//     collection: 'sessionStore'
//   })
// }));


app.use(flash());

~

 

 

routes/session/index.js

const express = require('express');
const router = express.Router();
const md5 = require('md5');
const sha256 = require('sha256');
const crypto = require('crypto');
const db = require('../../lib/db');


router.get('/count', function (req, res, next) {
    if (req.session.count) {
        req.session.count++;
    } else {
        req.session.count = 1;
    }
    res.render('session/count', { msg: req.session.count });
});

router.get('/tmp', function (req, res, next) {
    res.json("result : " + req.session.count);
})




// let user = [
//     {
//         username: 'egoing',
//         password: '50c1cc27d83110d7bf94dbd9b6f8d5a3b7a4e209fb0d98bc27efadc12ec7eed6f9af421a49dd25bc7700f8552dcdb35fdff57077d5074f74a60fcf60c258fdaa',
//         displayName: 'Egoing',
//         salt: "@#@#$SDA%#a213"
//     },
//     {
//         username: 'test1',
//         password: '079ec662a574122c1b10d91ab3be9ae4d9cc56e8bae1deadd3c7ee105195f027',
//         displayName: '홍길동',
//         salt: "#@fsa3%#@f5232"
//     },
// ]

router.get('/auth/login', function (req, res, next) {
    console.log("msg : ", req.session.msg);
    const msg = req.session.msg;
    delete req.session.msg;
    req.session.save(function () {
        res.render("session/login", { msg: msg });
    });

});
router.post('/auth/login', function (req, res, next) {
    const uname = req.body.username;
    const pwd = req.body.password;
    //password :1111

    db.query("SELECT *  FROM member WHERE username= ? ", [uname], function (err, results, fields) {
        if (err) return res.status(400).render("session/login", { error: err });

        if (results.length === 0) return res.status(400).render("session/login", { error: "등록된 아이디가 없습니다." });
        console.log("results[0] : ", results[0]);

        crypto.pbkdf2(pwd, results[0].salt, 100000, 64, 'sha512', (err, derivedKey) => {
            if (err) throw err;
            // Printing the derived key
            console.log(" derivedKey : ", derivedKey);
            //출력결과  <Buffer 50 c1 cc 27 d8 31 10 d7 bf 94 db d9 b6 f8 d5 a3 b7 a4 e2 09 fb 0d 98 bc 27 ef ad c1 2e c7 ee d6 f9 af 42 1a 49 dd 25 bc 77 00 f8 55 2d cd b3 5f df f5
            console.log("Key Derived: ", derivedKey.toString('hex'));
            //해싱함수를 hex 변환 출력 결과 => 50c1cc27d83110d7bf94dbd9b6f8d5a3b7a4e209fb0d98bc27efadc12ec7eed6f9af421a49dd25bc7700f8552dcdb35fdff57077d5074f74a60fcf60c258fdaa

            if (uname === results[0].username && derivedKey.toString('hex') === results[0].password) {
                //세션 저장
                req.session.displayName = uname;
                req.session.save(function () {
                    res.redirect("/session/welcome");
                })
            } else {
                res.render("session/login", { error: "아이디 또는 비밀번호가 일치하지 않습니다." });
            }

        });
    })
})

//회원 가입
router.get('/auth/register', function (req, res, next) {
    res.render("session/register");
});


router.post('/auth/register', function (req, res, next) {
    const uname = req.body.username;
    const pwd = req.body.password;
    const pwd2 = req.body.pw2;

    if (pwd !== pwd2) {
        res.render("session/register", { error: "비밀번호와 비밀번호확인이  일치하지 않습니다." });
        return;
    }

    const salt = crypto.randomBytes(64).toString('base64');

    crypto.pbkdf2(pwd, salt, 100000, 64, 'sha512', (err, derivedKey) => {
        if (err) throw err;
        console.log("저장할 데이터값:", uname, derivedKey.toString('hex'), salt);

        db.query("INSERT INTO member (username, password ,salt) VALUES (?,?,?) ", [uname, derivedKey.toString('hex'), salt],
            function (err, results, fields) {
                if (err) {
                    return res.status(400).render("session/login", { error: err });
                }
                req.session.msg = "회원 가입을 축하 합니다.";
                res.redirect("/session/auth/login");
            });
    });
});



router.get("/auth/logout", function (req, res, next) {
    delete req.session.displayName;
    req.session.save(function () {
        res.redirect("/session/welcome");
    })

});

router.get('/welcome', function (req, res, next) {
    res.render("session/welcome", { session: req.session });
});



module.exports = router;

 

 

views/session/register.html

{% extends '../layout.html' %}

{% block content %}

<h1>Login</h1>
<span style="color:red">{{error}}</span>


<form action="/session/auth/register" method="post">
    <p><input type="text" name="username" placeholder="유저명"></p>
    <p><input type="password" name="password" placeholder="비밀번호"></p>
    <p><input type="password" name="pw2" placeholder="비밀번호 확인"></p>
    <p>
        <input type="submit">
    </p>
</form>


{% endblock %}

 

 

 

views/session/login.html

{% extends '../layout.html' %}

{% block content %}

<h1>Login</h1>
<p><a href="/session/auth/register">회원가입</a></p>

<span style="color:red">{{error}}</span>
<span style="color:rgb(0, 119, 255)">{{msg}}</span>

<form action="/session/auth/login" method="post">
    <p><input type="text" name="username" placeholder="username"></p>
    <p><input type="password" name="password" placeholder="password"></p>
    <p>
        <input type="submit">
    </p>
</form>


{% endblock %}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

시집갈 때 등창이 난다 , 기다리던 때를 맞아 공교로운 일로 낭패를 본다는 말.

댓글 ( 4)

댓글 남기기

작성