๐Ÿงฉ ๋ชฉ์ 

express์˜ ํŠน์ง•์— ๋Œ€ํ•ด์„œ๋Š” Node ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์•Œ์•„์•ผ ํ•  ๊ธฐ๋ณธ ์ง€์‹ ํฌ์ŠคํŒ…์„ ํ†ตํ•ด ์ •๋ฆฌํ–ˆ์œผ๋‹ˆ ์ด๋ฒˆ์—๋Š” express generator๋กœ ์ƒ์„ฑ๋˜๋Š” ์—ฌ๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด์™€ ๋กœ๊ทธ์ธ ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„์— ์‚ฌ์šฉ๋œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.


๐Ÿ“ฝ๏ธ ํ”„๋กœ์ ํŠธ ๊นƒํ—™ ๋ ˆํฌ์ง€ํ† ๋ฆฌ

[์šฐ์•„ํ•œํ…Œํฌ์บ ํ”„] ๋ฐฐ๋ฏผ์ƒํšŒ ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ ๊ตฌํ˜„ ํ”„๋กœ์ ํŠธ


๐Ÿญ Express ์ฃผ์š” ๋ฏธ๋“ค์›จ์–ด

pug

Express๋Š” ๋Ÿฐํƒ€์ž„์— ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์ด์šฉํ•ด์„œ ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š” staticํ•œ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์— ์‹ค์ œ ๊ฐ’์„ ๋„ฃ์–ด html ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.

Pug๋Š” ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ํ…œํ”Œ๋ฆฟ์—”์ง„์œผ๋กœ ํ…œํ”Œ๋ฆฟ์ด ์žˆ๋Š” ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ views์— ์ •ํ•ด์ฃผ๊ณ  view engine์œผ๋กœ pug๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


app.js์— view engine ์ข…๋ฅ˜์™€ template directory๋ฅผ ์ •ํ•ด์ฃผ๊ณ 

app.set('views', './views');
app.set('view engine', 'pug');

๋‹ค์Œ๊ณผ ๊ฐ™์ด index.pug ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  title์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๊ฐ’์„ ์ „๋‹ฌ๋ฐ›๋Š” template์„ ์„ค์ •ํ•˜๊ณ 

extends layout

block content
  h1= title
  p Welcome to #{title}

router.js ์—์„œ res.render ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•  template๊ณผ ๋ณ€์ˆ˜ ๊ฐ’์„ ์ „๋‹ฌํ•ด์ฃผ๋ฉด

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

template์— ๋ณ€์ˆ˜ ๊ฐ’์„ ๋„ฃ์–ด์„œ ํ•ด๋‹นํ•˜๋Š” ํŽ˜์ด์ง€๋ฅผ ์œ ์ €์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค.


morgan

morgan์€ ์ด๋ฆ„์—์„œ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์—†์ง€๋งŒ request๋ฅผ logging์„ ํ•ด์ฃผ๋Š” ์•„์ฃผ ์œ ์šฉํ•œ ๋ฏธ๋“ค์›จ์–ด์ด๋‹ค.

var logger = require('morgan');

// ยทยทยท

