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

아래 링트 참조 :

Node.Js - Express (생활코딩) - 7 .Facebook Login , Passport Mysql 을 이용한 OAuth2.0 회원 가입 및 로그인 연동

 

 

 

 

 

 

 

타사인증 (Federation authentication)

 

 

27.Passportjs 1 : intro

 

 

 

 

28.PFederation authentication 2 : facebook

 

페이스북 로그인 설정하기

방문자가 Facebook 계정으로 내 사이트에 가입하고 로그인할 수 있도록 Facebook 로그인을 설치할 수 있습니다. 이 도움말에서는 Facebook 로그인을 설치하는 방법을 단계별로 안내합니다.

주의: 2019년 3월부터 Facebook 정책 변경에 따라 Facebook 로그인을 사용하기 위해선 SSL 보안서버 인증서를 설치해야 합니다. 자세한 설치방법은 SSL 보안서버 인증서 설치하기 도움말에서 확인할 수 있습니다.

 

https://imweb.me/faq?mode=view&category=29&category2=47&idx=71636

 

 

 

 

 

29.PFederation authentication 3 

 

 

 

30.Federation authentication 4

 

 

 

31.Federation authentication 5

 

 

 

 

 

 [Express  + Nunjucks (넌적스 )  +  Mysql  +  Passport   페이스북 로그인]

 

 

참조 : https://macaronics.net/index.php/m03/nodejs/view/1948

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

 

vscode  툴로 개발 소스 보기  :   https://github.dev/braverokmc79/Take-Nodejs

 

 

 

1. 페이스북 설정 및 https 설정

다음을 참조

https://imweb.me/faq?mode=view&category=29&category2=47&idx=71636

 

페이스북에  유효한 OAuth 리디렉션 URI 설정

이 프로젝트 에서는  https://localhost:3000/passport/oauth2/redirect/facebook 를 리다엑트로 설정 하였다.

 

Facebook 로그인  => 설정

 

페이스북에서 제시하는  "/oauth2/redirect/facebook " 경로가 아닌 localhost  는 https 를 설정해야 한다.

 

mkcert  사용  및 윈도우 설치

참조 :  https://macaronics.net/index.php/m03/nodejs/view/1948

 

 

 

 

 

프로젝트 구성

 

bin/www

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('take-nodejs:server');
var http = require('http');

const https = require('https');
const fs = require('fs');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */
//var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

var server = https.createServer(
  {
    // key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
    // cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
    key: fs.readFileSync('C:/https/localhost-key.pem', 'utf-8'),
    cert: fs.readFileSync('C:/https/localhost.pem', 'utf-8'),
  }, app.use('/', (req, res) => {
    //res.send('Congrats! You made https server now :)');
  })
).listen(port);

//server.listen(port);


~

 

 

 

 

 

2. 라이브러리 설치 및 env 설정

$ npm i passport
$ npm i passport-facebook
$ npm i dotenv

 

* dotenv 는 env  는 환경 변수를  사용을 위해 설치한다.

 

app.js

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

 

.env

FACEBOOK_CLIENT_ID=__INSERT_CLIENT_ID_HERE__
FACEBOOK_CLIENT_SECRET=__INSERT_CLIENT_SECRET_HERE__

 

 

 

 

 

3. Mysql 테이블 생성 및 연동

1)  mysql 설치

$npm i mysql

 

2)  express-mysql-session 설치

$ npm i express-mysql-session

 

3)테이블 생성 및 권한 부여

CREATE database opentutorials;
 
CREATE USER `opentutorials`@`localhost` identified by '1111';
 
GRANT ALL  privileges on opentutorials.* to `opentutorials`@`localhost` ;

 

schema.sql

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) unique NOT NULL,
  `email` varchar(30) unique NOT NULL,
  `hashed_password` varchar(100) DEFAULT NULL,
  `salt` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;


