StrongLoop / IBM์— ์˜ํ•ด ์ œ๊ณต์ด ๋ฒˆ์—ญ.

์ด ๋ฌธ์„œ๋Š” ์˜๋ฌธํŒ ๋ฌธ์„œ์— ๋น„ํ•ด ๋” ์˜ค๋ž˜๋œ ๋ฒ„์ „์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ์‹  ์—…๋ฐ์ดํŠธ๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด ์˜๋ฌธํŒ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

โœ–

์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ

๋‹ค๋ฅธ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์™€ ๋™์ผํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜๋Š” 3๊ฐœ๊ฐ€ ์•„๋‹Œ 4๊ฐœ์˜ ์ธ์ˆ˜, ์ฆ‰ (err, req, res, next)๋ฅผ ๊ฐ–๋Š”๋‹ค๋Š” ์ ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด๋Š” ๋‹ค๋ฅธ app.use() ๋ฐ ๋ผ์šฐํŠธ ํ˜ธ์ถœ์„ ์ •์˜ํ•œ ํ›„์— ๋งˆ์ง€๋ง‰์œผ๋กœ ์ •์˜ํ•ด์•ผ ํ•˜๋ฉฐ, ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


var bodyParser = require('body-parser');
var methodOverride = require('method-override');

app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
  // logic
});

๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜ ๋‚ด๋ถ€๋กœ๋ถ€ํ„ฐ์˜ ์‘๋‹ต์€ HTML ์˜ค๋ฅ˜ ํŽ˜์ด์ง€, ๋‹จ์ˆœํ•œ ๋ฉ”์‹œ์ง€ ๋˜๋Š” JSON ๋ฌธ์ž์—ด ๋“ฑ ์—ฌ๋Ÿฌ๋ถ„์ด ์„ ํ˜ธํ•˜๋Š” ๋ชจ๋“  ํ˜•์‹์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์กฐ์ง์ (๋ฐ ์ƒ์œ„ ๋ ˆ๋ฒจ ํ”„๋ ˆ์ž„์›Œํฌ) ๋ชฉ์ ์„ ์œ„ํ•ด, ์—ฌ๋Ÿฌ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ์™€ ๋งค์šฐ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด XHR๋ฅผ ์ด์šฉํ•œ ์š”์ฒญ ๋ฐ ๊ทธ๋ ‡์ง€ ์•Š์€ ์š”์ฒญ์— ๋Œ€ํ•œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ์ •์˜ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช…๋ น์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


var bodyParser = require('body-parser');
var methodOverride = require('method-override');

app.use(bodyParser());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);

์ด ์˜ˆ์—์„œ ์ผ๋ฐ˜ logErrors๋Š” ์š”์ฒญ ๋ฐ ์˜ค๋ฅ˜ ์ •๋ณด๋ฅผ stderr์— ๊ธฐ๋กํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


function logErrors(err, req, res, next) {
  console.error(err.stack);
  next(err);
}

๋˜ํ•œ ์ด ์˜ˆ์—์„œ clientErrorHandler๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋˜๋ฉฐ, ์ด ๊ฒฝ์šฐ ์˜ค๋ฅ˜๋Š” ๋ช…์‹œ์ ์œผ๋กœ ๊ทธ ๋‹ค์Œ ํ•ญ๋ชฉ์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.


function clientErrorHandler(err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' });
  } else {
    next(err);
  }
}

โ€œ๋ชจ๋“  ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”(catch-all)โ€ errorHandler ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


function errorHandler(err, req, res, next) {
  res.status(500);
  res.render('error', { error: err });
}

next() ํ•จ์ˆ˜๋กœ ์–ด๋– ํ•œ ๋‚ด์šฉ์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒฝ์šฐ('route'๋ผ๋Š” ๋ฌธ์ž์—ด ์ œ์™ธ), Express๋Š” ํ˜„์žฌ์˜ ์š”์ฒญ์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜๋ฉฐ, ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ์™€ ๊ด€๋ จ๋˜์ง€ ์•Š์€ ๋‚˜๋จธ์ง€ ๋ผ์šฐํŒ… ๋ฐ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๋ฅผ ์–ด๋–ป๊ฒŒ๋“  ์ฒ˜๋ฆฌํ•˜๊ธฐ ์›ํ•˜๋Š” ๊ฒฝ์šฐ, ๋‹ค์Œ ์„น์…˜์— ์„ค๋ช…๋œ ๊ฒƒ๊ณผ ๊ฐ™์ด ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ผ์šฐํŠธ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๊ฐ–๋Š” ๋ผ์šฐํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” route ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ ๋‹ค์Œ์˜ ๋ผ์šฐํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