app.use(logger('dev');

morgan์„ ์‚ฌ์šฉํ•  ๋•Œ parameter๋กœ format ๊ฐ’์„ ๋ณด๋‚ด์ค„ ์ˆ˜ ์žˆ๋‹ค.(์ด ๋ฏธ๋ฆฌ ์ •ํ•ด์ง„ format์€ tiny, common, short ๋“ฑ ๋‹ค์–‘ํ•˜๋‹ค.)

dev format๋Š” request๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ console์— ๊ธฐ๋กํ•ด์ค€๋‹ค.

:method :url :status :response-time ms - :res[content-length]

dev format์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์˜ ๋ชจ์Šต์ด๋‹ค.

express-middleware-0


morgan์„ ์‚ฌ์šฉํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ์–ด๋–ค ์š”์ฒญ์—์„œ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค. ๊ทผ๋ฐ ๋งŒ์ผ ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๋ฐฐํฌํ•œ ์ƒํƒœ์—์„œ ์„œ๋ฒ„์˜ ์ƒํƒœ๋ฅผ ์ฝ˜์†”๋กœ๋งŒ ํ™•์ธํ•œ๋‹ค๋ฉด ๋กœ๊ทธ๋ฅผ ๋ชจ๋‘ ํ™•์ธํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์ด ์žˆ๋‹ค.


๐Ÿฏ๊ฟ€ํŒ ๐Ÿฏ

morgan์€ console ๋Œ€์‹  file์— logging ํ•˜๋„๋ก ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

rotating-file-stream์ด๋ผ๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์ผ ๋‹ค๋ฅธ ํŒŒ์ผ์— ๋กœ๊น…์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ  ์‹ฌ์ง€์–ด๋Š” console์—๋Š” ์—๋Ÿฌ ์š”์ฒญ๋งŒ ๊ธฐ๋กํ•˜๋„๋ก ํ•˜๊ณ  ํŒŒ์ผ์—๋Š” ๋ชจ๋“  ์š”์ฒญ์„ ๋‹ค ๊ธฐ๋กํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋‹ค์Œ์€ ๊ทธ ์˜ˆ์‹œ์ด๋‹ค!

var express = require('express');
var morgan = require('morgan');
var path = require('path');
var rfs = require('rotating-file-stream'); // version 2.x

var app = express();

// create a rotating write stream
var accessLogStream = rfs.createStream('access.log', {
  interval: '1d', // rotate daily
  path: path.join(__dirname, 'log'),
});

// log only 4xx and 5xx responses to console
app.use(
  morgan('dev', {
    skip: function (req, res) {
      return res.statusCode < 400;
    },
  }),
);

// log all requests to access.log
app.use(
  morgan('common', {
    stream: fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' }),
  }),
);
// ยทยทยท

express-session

express-session์€ ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜์„ ์ด์šฉํ•˜๊ฒŒ ํ•ด์ฃผ๊ณ  ์œ ์ € ์ฟ ํ‚ค์— ์„ธ์…˜ ์ •๋ณด๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋งค์šฐ ์œ ์šฉํ•œ ๋ฏธ๋“ค ์›จ์–ด์ด๋‹ค.

์œ ์ € ์ฟ ํ‚ค์— ์„ธ์…˜ ์ •๋ณด๋ฅผ ๋‹ด์„ ๋•Œ๋Š” ์„ธ์…˜์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋‹ด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์„ธ์…˜์˜ ์•„์ด๋”” ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.

์ด๋•Œ ์—ฌ๋Ÿฌ ์˜ต์…˜์„ ์ •ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ ์ฃผ์š” ์˜ต์…˜์˜ ํŠน์ง•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

secret : session Id๋ฅผ hashํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” key ๊ฐ’์ด๋‹ค.(ํ•ด์‰ฌํ•œ ๊ฐ’์ด connect.sid์— ์ €์žฅ๋œ๋‹ค.)

resave : ์„ธ์…˜์„ ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์„ธ์…˜์„ ๋ฐœ๊ธ‰ํ• ์ง€ ๋ง์ง€ ์ •ํ•˜๋Š” ๊ณณ์ด๋‹ค.(true ์„ค์ •์‹œ race condition์„ ๋ฐœ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ false๊ฐ€ ์ ์ ˆํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.)

saveUninitialized : ์„ธ์…˜ ID๋ฅผ ๋ฐœ๊ธ‰ํ•˜์ง€ ์•Š๋Š” ์„ธ์…˜๋„ ๋‹ค ๊ธฐ๋กํ• ์ง€ ์ •ํ•œ๋‹ค.(๋กœ๊ทธ์ธ์— ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์„ ์ค„์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด false๋กœ ์„ค์ •ํ•œ๋‹ค.)

storage : ์„ธ์…˜์„ ์–ด๋””์— ์ €์žฅํ• ์ง€ ์ •ํ•œ๋‹ค.(๊ธฐ๋ณธ ๊ฐ’์€ MemoryStore๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— production์— ์‚ฌ์šฉํ•˜๋ฉด memory leak์ด ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์„ธ์…˜์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ storage๊ฐ€ ์กด์žฌํ•œ๋‹ค.)

express-middleware-2

๊ทธ๋Ÿผ ๊ฐ„๋‹จํžˆ ์–ด๋–ป๊ฒŒ ๋Œ์•„๊ฐ€๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

๋‹ค์Œ ์ฝ”๋“œ๋Š” session ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ง€๋‚˜๊ฐ€๋ฉด์„œ ์–ด๋–ค req์— ์–ด๋–ค ๋‚ด์šฉ์ด ์ƒ๊ธฐ๋Š”์ง€ ๋ณด๋Š” ์ฝ”๋“œ์ด๋‹ค.

var express = require('express');
var session = require('express-session');

var app = express();

function logSessionInfo(req) {
  console.log(`req.session : ${req.session}`);
  console.log(`req.sessionID : ${req.sessionID}`);
}

app.use(function (req, res, next) {
  console.log(`express-session ํ†ต๊ณผ ์ „`);
  console.log(`req.headers.cookie : ${req.session}`);
  logSessionInfo(req);
  next();
});

app.use(
  session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
  }),
);

