๐ŸŒณ๋ชฉํ‘œ

๋ฏธ๋“ค์›จ์–ด ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜์—ฌ serve-static ๋ชจ๋“ˆ์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด ํŒจํ„ด

์„œ๋ฒ„๋Š” ์š”์ฒญ์—์„œ๋ถ€ํ„ฐ ์‘๋‹ต๊นŒ์ง€ ํ•˜๋‚˜์˜ ํ๋ฆ„์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์š”์ฒญ๊ณผ ์‘๋‹ต ์‚ฌ์ด์— ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ ๋ชฉ๋ก์„ ์šฐ๋ฆฌ๋Š” โ€œ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜โ€๋ผ๊ณ  ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋Š” ๋ณธ์—ฐ์˜ ์—ญํ• ์„ ํ•œ ๋’ค ๋‘ ๊ฐ€์ง€ ์ผ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. โ–ฒ ์š”์ฒญํ•œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ต ํ•˜๊ฑฐ๋‚˜ โ–ฒ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด์ฃ . ํ›„์ž์ผ ๊ฒฝ์šฐ ํ˜„์žฌ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

๋จธ๋ฆฟ์† ์ด๋ฏธ์ง€์™€ ์•„๋ž˜ ๊ทธ๋ฆผ์ด ๊ฐ™๋‹ค๋ฉด ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์‹  ๊ฒ๋‹ˆ๋‹ค.

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์—์„œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ถ€๋ถ„๊ณผ, ์š”์ฒญ์ด ์˜ฌ๋•Œ ๋“ฑ๋ก๋œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜ ๋ชจ๋‘๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ฃผ์š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค.

๊ฐ๊ฐ ์Šˆ๋„ ์ฝ”๋“œ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด ๋“ฑ๋ก:

const middlewares = []
const use = fn => middlewares.push(fn)

๋ฏธ๋“ค์›จ์–ด ์‹คํ–‰:

let next = null
const run = () => middlewares.forEach(mw => {
  next = mw(next)
})

์ต์Šคํ”„๋ ˆ์ŠคJS์˜ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ๊ถ๊ธˆํ•˜์‹œ๋‹ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ณด์…”๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

Middleware ๋ชจ๋“ˆ

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

$ git checkout -f middleware/spec

src/Middleware.spec.js ํŒŒ์ผ์— ๋ฏธ๋“ค์›จ์–ด ์š”๊ตฌ์‚ฌํ•ญ์ด ์ ์ธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋‘ ๋‹ค์„ฏ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ„์–ด ์ฝ”๋“œ๋ฅผ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

require('should');
const sinon = require('sinon');
const Middleware = require('./Middleware');

