생활 코딩 강의 목록 : 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
참조 :
인증을 쉽게 도와주는 PassportJS 모듈
23.Passportjs 1 : intro
23.Passportjs 2 : configure
1) 공식 문서 참조
1) https://www.passportjs.org/
2)https://www.npmjs.com/package/passport
3) https://www.passportjs.org/howtos/password/
4)https://www.passportjs.org/tutorials/password/verify/
5) https://www.passportjs.org/concepts/authentication/downloads/html/
2) 설치
1. passport 설치
$ npm install passport
2. passport-local 설치
$ npm install passport-local
3. mysql 설치
$npm i mysql
4. express-mysql-session 설치
& npm i express-mysql-session
5. flash 설치
$ npm install connect-flash
24.Passportjs 3 : route
25.Passportjs 4 : serialize
26.Passportjs 5 : logout
26.Passportjs 6 : review

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;
1)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);
//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());
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) lib/db.js
const mysql = require('mysql');
const db = mysql.createConnection({
host: 'localhost',
user: 'opentutorials',
password: '1111',
database: 'opentutorials'
});
db.connect();
module.exports = db;
★현재 프로젝트에서는 적용하지 않았으나,
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);
}
));
3) lib/passport.js
const passport = require("passport");
const crypto = require('crypto');
const db = require("../lib/db");
const LocalStrategy = require('passport-local');
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: '등록된 이메일이 없습니다.' }); }
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: '로그인 성공' });
});
});
}));
return passport;
}
4) 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) {
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
}));
//로그 아웃 처리
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); }
db.query('INSERT INTO users (username,email, hashed_password, salt) VALUES (?, ?, ?, ?)', [
req.body.username,
req.body.email,
hashedPassword.toString('hex'),
salt
], function (err, results, fields) {
if (err) {
req.flash("error", "이이 등록된 처리된 아이디 혹은 이메일 입니다.");
return res.redirect('/passport/signup');
}
var user = {
id: this.lastID,
username: req.body.username
};
console.log("등록한 insertId :", results.insertId);
req.login(user, function (err) {
if (err) { return next(err); }
req.flash("success", "회원가입을 축하합니다.");
res.redirect('/passport/login');
});
});
});
});
});
return router;
}
5) 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 %}
6) 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">
</p>
</form>
{% endblock %}
7) 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 %}













댓글 ( 4)
댓글 남기기