app.use(function (req, res, next) {
  console.log(`express-session ํ†ต๊ณผ ํ›„`);
  logSessionInfo(req);
  next();
});

// ยทยทยท

์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

express-middleware-3


๐Ÿ—’๏ธ์ •๋ฆฌ ๐Ÿ—’๏ธ

express-sesssion์€ req.headers.cookie์— ์ฃผ์–ด์ง„ sid๋ฅผ sessionID๋กœ ๋ฒˆ์—ญํ•˜๊ณ  ๊ทธ sessionID์— ํ•ด๋‹นํ•˜๋Š” session ๊ฐ’์„ req.session์— ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค!

์ฆ‰, ์„ธ์…˜์˜ ์ •๋ณด ๊ฐ’์€ ์ฟ ํ‚ค์— ์ „๋‹ฌ๋˜์ง€ ์•Š๊ณ  ํ•ด์‰ฌํ™”๋œ sid๋งŒ ์œ ์ €์˜ ์ฟ ํ‚ค์— ์ €์žฅ๋˜๊ณ  ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด sid๋ฅผ ๋ฒˆ์—ญํ•˜๊ณ  ์–ป์€ session ID์— ํ•ด๋‹นํ•˜๋Š” session ๊ฐ’์„ req.session์— ๋„ฃ์–ด์ฃผ์–ด nextํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค!!(์ด์ œ ์ข€ ์•Œ๊ฒ ๋‹คโ€ฆใ…‹ใ…‹ใ…‹)


๐Ÿค ๋ณธ๊ฒฉ express๋กœ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ

๊ทธ๋Ÿผ ์ด์ œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„์˜ express์˜ middleware์„ ์•Œ์•„๋ณด๊ณ  ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž.

passport

passport๋Š” node์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” authentication ๋ฏธ๋“ค์›จ์–ด์ด๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ username๊ณผ password๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋กœ๊ทธ์ธ ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ OpenID์™€ OAuth ์ „๋žต๋„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

passport์—์„œ ์ œ๊ณตํ•˜๋Š” ์—ฌ๋Ÿฌ strategy๋ฅผ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

express-middleware-4

๋‹ค์Œ๊ณผ ๊ฐ™์ด passport์™€ ํ•˜๋‚˜์˜ Strategy๋ฅผ ๊ฐ€์ ธ์™€์„œ

const passport = require('passport');
const localStrategy = require('passport-local').Strategy;

