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 회원 가입 및 로그인 연동

 

 


 

 

32.Auth orientdb 1

 

 

 

33.Auth orientdb 2 - session store

 

 

 

 

34.Auth orientdb 3 - register

 

 

 

 

35.Auth orientdb 4 - login

 

 

 

 

36.Auth orientdb 5 - federation

.

 

 

 

Mogodb 버전으로 로그인 인증 구현하기

 

프로젝트 구성

 

 

passport 공식 문서 참조 : https://www.passportjs.org/tutorials/facebook/

 

 

1.Nodejs Mongodb 연동 , 몽고 DB 윈도우 설치 및 실행

 

링크 주소 참조 : https://macaronics.net/m03/nodejs/view/1955

 

 

몽고 DB는 라이브러리 설치후 app.js 파일에서 const mongodb = require("./lib/mongodb"); 와 같이 

접속 정보만  임포트만 해주면 model 에서   mongoose 만 임포트 하는 것만으로도 접속이  가능하다.

 

lib/mongodb.js

const config = require("../config/key");
const mongoose = require("mongoose");

mongoose.connect(
    config.mongoURI,
).then(() => console.log("MongoDB Connected...")).catch(err => console.error("에러 :", err));

module.exports = mongoose;

 

 

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

const mongodb = require("./lib/mongodb");


const app = express();

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




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

console.log("* 몽고DB 연결 상태 :", mongodb.connections[0]._connectionString);


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);



// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

 

 

 

2.  이 프로젝트는 https 로  테스트 하므로   

https://localhost  접속하기 위해서는 다음을 참조

 

mkcert 을 사용하여 로컬 개발에 HTTPS를 사용하는 방법

링크 주소 참조 : https://macaronics.net/index.php/m03/nodejs/view/1956

 

 

 

 

3. production    환경  ,  development   환경  설정 및 키값 설정

 

1) dotenv 설치

$npm i dotenv

 

app.js 에 다음과 같이 임포트

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

 

.env 파일 생성 후

FACEBOOK_CLIENT_ID=페이스북 키값
FACEBOOK_CLIENT_SECRET=페이스북 시크리트값
MONOGDB_URI=mongodb://localhost:27017/take-ndoejs

 

 

2) package.json  scripts 를 다음 과 같이 수정

nodemon 은 실시간 프로젝트 변경감지

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

 

 

3)config/dev.js

module.exports = {
    mongoURI: process.env['MONOGDB_URI']
}

 

4)config/key.js

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

 

5)config/prod.js

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

 

 

 

 

 

4. 모델 만들기

1) models/User.js

const mongoose = require('mongoose');

const userSchema = mongoose.Schema({
    username: {
        type: String,
        maxlength: 50
    },
    email: {
        type: String,
        trim: true,
        unique: 1
    },
    password: {
        type: String,
        minlength: 5
    },
    salt: {
        type: String,
        maxlength: 100
    },
    role: {
        type: Number,
        default: 0
    },
    image: String,
    
    token: {
        type: String
    },
    tokenExp: {
        type: Number
    }

})



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

 

 

2) models/Oauth2.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;


const Oauth2Schema = Schema({

    email: {
        type: String,
        ref: 'User'
    },
    provider: {
        type: String
    },
    subject: {
        type: String
    }

}, { timestamps: true });



const Oauth2 = mongoose.model('Oauth2', Oauth2Schema);
module.exports = { Oauth2 };

 

 

 

 

 

5. route  설정하기  회원가입 처리

routes/passport/index.js

const express = require('express');
const router = express.Router();
const { User } = require("../../models/User");
global.crypto = require('crypto')