describe('Middleware', () => {
  let middleware;
  beforeEach(()=> {
    middleware = Middleware();
  })

  it('์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹ค', () => {
    middleware._middlewares.length.should.be.equal(0);
  })

ํ…Œ์ŠคํŠธ ๊ด€๋ จํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ธ Middleware ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๋ฌผ๋ก  Middleware๋Š” ์•„์ง ๋งŒ๋“ค์ง€ ์•Š์•˜๊ตฌ์š”.

beforeEach์—์„œ ๋ฏธ๋“ค์›จ์–ด ๋ชจ๋“ˆ์„ ์ด์šฉํ•ด ๋ฏธ๋“ค์›จ์–ด ์ธ์Šคํ„ด์Šค๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์ฒซ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” โ€œ์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. middleware ์ธ์Šคํ„ด์Šค์˜ ํ…Œ์ŠคํŠธ ์ „์šฉ ์†์„ฑ์ธ _middlewares๋ฅผ ํ†ตํ•ด ๋ฐฐ์—ด์˜ ๊ธธ์ด๊ฐ€ 0์ธ์ด ํ™•์ธํ•˜๊ณ  ์žˆ์ฃ ?

  describe('add()', () => {
    it('๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค', () => {
      const fns = [
        ()=>{},
        ()=>{},
        ()=>{},
      ]

      fns.forEach(fn => middleware.add(fn));

      middleware._middlewares.length.should.be.equal(fns.length)
    })
  });

๋‘๋ฒˆ์งธ ํ…Œ์ŠคํŠธ์ผ€์ด์Šค๋Š” โ€œ๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ํ…Œ์ŠคํŠธ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ ๋นˆ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜ 3๊ฐœ๋ฅผ ๋งŒ๋“ค์–ด fns ๋ฐฐ์—ด์— ์ €์žฅํ•˜๊ณ  ์ด๊ฒƒ์„ middleware.add() ๋ฉ”์†Œ๋“œ๋กœ ๋“ฑ๋กํ–ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•  ๋ชจ๋“ˆ์€ add() ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ ธ์•ผํ•˜๊ฒ ์ฃ ?

๋ฉ”์†Œ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ฐฐ์—ด์˜ ๊ธธ์ด๊ฐ€ 3์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

  describe('run()', () => {
    it('๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค', () => {
      const stub = {
        mw1() {},
        mw2() {}
      };
      sinon.stub(stub, 'mw1').callsFake((req, res, next) => next());
      sinon.stub(stub, 'mw2').callsFake((req, res, next) => next());

      const fns = [
        stub.mw1,
        stub.mw2,
      ]
      fns.forEach(fn => middleware.add(fn));

      middleware.run();

      fns.forEach(fn => {
        should(fn.called).be.equal(true)
      })
    })

์„ธ๋ฒˆ์งธ ํ…Œ์Šค๋Š” ์‹œ๋…ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ stub ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์Šคํ…์ด๋ž€ ์ง„์งœ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋Š” ํ…Œ์ŠคํŠธ์šฉ ๋ฉ”์†Œ๋“œ์ธ๋ฐ์š” ์—ฌ๊ธฐ์„œ๋Š” ๋ฏธ๋“ค์›จ์–ดํ•จ์ˆ˜ mw1, mw2์— ์Šคํ…์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” req, res, next๋ผ๋Š” ์ธ์ž ์„ธ ๊ฐœ๋ฅผ ๋ฐ›๊ณ  ๋งˆ์ง€๋ง‰ ์ธ์ž๋ฅผ ์‹คํ–‰(invoke)ํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด๊ฑด ๋ฏธ๋ฆฌ ์šฐ๋ฆฌ๊ฐ€ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€ ์ •์˜ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋Ÿฐ ์‹์œผ๋กœ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ณ  ์‹ค์ œ ๋ฏธ๋“ค์›จ์–ด๋„ ๊ทธ๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผ๊ฒ ์ง€์š”.

๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ add()๋กœ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋“ฑ๋กํ•˜๊ณ  run() ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋„ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•ด์•ผ๊ฒ ์ฃ .

๋ฉ”์†Œ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ชจ๋“  ์Šคํ…์ด ์‹คํ–‰๋˜์—ˆ๋Š”์ง€ ์ ๊ฒ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ โ€œrun()์€ ๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด์ง€์š”.

    it('next๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜ ์ฒด์ธ์„ ์ฆ‰์‹œ ์ค‘์ง€ํ•œ๋‹ค', () => {
      const stub = {
        mw1() {},
        mwWillStop() {}, // next๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ฏธ๋“ค์›จ์–ด
        mw2() {}
      };
      sinon.stub(stub, 'mw1').callsFake((req, res, next) => next());
      sinon.stub(stub, 'mwWillStop').callsFake(() => null);
      sinon.stub(stub, 'mw2').callsFake((req, res, next) => next());

      const fns = [
        stub.mw1,
        stub.mwWillStop,
        stub.mw2,
      ]
      fns.forEach(fn => middleware.add(fn));

      middleware.run();

      fns.forEach((fn, idx) => {
        const shouldInvoked = idx < 2
        should(fn.called).be.equal(shouldInvoked)
      });
    });

์ด์ „ ํ…Œ์ŠคํŠธ์™€ ๋น„์Šทํ•œ๋ฐ ์˜ˆ์™ธ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. โ€œ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๊ฐ€ next()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ „์ฒด ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ฆ‰์‹œ ์ค‘๋‹จํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด์ฃ . ๋‘๋ฒˆ์งธ ๋ฏธ๋“ค์›จ์–ด ์Šคํ… mwWillStop์€ ๋‹ค๋ฅธ ๋ฏธ๋“ค์›จ์–ด์™€ ๋‹ฌ๋ฆฌ next()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

add()๋กœ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋“ฑ๋กํ•˜๊ณ  run()์œผ๋กœ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์—” ๋‘ ๋ฒˆ์งธ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰๋˜์—ˆ๋Š”์ง€ ์ ๊ฒ€ํ•ฉ๋‹ˆ๋‹ค.

    it('์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹ค', () => {
      const stub = {
        mw1(req, res, next) {},
        mwWillThrow(req, res, next) {}, // ์—๋Ÿฌ ๋ฐœ์ƒ ๋ฏธ๋“ค์›จ์–ด
        mw2(req, res, next) {},
        mwWillCatchError(err, req, res, next) {} // ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด
      };
      sinon.stub(stub, 'mw1').callsFake((req, res, next) => next());
      sinon.stub(stub, 'mwWillThrow').callsFake((req, res, next) => next(Error()));
      sinon.stub(stub, 'mw2').callsFake((req, res, next) => next());
      sinon.stub(stub, 'mwWillCatchError').callsFake((err, req, res, next) => null);

      const fns = [
        stub.mw1,
        stub.mwWillThrow,
        stub.mw2,
        stub.mwWillCatchError,
      ]
      fns.forEach(fn => middleware.add(fn));

      middleware.run();

      fns.forEach((fn, idx) => {
        const shouldInvoked = idx !== 2;
        should(fn.called).be.equal(shouldInvoked)
      });
    })
  })
});