์–ด๋–ค ์‹์œผ๋กœ strategy์˜ ๊ตฌ์ฒด์ ์ธ ๋‚ด์šฉ์„ ์ •์˜ํ•˜๊ณ  passport์— ๋„ฃ์–ด์ค€๋‹ค.

passport.use(new LocalStrategy(
	//verify callback ํ•จ์ˆ˜
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
			// ์•„์ด๋”” ์—†์Œ
      if (!user) {
        return donnull, false, { message: 'Incorrect username.' });
      }
			// ๋น„๋ฐ€๋ฒˆํ˜ธ ํ‹€๋ฆผ
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
			// ์„ฑ๊ณต
      return done(null, user);
    });
  }
));

๊ทธ๋ฆฌ๊ณ  passport.authenticate๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด login request์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

app.post('/login', passport.authenticate('local'), function (req, res) {
  // If this function gets called, authentication was successful.
  // `req.user` contains the authenticated user.
  res.redirect('/users/' + req.user.username);
});

passport.authentication ํ•จ์ˆ˜๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด req.user์— ์œ ์ € ์ •๋ณด๋ฅผ ๋„ฃ์–ด์„œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜์ง€๋งŒ ๋กœ๊ทธ์ธ ์‹คํŒจ์‹œ ๋ฐ”๋กœ 401 authentication error ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.


โ“๊ทธ๋Ÿผ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋Š” ์–ด๋””์„œ ๊ฒฐ์ • ๋ ๊นŒ?

์œ„์˜ ์ฝ”๋“œ์—์„œ username, password, done์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋Š” verify callback๋ผ๊ณ  ํ•˜๋Š”๋ฐ ์—ฌ๊ธฐ์„œ authentication ์„ฑ๊ณต๊ณผ ์‹คํŒจ ์—ฌ๋ถ€๋ฅผ doneํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•œ๋‹ค. (์ด๋•Œ ์ „๋‹ฌ๋œ ๊ฐ’์ด req.user์— ๋„ฃ์–ด์ง„๋‹ค.)

์„ฑ๊ณต์‹œ์—๋Š” done์˜ ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ ์œ ์ € ์ •๋ณด๋กœ ์‹คํŒจ์‹œ์—๋Š” false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์—๋Ÿฌ ๋ฐœ์ƒ์‹œ์—๋Š” ์ฒซ๋ฒˆ์งธ ์ธ์ž์— ์—๋Ÿฌ๋ฅผ ๋„ฃ์–ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค. // done(err);


โ“๋กœ๊ทธ์ธ ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?

passport ๋˜ํ•œ session์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌด์กฐ๊ฑด session ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ด์šฉํ•œ ํ›„์— passport์˜ session์„ ์‚ฌ์šฉํ•œ๋‹ค.

app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());

์„ธ์…˜์„ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” serializeUser์™€ deserializeUser ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

passport.serializeUser(function (user, done) {
  done(null, user.id);
});

passport.deserializeUser(function (id, done) {
  User.findById(id, function (err, user) {
    done(err, user);
  });
});

๐Ÿ“ฉ serializeUser

verify callback๊ฐ€ ์œ ์ € ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜๋กœ, ์„ฑ๊ณตํ•œ ์œ ์ €์˜ ์ •๋ณด๋ฅผ session์— ์ถ”๊ฐ€ํ•œ๋‹ค.


๐Ÿ“ค deserializeUser

deserializeUser์€ Cookie์— ์ €์žฅ๋œ passport session ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ User์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜์ด๋‹ค.
์•ž์—์„œ session๊ณผ ๊ฐ™์ด cookie์—๋Š” ๊ตฌ์ฒด์ ์ธ ์œ ์ €์ •๋ณด๊ฐ€ ๋‹ด๊ธฐ์ง€ ์•Š๊ณ  passport session id๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๋‹ค.

๐ŸŽˆsession ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ user์˜ id๋งŒ ์ €์žฅํ•˜๊ณ  ์š”์ฒญ์ด ์˜ค๋ฉด id์— ํ•ด๋‹นํ•˜๋Š” ์œ ์ €์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์„œ ์“ฐ๋ฉด ์ข‹๋‹ค.