module.exports = function (passport) {

    router.get("/welcome", (req, res) => {
        console.log(" req.user  : ", req.user);
        res.render("passport/welcome", { user: req.user });
    })


    //로그인 페이지
    router.get("/login", (req, res) => {
        const fmsg = req.flash();
        let error, success = "";
        if (fmsg.error) error = fmsg.error[0];
        if (fmsg.success) success = fmsg.success[0];

        res.render("passport/login", { error: error, success: success })
    });


    // 로그인 처리 - 1) 성공 및 실패 페이지 설정 및 flash 사용여부 설정하기
    router.post('/login_process', passport.authenticate('local', {
        successRedirect: '/passport/welcome',
        failureRedirect: '/passport/login',
        failureFlash: true,
        successFlash: true
    }));





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



    //로그 아웃 처리
    router.get('/logout', function (req, res, next) {
        req.logout(function (err) {
            if (err) { return next(err); }
            req.session.destroy(function (err) {
                res.redirect("/passport/welcome");
            });
        });
    });


    //회원가입페이지
    router.get('/signup', function (req, res, next) {
        const fmsg = req.flash();
        let error, success = "";
        if (fmsg.error) error = fmsg.error[0];
        if (fmsg.success) success = fmsg.success[0];

        res.render("passport/register", { error: error, success: success });
    });


    //회원가입처리
    router.post('/signup_process', function (req, res, next) {

        if (req.body.password !== req.body.pw2) {
            req.flash("error", "비밀번호가 일치 하지 않습니다.");
            return res.redirect("/passport/signup");
        }
        crypto.randomBytes(16, (error, buf) => {
            const salt = buf.toString("base64");

            crypto.pbkdf2(req.body.password.trim(), salt, 310000, 32, 'sha256', function (err, hashedPassword) {
                console.log("회원가입 데이터 : ", req.body.password, salt, hashedPassword.toString('hex'));
                if (err) { return next(err); }


                const user = new User(
                    {
                        username: req.body.username,
                        email: req.body.email,
                        password: hashedPassword.toString('hex'),
                        salt: salt
                    });

                user.save((err, doc) => {
                    if (err) {
                        console.log(" 회원 가입 오류 : ", err);
                        //req.flash("error", "이이 등록된  처리된 아이디 혹은 이메일 입니다.");
                        req.flash("error", err.toString());
                        return res.redirect('/passport/signup');
                    }
                    console.log("회원가입 성공후 반환처리된 유저 값  :", doc);

                    req.login(doc, function (err) {
                        if (err) { return next(err); }
                        req.flash("success", "회원가입을 축하합니다.");
                        res.redirect('/passport/login');
                    });
                });
            });

        });
    });



    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("./mongodb");
const LocalStrategy = require('passport-local');
const FacebookStrategy = require('passport-facebook');
const { User } = require("../models/User");
const { Oauth2 } = require("../models/Oauth2");


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) {


        User.findOne({ "email": email }, function (err, user) {
            if (err) { return cb(err); }
            if (!user) { return cb(null, false, { message: '등록된 데이터가 없습니다.' }); }

            if (!user.salt) return cb(null, false, { message: '소셜 로그인으로 등록된 유저입니다.' })
            console.log(" 유저 정보 : ", user);

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

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


                return cb(null, user, { 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);

        Oauth2.findOne({ "provider": "facebook", "subject": profile.id }, function (err, oauth2) {
            if (err) { return cb(err); }

            console.log(" oauth2 정보 : ", oauth2);
            if (!oauth2) {
                //1.oauth2 컬렉션에 등록되어 있지 않으면 신규등록처리

                //User 컬렉션에 존재하는지 확인
                User.findOne({ "email": profile.emails[0].value }, function (err, user) {
                    if (err) { return cb(err); }


                    if (user) {
                        //1) User 컬렉션에 등록된 이메일이 존재하면은 oauth2 컬렉션에 등록후 로그인 처리
                        const oauth2 = new Oauth2({ email: profile.emails[0].value, provider: "facebook", subject: profile.id });
                        oauth2.save((err, doc) => {
                            //구글 페이스북 등 여러 소셜에 대한
                            //user_id 중복 오류가 나올수 있다. 따라서,  에러 상관 없이 로그인 처리
                            return cb(null, user);
                        });


                    } else {
                        //2) User 컬렉션에 등록되어 있지 않다면 User 컬렉션에 등록처리 및  oauth2 컬렉션에 등록후 로그인 처리
                        const user = new User({ username: profile.displayName, email: profile.emails[0].value });
                        user.save((err, doc) => {
                            const oauth2 = new Oauth2({ email: profile.emails[0].value, provider: "facebook", subject: profile.id });
                            oauth2.save((err, doc) => {
                                if (err) { return cb(err); }
                                return cb(null, doc);
                            });
                        });
                    }
                });

            } else {
                //2.oauth2 컬렉션에 등록되어 있다면 바로 로그인처리
                User.findOne({ "email": profile.emails[0].value }, function (err, user) {
                    if (err) { return cb(err); }
                    if (!user) { return cb(null, false, { message: "로그인 실패" }); }
                    return cb(null, user);
                });
            }


        });



    }));




    return passport;
}

 

 

 

 

7.화면 View  만들기

1) 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 %}

 

2) views/passport/register.html

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

{% block content %}

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

<p><a href="/passport/login">로그인</a></p>

 <form action="/passport/signup_process" method="post">
          <section>
              <label for="username">이름</label>
              <input id="username" name="username" type="text" placeholder="이름" required>
          </section>
         <section>
              <label for="email">이메일</label>
              <input id="email" name="email" type="email" placeholder="이메일" required>
          </section>          
          <section>
              <label for="password">비밀번호</label>
              <input id="password" name="password" type="password" autocomplete="new-password" placeholder="비밀번호" required>
          </section>
          <section>
              <label for="password">비밀번호확인</label>
              <input id="password" name="pw2" type="password" autocomplete="new-password" placeholder="비밀번호확인" required>
          </section>          
          <button type="submit">Sign up</button>
</form>


{% endblock %}

 

3) views/passport/welcome.html

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

{% block content %}

<h1>WELCOME</h1><br/>

{% if user %}
<h2>{{user.username}}  님 환영합니다.  &nbsp;&nbsp;<a href="/passport/logout">로그아웃</a></h2>


{% else  %}
<p><a href="/passport/login">로그인</a>&nbsp;&nbsp;<a href="/passport/signup">회원가입</a></p>
{% endif %}



{% endblock %}

 

 

 

이 프로젝트는  ssl 접속인  https   해야 한다.

 

회원 가입주소

https://localhost:3000/passport/signup

 

로그인 주소

https://localhost:3000/passport/login

 

로그인 성공 주소

https://localhost:3000/passport/welcome

 

 

 

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

 

 

 

 

 

about author

PHRASE

Level 60  라이트

토끼를 잡고 나면 토끼 잡는 올가미는 필요 없고 물고기를 잡고 나면 통발은 필요가 없다. 그래서 이것들을 잊어버리게 된다. 근본을 얻게 되면 그 끝은 잊어버리게 된다는 말. 학문이 성취되면 서적은 필요가 없게 된다. -장자

댓글 ( 4)

댓글 남기기

작성