๋งˆ์ง€๋ง‰ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๊ธธ์ง€๋งŒ ๊ทธ๋ž˜๋„ ๊พน ์ฐธ๊ณ  ์ฝ์–ด๋ณด์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

โ€œ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.

์ต์Šคํ”„๋ ˆ์Šค ๋ฏธ๋“ค์›จ์–ด ๋ฌธ์„œ๋ฅผ ์ฝ์–ด๋ณด์…จ๋‚˜์š”? ๊ทธ๊ฒƒ์€ ์ธ์ž ๊ฐฏ์ˆ˜์— ๋”ฐ๋ผ ๋‘ ๊ฐ€์ง€๋กœ ๋ถ„๋ฅ˜ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ผ๋ฐ˜ ๋ฏธ๋“ค์›จ์–ด: ์ธ์ž ์„ธ ๊ฐœ (req, res, next)
  • ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด: ์ธ์ž ๋„ค ๊ฐœ (err, req, res, next)

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

๋ฏธ๋“ค์›จ์–ด ์Šคํ…์„ ๋„ค ๊ฐœ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ์š” ๋‘๋ฒˆ์งธ mwWillThrow ๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋˜์ง€๋Š” ๋ฏธ๋“ค์›จ์–ด์ด๊ณ  mwWillCatchError๊ฐ€ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด์ž…๋‹ˆ๋‹ค.

add()๋กœ ๋“ฑ๋กํ•˜๊ณ  run()์œผ๋กœ ์‹คํ–‰ํ•œ๋’ค 3๋ฒˆ์žฌ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ๋ฏธ์‹คํ–‰ ๋˜์—ˆ๋Š” (๋‘๋ฒˆ ์งธ์—์„œ ๋„ค ๋ฒˆ์งธ๋กœ ๋„˜์–ด๊ฐ”๋Š”์ง€) ์ฒดํฌํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

๐Ÿค์‹ค์Šต - 1๋ฒˆ ์š”๊ตฌ์‚ฌํ•ญ ๊ตฌํ˜„

โ€œ์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹คโ€ ๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๋จผ์ € ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”.

๐Ÿคํ’€์ด

์ฒซ๋ฒˆ์งธ ์š”๊ตฌ์‚ฌํ•ญ์€ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ฃ ? src ํด๋”์— Middleware.js ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

const Middleware = () => {
  const _middlewares = [];

  return {
    _middlewares,
  }
}

module.exports = Middleware;

_middlewares์— ๋นˆ ๋ฐฐ์—ด์„ ํ• ๋‹นํ•˜๊ณ  ๋ฐ”๋กœ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ๋ฆฌํ„ด ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋Œ๋ ค ๋ณผ๊นŒ์š”?