โ“๊ทธ๋Ÿผ passport session์€ ์œ ์ €์—๊ฒŒ ์–ด๋–ป๊ฒŒ ์ €์žฅ๋ ๊นŒ?

passport์šฉ ์„ธ์…˜์ด ์ƒˆ๋กœ ์ƒ๊ธฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ express session์— ํ•˜๋‚˜์˜ property๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.
cookie์— ๋“ค์–ด์žˆ๋Š” express session์ด ์ƒ์„ฑํ•œ connect.sid์„ ํ’€๋ฉด ๊ทธ ์•ˆ์— passport session ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

express-middleware-5


โ“๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ผ ํŽ˜์ด์ง€ ์ ‘๊ทผ ์ œ์–ดํ•˜๊ธฐ!

๋งˆ์ดํŽ˜์ด์ง€์™€ ๊ฐ™์€ ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ์œ ์ €์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ผ์„œ ํŽ˜์ด์ง€๋ฅผ ๋‹ฌ๋ฆฌ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์ด๋Ÿฐ ์ƒํ™ฉ์—๋Š” passport์— ์˜ํ•ด req์— ์ƒ์„ฑ๋œ isAuthenticated๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•จ์ˆ˜์˜ ํ˜•ํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

req.isAuthenticated = function () {
  var property = 'user';
  if (this._passport && this._passport.instance) {
    property = this._passport.instance._userProperty || 'user';
  }

  return this[property] ? true : false;
};

์œ„์˜ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์ „์— ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์—ฌ ๋กœ๊ทธ์ธ ๋˜์ง€ ์•Š์€ req๋ฅผ login page๋กœ redirect ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

function isAuthenticated(req, res, next) {
  if (req.isAuthenticated()) return next();
  return res.redirect('/login');
}

// ยทยทยท

router.get('/mypage', isAuthenticated, (req, res) => res.render('mypage', { user: req.user }));

bcrypt

bcrpyt๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‰ฝ๊ฒŒ ์•”ํ˜ธํ™”ํ•ด์ฃผ๋Š” ๋ฏธ๋“ค์›จ์–ด์ด๋‹ค.

hash๋ฅผ ์ƒ์„ฑํ• ๋•Œ๋Š” bcrypt์˜ hash ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•  ๋•Œ๋Š” compare ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•œ๋‹ค.

const saltRounds = 10;
user.passwordHash = await bcrypt.hash(password, saltRounds);
const match = await bcrypt.compare(password, user.passwordHash);

โ“bcrpyt๊ฐ€ ๊ทผ๋ฐ ๋ญ์ง€์š”? ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” ๊ด€๋ จ ์‚ฌ์ง„ ์ถœ์ฒ˜ ๋ฐ ์ฐธ๊ณ ์ž๋ฃŒ

๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ hashing ๋งŒ์œผ๋กœ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š” ์œ ์ถ” ๊ฐ€๋Šฅ์„ฑ์ด๋‚˜ ๋น ๋ฅธ ์†๋„๋กœ ์ธํ•ด ํ•ด์ปค๋“ค์—๊ฒŒ ํŽธ์˜์„ฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด salt๋ฅผ ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๋”ํ•œ ํ›„์— hashing์„ ์ง„ํ–‰ํ•˜์—ฌ ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
bcrypt๋Š” ์ด์— ๋”ํ•ด ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์„ ๋ช‡๋ฒˆ ์ง„ํ–‰ํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” โ€˜work factorโ€™(์—ฌ๊ธฐ์„œ๋Š” saltRounds์ด๋‹ค)๋ฅผ ์กฐ์ •ํ•˜๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ ์‹œ์Šคํ…œ ๋ณด์•ˆ์„ฑ์„ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค๊ณ  ํ•œ๋‹ค.

express-middleware-6