CREATE TABLE `federated_credentials` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) unique NOT NULL,
  `provider` varchar(100) unique NOT NULL,
  `subject` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`) ,
  FOREIGN KEY (user_id) REFERENCES users(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  

 

Usage

샘플 사용예 )

var express = require('express');
var app = module.exports = express();
var session = require('express-session');
var MySQLStore = require('express-mysql-session')(session);

var options = {
	host: 'localhost',
	port: 3306,
	user: 'session_test',
	password: 'password',
	database: 'session_test'
};

var sessionStore = new MySQLStore(options);

app.use(session({
	key: 'session_cookie_name',
	secret: 'session_cookie_secret',
	store: sessionStore,
	resave: false,
	saveUninitialized: false
}));

 

 

 

lib/db.js

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

module.exports = db;

 

 

 

 


4. app 설정 ( session   MySQLStore 설정 및  env  ,   passport  require시에   파라미터값  app 값과  passport 설정 주의 )

 

app.js

passport  require시에   파라미터값  app 값과  passport 설정 주의

const passport = require("./lib/passport")(app);

const passportRouter = require('./routes/passport/index')(passport);

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 dotenv = require("dotenv");
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);

dotenv.config();



//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'
  })
}));





app.use(flash());
const passport = require("./lib/passport")(app);

// 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,
});


app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser('3213e1da4523fa'));
app.use(express.static(path.join(__dirname, 'public')));
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const cookieRouter = require('./routes/cookies/index');
const sessionRouter = require('./routes/session/index');
const passportRouter = require('./routes/passport/index')(passport);


app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/cookies', cookieRouter);
app.use('/session', sessionRouter);
app.use('/passport', passportRouter);



~

 

 

 

 

 

5.URL 설정

 

routes/passport/index.js

const express = require('express');
const router = express.Router();
const db = require("../../lib/db");
global.crypto = require('crypto')

module.exports = function (passport) {


   

~



    //1.페이스북그인 처리 - 로그인 버튼 클릭시
    router.get('/login/federated/facebook', passport.authenticate('facebook', { scope: 'email' }));


    //2.페이스북그인  처리 - 콜백 반환
    router.get('/oauth2/redirect/facebook', passport.authenticate('facebook', {
        successRedirect: '/passport/welcome',
        failureRedirect: '/passport/login',
        failureFlash: true,
        successFlash: true
    }));



   
~


    return router;
}

 

 

 

 

 

 

 

6. passport  페이스북 로그인 처리

 

 


★현재 프로젝트에서는 적용하지 않았으나,

 passReqToCallback: true 옵션값을 넣어 req  파라미터값을 사용할 수 있다.

 

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true
}, function verify(req, email, password, done) {
    console.log('local-join callback called');
    console.log(email, password, done);
}
));

 

lib/passport.js

const passport = require("passport");
const crypto = require('crypto');
const db = require("../lib/db");
const LocalStrategy = require('passport-local');
const FacebookStrategy = require('passport-facebook');

module.exports = function (app) {
    app.use(passport.initialize());
    app.use(passport.session());

    passport.serializeUser(function (user, cb) {
        console.log("로그인 처리시 최초 한번  passport.serializeUser 호출 user 값  :", user);
        process.nextTick(function () {
            //다음 내용을 세션에 저장
            cb(null, { id: user.id, eamil: user.email, username: user.username });
        });
    });

    // 로그인 성공되면 passport.deserializeUser  매번 실행 처리된다
    passport.deserializeUser(function (user, cb) {
        console.log("deserializeUser  :", user);
        process.nextTick(function () {
            return cb(null, user);
        });
    });


    // 로그인 처리 - 2) passport 로그인
    passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password'
    }, function verify(email, password, cb) {

        db.query('SELECT * FROM users WHERE email = ?', [email], function (err, row) {
            if (err) { return cb(err); }
            if (!row[0]) { return cb(null, false, { message: '등록된 이메일이 없습니다.' }); }

            if (!row[0].salt) return cb(null, false, { message: '소셜 로그인으로 등록된 유저입니다.' })

            crypto.pbkdf2(password, row[0].salt, 310000, 32, 'sha256', function (err, hashedPassword) {
                console.log("로그인 데이터 : ", password, row[0].salt, hashedPassword.toString('hex'));
                console.log("로그인 DB암호: ", row[0].hashed_password);

                if (err) { return cb(err); }
                if (row[0].hashed_password !== hashedPassword.toString('hex')) {
                    console.log("비밀번호 오류");
                    //flash 에 저장 처리됨  - "flash":{"error":["비밀번호가 일치하지 않습니다."]}}
                    return cb(null, false, { message: '비밀번호가 일치하지 않습니다.' });
                }


                console.log("로그인 성공");
                return cb(null, row[0], { message: '로그인 성공' });
            });
        });

    }));



    //페이스북 로그인
    passport.use(new FacebookStrategy({
        clientID: process.env['FACEBOOK_CLIENT_ID'],
        clientSecret: process.env['FACEBOOK_CLIENT_SECRET'],
        callbackURL: '/passport/oauth2/redirect/facebook',
        state: true,
        profileFields: ['id', 'emails', 'name', 'displayName'],
    }, function verify(accessToken, refreshToken, profile, cb) {

        console.log(" 페이스북 로그인 ", profile);

        db.query('SELECT * FROM federated_credentials WHERE provider = ? AND subject = ?', [
            'Facebook',
            profile.id
        ], function (err, row) {
            if (err) { return cb(err); }

            console.log(" row.length ", row.length, profile.emails[0].value);

            //1.federated_credentials 테이블에 등록되어 있지 않으면 신규등록처리
            if (row.length === 0) {



                db.query('SELECT * FROM users WHERE email = ?', [profile.emails[0].value], function (err, results) {
                    console.log("등록된 이메일이 존재 results : ", results);

                    if (results.length > 0) {

                        //1) User 테이블에  등록되어 있다면 federated_credentials 테이블에만 등록후 로그인 처리
                        db.query('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
                            results[0].id, 'Facebook', profile.id], function (err) {
                                //구글 페이스북 등 여러 소셜에 대한
                                //user_id 중복 오류가 나올수 있다. 따라서,  에러 상관 없이 로그인 처리
                                return cb(null, results[0]);
                            });



                    } else {
                        //1) User 테이블 및   federated_credentials 테이블 모두 등록 되어 있지 않다면 모두 등록후 로그인 처리

                        db.query('INSERT INTO users (username, email) VALUES (?, ?)', [
                            profile.displayName, profile.emails[0].value
                        ], function (err, results, fields) {
                            if (err) return cb(err);

                            const id = results.insertId;
                            db.query('INSERT INTO federated_credentials (user_id, provider, subject) VALUES (?, ?, ?)', [
                                id, 'Facebook', profile.id
                            ], function (err) {
                                if (err) { return cb(err); }
                                const user = {
                                    id: id,
                                    username: profile.displayName
                                };
                                return cb(null, user);
                            });
                        });
                    }

                });


            } else {

                //2.federated_credentials 테이블 등록되어 있다면 바로 로그인처리
                db.query('SELECT * FROM users WHERE id = ?', [row[0].user_id], function (err, row) {
                    if (err) { return cb(err); }
                    if (!row[0]) { return cb(null, false); }
                    return cb(null, row[0]);
                });
            }
        });
    }));




    return passport;
}

 

 

 

 

 

 

7. 화면 뷰

 

views/passport/login.html

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

{% block content %}

<h1>Login</h1>
<p><a href="/passport/signup">회원가입</a></p>

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



  <form action="/passport/login_process" method="post">
              <p><input type="email" name="email" placeholder="email"></p>  
              <p><input type="password" name="password" placeholder="password"></p>                                 
              <p>
                <input type="submit" value="login">

   
                <a class="btn facebook" href="/passport/login/federated/facebook"><span class="icon">페이스북으로 로그인</span></a>
              </p>
</form>

{% endblock %}

 

실행 

$ npm run dev

 

 

https 로  테스트 할것

테스트 주소

 

https://localhost:3000/passport/login

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

나는 참고 견디면서 생각한다. 모든 불행은 뭔지 모르지만 좋은 것을 동반해 온다고. -베토벤

댓글 ( 4)

댓글 남기기

작성