$ npm test

  Middleware
    โœ“ ์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹ค
    add()
      1) ๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค
    run()
      2) ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
      3) next๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜ ์ฒด์ธ์„ ์ฆ‰์‹œ ์ค‘์ง€ํ•œ๋‹ค
      4) ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹ค

์ฒซ ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ํ†ต๊ณผ ํ–ˆ์Šต๋‹ˆ๋‹ค.โœ…

๐Ÿค์‹ค์Šต - 2๋ฒˆ ์š”๊ตฌ์‚ฌํ•ญ ๊ตฌํ˜„

โ€œadd() ๋ฉ”์†Œ๋“œ๋Š” ๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”.

ํžŒํŠธ: ์ธ์ž๋Š” ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜

๐Ÿคํ’€์ด

์ด๊ฒƒ๋„ ์•„์ฃผ ๊ฐ„๋‹จํžˆ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

  const add = fn => {
    _middlewares.push(fn)
  }

  return {
    _middlewares,
    add,
  }

ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค ๋ณผ๊นŒ์š”?

$ npm test 

  Middleware
    โœ“ ์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹ค
    add()
      โœ“ ๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค
    run()
      1) ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
      2) next๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜ ์ฒด์ธ์„ ์ฆ‰์‹œ ์ค‘์ง€ํ•œ๋‹ค
      3) ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹ค

๋‘๋ฒˆ์งธ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊นŒ์ง€ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค.โœ…

๐Ÿค์‹ค์Šต - 3๋ฒˆ ์š”๊ตฌ์‚ฌํ•ญ ๊ตฌํ˜„

โ€œrun() ๋ฉ”์†Œ๋“œ๋Š” ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”.

ํžŒํŠธ: run ๋ฉ”์†Œ๋“œ ์ธ์ž๋Š” req์™€ res, ์žฌ๊ท€ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉ?

๐Ÿค ํ’€์ด

์ด๋ฒˆ ์š”๊ตฌ์‚ฌํ•ญ์€ ์ข€ ํž˜๋“ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌ๊ท€๊ฐ€ ๋“ค์–ด ๊ฐ€๋Š” ๋ถ€๋ถ„์ด ์žˆ์–ด์„œ ๋”์šฑ ๊ทธ๋ ‡๊ตฌ์š”. ๊ทธ๋Ÿผ ๊ฐ™์ด ํ’€์–ด ๋ณด์ง€์š”. ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ  ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

const Middleware = () => {
   const _middlewares = [];
   const _req, _res

  const run = (req, res) => {
    _req = req;
    _res = res;

    _run(0);
  }
}

๋ชจ๋“  ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์—์„œ req, res ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ํด๋กœ์ ธ ๋ณ€์ˆ˜ _req, _res๋กœ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด ๋‹ค์Œ์— ๋งŒ๋“ค _run(0) ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ธ์ž๊ฐ’์€ 0๋ฒˆ(๋ฐฐ์—ด์˜ ์ฒซ๋ฒˆ์งธ) ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋Š” ์˜๋„ ์ž…๋‹ˆ๋‹ค.

  const _run = i => {
    if (i < 0 || i >= _middlewares.length) return;

    const nextMw = _middlewares[i]
    const next = () => _run(i + 1)

    nextMw(_req, _res, next);
  }

์ธ๋ฑ์Šค ๊ฐ’์ด ๋ฐฐ์—ด ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚  ๊ฒฝ์šฐ ๋ฐ”๋กœ ํ•จ์ˆ˜๋ฅผ ์ข…๋ฃŒํ•˜๋Š” ๋ฐฉ์–ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์‹คํ–‰ํ•  ๋ฏธ๋“ค์›จ์–ด๋ฅผ _middlewares ๋ฐฐ์—ด์—์„œ ์ฐพ์•„ nextMw์— ์ €์žฅํ•ด ๋‘ก๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋Š” req, res ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ํ•จ์ˆ˜์ธ(์„ฑํฌ, thunk) next๋ฅผ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋„ ๋งŒ๋“ค์–ด next์— ๋‹ด์•„ ๋‘ก๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ nextMw์— _req, _res, next๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค๋ณผ๊นŒ์š”?