flash

flash๋Š” ์„ธ์…˜์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ณต๊ฐ„์ด๋‹ค.

flash์— ์ž‘์„ฑ๋œ ๋ฉ”์‹œ์ง€๋Š” ํ•œ๋ฒˆ ์œ ์ €ํ•œํ…Œ display๋˜๋ฉด ๋ฐ”๋กœ ์‚ญ์ œ๋œ๋‹ค.


โ“์–ด๋””์— ์“ธ๊นŒ? ๋กœ๊ทธ์ธ ์‹คํŒจ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์—!

์ฃผ๋กœ redirect์™€ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š”๋ฐ ํŠนํžˆ ๋กœ๊ทธ์ธ ์‹คํŒจ ์ด์œ  ๋ฉ”์‹œ์ง€๋ฅผ ๋„์šธ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค ใ…Žใ…Ž

passport๋Š” flash๋ฅผ ์ด์šฉํ•ด์„œ message๋ฅผ ๋„์šฐ๋Š” ๊ฑธ ์ง€์›ํ•œ๋‹ค.

verify callback ํ•จ์ˆ˜๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด message๋ฅผ ๊ฐ™์ด ๋ณด๋‚ด์ฃผ๊ฒŒ ๋œ๋‹ค. ์ด ๋ถ€๋ถ„์€ flash์— ์ €์žฅ๋˜๊ฒŒ ๋œ๋‹ค.

// ยทยทยท
if (!user) {
  return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
  return done(null, false, { message: 'Incorrect password.' });
}
// ยทยทยท

๊ทธ๋ฆฌ๊ณ  req.flash()๋ฅผ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ๊ฐ’์„ ์œ ์ €์—๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค!(ํ•œ๋ฒˆ ๋ณด์—ฌ์ฃผ๋ฉด ์‚ฌ๋ผ์ง„๋‹ค.)

router.get('/login', (req, res) =>
  req.isAuthenticated()
    ? res.redirect('/mypage')
    : res.render('login', { failureMsg: req.flash().error }),
);

express-middleware-7


๐Ÿ“„ ๋กœ๊ทธ์ธ ์ •๋ณด ์ €์žฅ์šฉ ํŒŒ์ผ ๊ธฐ๋ฐ˜ ๋””๋น„ ๊ตฌํ˜„ํ•˜๊ธฐ

์ด์ „์— ์‚ฌ์šฉํ–ˆ๋˜ sequelize์™€ ๊ฐ™์€ ORM๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ํ…Œ์ด๋ธ” ํ˜•ํƒœ๋ฅผ ์ •์˜ํ•˜๊ณ  create, update, getById ํ•จ์ˆ˜๋ฅผ ๋‚ด์žฅํ•œ ๋ชจ๋ธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค.

validate ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์ด ํ…Œ์ด๋ธ”์˜ attributes์˜ ํƒ€์ž…๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜๋„ ๊ตฌํ˜„ํ•ด๋ดค๋‹ค ใ…Žใ…Ž

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

class Model {
  static sync = function () {
    if (fs.existsSync(this.fileName)) {
      const data = fs.readFileSync(this.fileName, 'utf8');
      this.items = JSON.parse(data);
    }
  };

  static init = (attributesTypes, { tableName, sync }) => {
    this.tableName = tableName;
    this.dirname = path.join(__dirname, `../database`);
    this.fileName = `${this.dirname}/${tableName}.json`;
    this.attributesTypes = attributesTypes;
    this.items = [];
    if (sync) this.sync();
  };

  static write = function () {
    const data = JSON.stringify(this.items);
    if (!fs.existsSync(this.dirname)) fs.mkdirSync(this.dirname);
    fs.writeFileSync(this.fileName, data, 'utf8');
  };

