생활 코딩 강의 목록 : 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 기술로만 웹 어플리케이션을 구현할 수 있는 지식을 전달해줍니다. 앞선 두 강좌에서 학습한 내용과 실습예제들을 통합해 게시물과 로그인 기능이 있는 웹어플리케이션을 직접 만들어 볼수 있는 강좌입니다.
- 선수강좌가 2개 있습니다. 아래것을 먼저 학습하신후 본 강좌를 들으시면 좀더 이해가 쉬우실거에요.
- 자바스크립트의 기초적인 문법은 설명하지 않습니다. 여러 자바스크립트에 대한 다른 수업은 >>여기<< 에서 학습 가능하세요.
- 강의에서 사용하는 소스코드 링크 링크바로가기
프로젝트 준비
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
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}} 님 환영합니다. <a href="/passport/logout">로그아웃</a></h2> {% else %} <p><a href="/passport/login">로그인</a> <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
댓글 ( 4)
댓글 남기기