app.get('/a_route_behind_paywall',
  function checkIfPaidSubscriber(req, res, next) {
    if(!req.user.hasPaid) {

      // continue handling this request
      next('route');
    }
  }, function getPaidContent(req, res, next) {
    PaidContent.find(function(err, doc) {
      if(err) return next(err);
      res.json(doc);
    });
  });

์ด ์˜ˆ์—์„œ getPaidContent ํ•ธ๋“ค๋Ÿฌ์˜ ์‹คํ–‰์€ ๊ฑด๋„ˆ๋›ฐ์ง€๋งŒ, /a_route_behind_paywall์— ๋Œ€ํ•œ app ๋‚ด์˜ ๋‚˜๋จธ์ง€ ํ•ธ๋“ค๋Ÿฌ๋Š” ๊ณ„์†ํ•˜์—ฌ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

next() ๋ฐ next(err)์— ๋Œ€ํ•œ ํ˜ธ์ถœ์€ ํ˜„์žฌ์˜ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ๊ณผ ํ•ด๋‹น ํ•ธ๋“ค๋Ÿฌ์˜ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. next(err)๋Š” ์œ„์— ์„ค๋ช…๋œ ๊ฒƒ๊ณผ ๊ฐ™์ด ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค์ •๋œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ์™ธํ•œ ์ฒด์ธ ๋‚ด์˜ ๋‚˜๋จธ์ง€ ๋ชจ๋“  ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ

Express๋Š” ๋‚ด์žฅ๋œ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ์™€ ํ•จ๊ป˜ ์ œ๊ณต๋˜๋ฉฐ, ๋‚ด์žฅ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ์•ฑ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋ณธ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋Š” ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜ ์Šคํƒ์˜ ๋์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

next()๋กœ ์˜ค๋ฅ˜๋ฅผ ์ „๋‹ฌํ•˜์ง€๋งŒ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ์—์„œ ํ•ด๋‹น ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๊ธฐ๋ณธ ์ œ๊ณต ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ•ด๋‹น ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ํ•ด๋‹น ์˜ค๋ฅ˜๋Š” ํด๋ผ์ด์–ธํŠธ์— ์Šคํƒ ์ถ”์ ๊ณผ ํ•จ๊ป˜ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. ์Šคํƒ ์ถ”์ ์€ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ๋•์…˜ ๋ชจ๋“œ์—์„œ ์•ฑ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ํ™˜๊ฒฝ ๋ณ€์ˆ˜ NODE_ENV๋ฅผ production์œผ๋กœ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค.

์‘๋‹ต์˜ ๊ธฐ๋ก์„ ์‹œ์ž‘ํ•œ ํ›„์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” next()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ(์˜ˆ: ์‘๋‹ต์„ ํด๋ผ์ด์–ธํŠธ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•˜๋Š” ์ค‘์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ), Express์˜ ๊ธฐ๋ณธ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ํ•ด๋‹น ์—ฐ๊ฒฐ์„ ๋‹ซ๊ณ  ํ•ด๋‹น ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž ์ •์˜ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ, ํ—ค๋”๊ฐ€ ์ด๋ฏธ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก๋œ ๊ฒฝ์šฐ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด Express ๋‚ด์˜ ๊ธฐ๋ณธ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ์œ„์ž„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:


function errorHandler(err, req, res, next) {
  if (res.headersSent) {
    return next(err);
  }
  res.status(500);
  res.render('error', { error: err });
}

๋งŒ์•ฝ next()๋ฅผ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ์—์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž ์ •์˜ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๊ธฐ๋ณธ ์˜ค๋ฅ˜ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋ฐœ๋™๋  ์ˆ˜ ์žˆ์Œ์— ์ฃผ์˜ํ•˜์‹ญ์‹œ์˜ค.