$ npm test 

  Middleware
    โœ“ ์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹ค
    add()
      โœ“ ๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค
    run()
      โœ“ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
      โœ“ next๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜ ์ฒด์ธ์„ ์ฆ‰์‹œ ์ค‘์ง€ํ•œ๋‹ค
      1) ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹ค

์„ธ ๋ฒˆ์งธ์™€ ๋„ค ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊นŒ์ง€ ๋ชจ๋‘ ํ†ต๊ณผํ–ˆ๋„ค์š”.โœ… ์ข‹์Šต๋‹ˆ๋‹ค. ์ผ์„์ด์กฐ์—์š”. ๐Ÿ‘

๐Ÿค์‹ค์Šต - 5๋ฒˆ ์š”๊ตฌ์‚ฌํ•ญ ๊ตฌํ˜„

โ€œrun() ๋ฉ”์†Œ๋“œ๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹คโ€๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”.

ํžŒํŠธ: ์ต์Šคํ”„๋ ˆ์Šค ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋Š” ์ธ์ž ๊ฐฏ์ˆ˜๋กœ ๊ตฌ๋ณ„ํ•จ, ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋กœ ์—๋Ÿฌ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ๋Š” next(err) ํ˜ธ์ถœ

๐Ÿคํ’€์ด

๊ทธ๋Ÿผ ๊ฐ™์ด ํ’€์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ ์„œ ์„ค๋ช…ํ• ๊ป˜์š”

    const _run = (i, err) => {
      if (i < 0 || i >= _middlewares.length) return;

      const nextMw = _middlewares[i]
      const next = err => _run(i + 1, err)

next ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์— ๋นˆ ์ธ์ž๋ฅผ err๋กœ ์ฑ„์›Œ ๋„ฃ์Šต๋‹ˆ๋‹ค. ์ฆ‰ ์—๋Ÿฌ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ›๊ฒ ๋‹ค๋Š” ๊ฒƒ์ด์ฃ .

      if (err) {
        const isNextErrorMw = nextMw.length === 4

        return isNextErrorMw ?
          nextMw(err, _req, _res, next) :
          _run(i + 1, err)
     }

     nextMw(_req, _res, next);
   }

_run ๋ฉ”์†Œ๋“œ์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž์ธ ์—๋Ÿฌ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ํ˜„์žฌ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด์ธ์ง€ ์ธ์ž ๊ธธ์ด๋กœ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค. ๊ธธ์ด๊ฐ€ 4์ด๋ฉด ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด์ธ ์…ˆ์ด์ฃ .

๊ทธ๋ž˜์„œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด์ด๋ฉด(isNextErrorMw) ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ธฐ๋ณธ ๋ฏธ๋“ค์›จ๋ผ๋ผ๋ฉด ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ฐพ๋„๋ก _run(i + 1, err)๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ๊ฐ€ ์ข€ ์–ด๋ ค์›Œ ๋ณด์ผ์ง€๋„ ๋ชจ๋ฅด๊ฒ ๋„ค์š”. ์ดํ•ด๊ฐ€ ์•ˆ๋œ๋‹ค๋ฉด ์ฐจ๊ทผ์ฐจ๊ทผ ์—ฌ๋Ÿฌ๋ฒˆ ์ฝ์–ด ๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์ €์žฅํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค๋ณด์ฃ .

$ npm test 

  Middleware
    โœ“ ์ดˆ๊ธฐ ๋ฏธ๋“ค์›จ์–ด ๊ฐฏ์ˆ˜๋Š” 0๊ฐœ์ด๋‹ค
    add()
      โœ“ ๋ฐฐ์—ด์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค
    run()
      โœ“ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
      โœ“ next๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜ ์ฒด์ธ์„ ์ฆ‰์‹œ ์ค‘์ง€ํ•œ๋‹ค
      โœ“ ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ์—๋Ÿฌ ๋ฏธ๋“ค์›จ์–ด๋งŒ ์‹คํ–‰ํ•œ๋‹ค

๋“œ๋””์–ด ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ํ†ต๊ณผ ํ–ˆ์Šต๋‹ˆ๋‹ค!โœ…

์ •๋ฆฌ

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

๋ชฉ์ฐจ ๋ฐ”๋กœ๊ฐ€๊ธฐ