  static validate = function (item) {
    const data = {};
    for (const [key, value] of Object.entries(item)) {
      if (!this.attributesTypes[key] || !value) continue;

      switch (this.attributesTypes[key]) {
        case 'date':
          data[key] = Date(value);
          break;
        case 'boolean':
          data[key] = value == 'on' || value == 1;
          break;
        case typeof value:
          data[key] = value;
          break;
        default:
          throw new Error('User Input Error');
      }
    }
    data.createdAt = Date.now();
    data.updatedAt = Date.now();
    return data;
  };

  static getById = (id) => this.items.find((item) => item.id === id);

  static create = function (item) {
    const validatedItem = this.validate(item);
    this.items.push(validatedItem);
    this.write();
    return validatedItem;
  };

  static update = function (item) {
    const id = this.items.findIndex(item.id);
    item.updatedAt = Date.now();
    this.items[id] = item;
    this.write();
  };

  static delete = function (item) {
    const id = this.items.findIndex(item.id);
    item.updatedAt = Date.now();
    if (id) this.items[id] = null;
    this.write();
  };
}

module.exports = Model;

๊ทธ๋ฆฌ๊ณ  User table์„ ์œ„์˜ ๋ชจ๋ธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค:)

const Model = require('./model');

class Users extends Model {
  static init() {
    return super.init(
      {
        id: 'string',
        pw: 'string',
        email: 'string',
        name: 'string',
        phoneNo: 'string',
        address1: 'string',
        address2: 'string',
        zipCode: 'string',
        isAdAgreed: 'boolean',
      },
      {
        tableName: 'Users',
        sync: true,
      },
    );
  }
}

module.exports = Users;

๐Ÿ“ฝ๏ธ ํ”„๋กœ์ ํŠธ ๊นƒํ—™ ๋ ˆํฌ์ง€ํ† ๋ฆฌ

[์šฐ์•„ํ•œํ…Œํฌ์บ ํ”„] ๋ฐฐ๋ฏผ์ƒํšŒ ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ ๊ตฌํ˜„ ํ”„๋กœ์ ํŠธ


๐Ÿ’ญ ํšŒ๊ณ 

  • ์ด๋ฒˆ ๊ธฐํšŒ๋ฅผ ํ†ตํ•ด์„œ ์„ธ์…˜์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๊ณ  ํŠนํžˆ passport์—์„œ ์„ธ์…˜์ด ์–ด๋–ป๊ฒŒ ์ด์šฉ๋˜๋Š”์ง€ ์ข€๋” ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.
  • ๋‹ค์Œ์—๋Š” morgan์„ ํŒŒ์ผ๋กœ ๋กœ๊น…ํ•˜๋Š”๋ฐ ๊นŒ์ง€ ์‚ฌ์šฉํ•ด๋ด์•ผ๊ฒ ๋‹ค.
  • ๋ชจ๋ฅด๊ณ  ์‚ฌ์šฉํ•ด์„œ ์‚ฌ์‹ค ๋ถˆ์•ˆํ•œ ๋ถ€๋ถ„๋“ค์ด ๋งŽ์•˜๋Š”๋ฐ ์ด๋ฒˆ ๊ธฐํšŒ์— ๊ทธ๋Ÿฐ ๋ถˆ์•ˆํ•จ์ด ๋งŽ์ด ํ•ด์†Œ๋œ ๊ฒƒ ๊ฐ™์•„ ๋ณด๋žŒ์ฐจ๋‹ค.
  • โ“๋ฌด์—‡๋ณด๋‹ค ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋„ˆ๋ฌด ๋ถ€์กฑํ•˜๋‹ˆ ๋‹ค์Œ ๊ธฐํšŒ์—๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ณต๋ถ€์—๋„ ๋” ๋งŽ์ด ์‹ ๊ฒฝ์จ๋ด์•ผ๊ฒ ๋‹ค!

์ž˜๋ชป ์ •๋ฆฌ๋œ ์ ์ด๋‚˜ ํ”ผ๋“œ๋ฐฑ ์žˆ์œผ๋ฉด ๋ง์”€ํ•ด์ฃผ์„ธ์š”!