์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ Express๋ฅผ ์˜ค๋žœ ๊ธฐ๊ฐ„ ์‚ฌ์šฉํ–ˆ์—ˆ๋Š”๋ฐ hapi ๊ฐ€ ์ข‹๋‹ค๋Š” ์–˜๊ธฐ๋ฅผ ๋“ฃ๊ณ ๋Š” hapi๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด์™”๋‹ค. Hapi๋„ ๋‹จ์ˆœํ•˜๊ธด ํ•˜์ง€๋งŒ โ€œ์„ค์ •๋งŒ ๋„ฃ์œผ๋ฉด ๋˜๋Š”โ€ ๋‹จ์ˆœํ•จ์ด๋ผ์„œ ์„ค์ •์— ๋“ค์–ด๊ฐ€๋Š” ์ˆ˜๊ณ ๊ฐ€ ๊ฝค ์ปธ๋‹ค. ์ตœ๊ทผ์—๋Š” ํ† ์ด ํ”„๋กœ์ ํŠธ์—์„œ API๋ฅผ ์ž‘์„ฑํ•˜๋Š”๋ฐ ์—๋Ÿฌ ๋ฐœ์ƒ ์—ฌ๋ถ€์— ๋”ฐ๋ผ์„œ {"ok": true} ํ•˜๋‚˜ ๋„ฃ์–ด์ฃผ๋Š” ์ž‘์—…์— ์˜ค๋งŒ๊ฐ€์ง€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ–ˆ๋‹ค. express์™€ ๋‹ค๋ฅด๊ฒŒ ๋ฏธ๋“ค์›จ์–ด์—์„œ request, response์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํฌ์ธํŠธ๊ฐ€ ์›Œ๋‚™์— ๋งŽ์•„ ๋” ๋ณต์žกํ•˜๊ฒŒ ๋А๊ปด์กŒ๋‹ค. ๊ทธ๋Ÿฌ๋˜ ์ค‘ ์˜ˆ์ „์— ์ž ์‹œ ๋น„๊ต๊ธ€๋กœ ๋ดค๋˜ koa๋ฅผ ์‚ดํŽด๋ดค๋Š”๋ฐ ์ง€๊ธˆ ํ•„์š”ํ•œ ์ƒํ™ฉ์— ๋งž๋Š” ๊ฒƒ ๊ฐ™์•„ koa๋กœ ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๊ณ  ๋งˆ์Œ์— ๋“œ๋Š” ๊ตฌ์„์ด ๋งŽ์•„์„œ ๊ฐ„๋‹จํ•œ ์†Œ๊ฐœ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

Koa๋Š” ES2015์˜ ๋ฌธ๋ฒ• ์ค‘ ํ•˜๋‚˜์ธ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•˜๊ณ  ์žˆ๋Š” ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค. ๋ชจ๋“  ์š”์ฒญ๊ณผ ์ฒ˜๋ฆฌ๋ฅผ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•ด ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ํŠน์ง•์ด๋ฉฐ ๊ทธ ๋•๋ถ„์— ๊น”๋”ํ•œ async ์ฝ”๋“œ๋ฅผ ์†์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. Express ๋งŒํผ์€ ์•„๋‹ˆ๋”๋ผ๋„ ๋‹ค์–‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ณ , express์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ๋ฏธ๋“ค์›จ์–ด๋„ thenify๋‚˜ co๋กœ ๋ณ€ํ™˜ํ•ด์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์„ ๋งŒํผ ํ™•์žฅ์„ฑ์ด ๋†’๋‹ค.

์ด ํฌ์ŠคํŠธ๋Š” ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณด๊ณ , ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” co๋ฅผ ์‚ดํŽด๋ณธ ํ›„, KoaJS๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์œผ๋กœ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค.


์ œ๋„ˆ๋ ˆ์ดํ„ฐ Generator

๋‹ค๋ฅธ ์–ธ์–ด์—๋„ ์ด๋ฏธ ์กด์žฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํฌ๊ฒŒ ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์€ ์•„๋‹ˆ์ง€๋งŒ ES6์—์„œ์˜ ๊ตฌํ˜„์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

์ผ๋ฐ˜์ ์ธ ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ, ๋งค ์‹คํ–‰๋งˆ๋‹ค ๊ฐ™์€ ํ๋ฆ„์œผ๋กœ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€๋งŒ Generator ํ•จ์ˆ˜๋Š” ์‹คํ–‰ ์ค‘๊ฐ„์—์„œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ณ , ๋‹ค๋ฅธ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ ํ›„์— ๋‹ค์‹œ ๊ทธ ์œ„์น˜์—์„œ ์ฝ”๋“œ๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋Š” ๋ฐ˜๋ณต ํ•จ์ˆ˜ iterator๋ฅผ next()๋กœ ์ œ๊ณตํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ value๋กœ, ์ง„ํ–‰ ์ƒํ™ฉ์„ done์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ตฌ๊ตฌ๋‹จ์„ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

function* nTimesTable(n) {
  for(var i = 1; i <= 9; i++) yield n * i;
}

์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋Š” ์œ„์™€ ๊ฐ™์ด function* fnName(){} ์‹์œผ๋กœ ์„ ๋„ฃ์–ด ์„ ์–ธํ•œ๋‹ค. ์ต๋ช… ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ๋„ `function(){}` ์‹์œผ๋กœ ์„ ์–ธํ•œ๋‹ค.

์ด์ œ ์ดํ„ฐ๋ ˆ์ดํ„ฐ(iterator)๋ฅผ nineTimesTable์— ๋ฐ˜ํ™˜ ๋ฐ›๋Š”๋‹ค.

var nineTimesTable = nTimesTable(9);

์ดํ„ฐ๋ ˆ์ดํ„ฐ๋Š” next()๋ฅผ ํ†ตํ•ด ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ํ•จ์ˆ˜๋กœ ์ค‘๋‹จํ•œ ์œ„์น˜์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

var result = nineTimesTable.next();
console.log(result); // { value: 9, done: false }
result = nineTimesTable.next();
console.log(result); // { value: 18, done: false }
result = nineTimesTable.next();
console.log(result); // { value: 27, done: false }

// keep calling...

result = nineTimesTable.next();
console.log(result); // { value: 72, done: false }
result = nineTimesTable.next();
console.log(result); // { value: 81, done: false }
result = nineTimesTable.next();
console.log(result); // { value: undefined, done: true }

๋งค ๋ฐ˜๋ณต ์‹คํ–‰์—์„œ value๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ ๋™์‹œ์— done์œผ๋กœ ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ yield ๊ฒฐ๊ณผ ์—†์ด ์ข…๋ฃŒ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งˆ์ง€๋ง‰์— ๋ณ„๋„์˜ return ๊ฐ’์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— value๊ฐ€ undefined๊ฐ€ ๋œ๋‹ค.

์ด๋Ÿฐ ์ดํ„ฐ๋ ˆ์ดํ„ฐ์˜ ๋ฐ˜ํ™˜ ํŠน์ง•์„ ์ด์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด iterator๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

function caller(iter) {
  var result, value;
  while(result = iter.next()) {
    if(result.done) break;
    value = result.value || value;
  }
  return value;
}

var result = caller(nTimesTable(3));
console.log(result); // 27

done์ด true๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ๊นŒ์ง€ ํ•ด๋‹น ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ์‹คํ–‰ํ•ด ๊ฒฐ๊ณผ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” caller๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค. ๋งŒ์•ฝ ๋งค ๋ฐ˜๋ณต์—์„œ ํŠน์ • ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์•ž์„œ ์ž‘์„ฑํ•œ nTimesTable ํ•จ์ˆ˜๊ฐ€ ๋” ๋งŽ์€ ๋‚ด์šฉ์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ๋‹ค.

function * nTimesTable(n) {
  for(var i = 1; i <= 9; i++) yield { n: n, i: i, result: n * i };
}

function caller(iter, func) {
  var result, value;
  while(result = iter.next()) {
    if(result.done) break;
    value = result.value || value;
    if(func) func(value);
  }
  return value;
}

caller(nTimesTable(3), value => {
  console.log('%d x %d = %d', value.n, value.i, value.result);
});

์•ž์„œ ์ž‘์„ฑํ•œ caller๋Š” ์ œ๋„ˆ๋ ˆ์ดํ„ฐ ๋‚ด์˜ yield์— ๋Œ€ํ•ด์„œ๋Š” ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์ œ๋„ˆ๋ ˆ์ดํ„ฐ์—์„œ ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ง„ํ–‰์„ ์ค‘๋‹จํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ด์„œ ๋‹ค์‹œ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ๊ฒฐ๊ณผ๋ฅผ ๋„ฃ๊ณ  ๋‹ค์‹œ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

function* getAnimalInCage() {
  yield "Wombat";
  yield "Koala";
  return "Kangaroo";
}

function* Cage() {
  var cageAnimals = getAnimalInCage();

  var first = yield cageAnimals;
  var second = yield cageAnimals;
  var third = yield cageAnimals;

  console.log(first, second, third);
}

์ด Cage ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด yield๋ฅผ 3๋ฒˆ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์ข… console.log๊ฐ€ ์ถœ๋ ฅํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ธฐ๊นŒ์ง€ 4๋ฒˆ์— ๊ฑธ์ณ ์‹คํ–‰๋œ๋‹ค.

var cage = Cage();
var firstStop = cage.next();
// {value: iterator, done: false}

์ฒซ ๋ฒˆ์งธ yield ๊ฒฐ๊ณผ๊ฐ€ firstStop์— ์ €์žฅ๋˜์—ˆ๋‹ค. cageAnimals๋Š” ์œ„์—์„œ ์ฝ”๋“œ์—์„œ์™€ ๊ฐ™์ด getAnimalInCage ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑํ•œ ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋‹ค. ์ด ์ดํ„ฐ๋ ˆ์ดํ„ฐ์— next() ๋ฉ”์†Œ๋“œ๋กœ ๊ฐ’์„ ๋ฐ›์€ ํ›„, ๊ทธ ๊ฐ’์„ ๋‹ค์‹œ first ๋ณ€์ˆ˜์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.

var firstAnimal = firstStop.value.next();
// firstAnimal: {value: "Wombat", done: false}
var secondStop = cage.next(firstAnimal.value);

next์˜ ์ธ์ž๊ฐ’์œผ๋กœ ์ฒซ ๊ฒฐ๊ณผ์ธ Wombat์„ ๋„ฃ์—ˆ๋‹ค. ์ด์ „์— ๋ฉˆ์ท„๋˜ ์œ„์น˜์ธ ์ฒซ ๋ฒˆ์งธ yield๋กœ ๋Œ์•„๊ฐ€ ํ•จ์ˆ˜ ๋‚ด first์—๋Š” Wombat์ด ์ €์žฅ๋œ๋‹ค. ๋‚˜๋จธ์ง€๋„ ๋™์ผํ•˜๊ฒŒ ์ง„ํ–‰๋œ๋‹ค.

var secondAnimal = secondStop.value.next();
// secondAnimal: { value: 'Koala', done: false }

var thirdStop = cage.next(secondAnimal.value);
var thirdAnimal = thirdStop.value.next();
// thirdAnimal: { value: 'Kangaroo', done: true }

var lastStop = cage.next(thirdAnimal.value);

// Wombat Koala Kangaroo

๋งˆ์ง€๋ง‰ Kangaroo๋Š” yield๊ฐ€ ์•„๋‹Œ return์ด๊ธฐ ๋•Œ๋ฌธ์— done์ด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์•ž์„œ ์ง์ ‘ ํ˜ธ์ถœํ•ด์„œ ํ™•์ธํ•œ ์ฝ”๋“œ๋Š” ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์ด๋‚˜ ํ˜ธ์ถœํ•˜๋Š” ํ˜•ํƒœ๊ฐ€ ์ผ์ •ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ๋‹ค.

๋‹ค์Œ์€ catchEscapedAnimal()๊ณผ getTodaysZookeeper() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•œ Zoo ์ œ๋„ˆ๋ ˆ์ดํ„ฐ ์˜ˆ์‹œ๋‹ค.

function catchEscapedAnimal() {
  return function(done) {
    setTimeout(function() {
      done(null, {name: 'Kuma', type: 'Bear'});
    }, 1000);
  };
}

function* getTodaysZookeeper() {
  yield {status: 'loading'};
  return {status: 'loaded', name: 'Edward'};
}

function* Zoo() {
  var animal = yield catchEscapedAnimal();
  var zookeeper = yield getTodaysZookeeper();

  console.log('%s catches by %s', animal.name, zookeeper.name);
}

catchEscapedAnimal()์€ ajax๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๊ฐ€์ •ํ•ด์„œ setTimeout์„ ์ด์šฉํ•ด ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๋Š” ํ˜•ํƒœ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค. getTodaysZookeeper()๋Š” ์ผ๋ฐ˜์ ์ธ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ ํ•จ์ˆ˜๋กœ ์ฒซ ํ˜ธ์ถœ์—๋Š” loading์„, ๋‘๋ฒˆ์งธ ํ˜ธ์ถœ์—์„œ ์ตœ์ข… ๊ฐ’์„ ์ „์†กํ•œ๋‹ค. Zoo๋„ ์•ž์—์„œ ๋ณธ Cage์ฒ˜๋Ÿผ, ์ค‘๊ฐ„์— yield๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ compose ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

function compose(iter, value, next) {
  var result = iter.next(value);
  if(result.done) return next ? next(value) : value;
  else if(typeof result.value == 'function') {
    return result.value(function(err, data) {
      if(err) throw err;
      compose(iter, data);
    });
  } else if(typeof result.value.next == 'function') {
    var _iter = iter;
    next = function(result){
      compose(_iter, result);
    };
    iter = result.value;
    result = iter.next();
  }
  return compose(iter, result.value, next);
}

์ด compose ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ๋‹ค๋ฃฌ๋‹ค.

  • yield ๋œ ๊ฐ’์ด ํ•จ์ˆ˜์ผ ๋•Œ, ํ˜ธ์ถœ ์ฒด์ธ์„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก next ํ•จ์ˆ˜๋ฅผ ๋„˜๊ฒจ์คŒ (๊ธฐ์กด callback ๋ฐฉ์‹)
  • yield ๋œ ๊ฐ’์ด ์ดํ„ฐ๋ ˆ์ดํ„ฐ์ผ ๋•Œ, ์ดํ„ฐ๋ ˆ์ดํ„ฐ๊ฐ€ done์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ๊นŒ์ง€ ํ˜ธ์ถœํ•œ ํ›„ ์ตœ์ข… ๊ฐ’์„ ๋ฐ˜ํ™˜
  • ๊ทธ ์™ธ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ, ํ•ด๋‹น ๊ฐ’์„ ์ดํ„ฐ๋ ˆ์ดํ„ฐ์— ๋„ฃ๊ณ  ๋‹ค์‹œ compose๋ฅผ ํ˜ธ์ถœ
  • ์ดํ„ฐ๋ ˆ์ดํ„ฐ๊ฐ€ ์ข…๋ฃŒ(done == true)๋˜์—ˆ์„ ๋•Œ, next ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœ์„ ์ง„ํ–‰ํ•˜๊ณ  ์—†์œผ๋ฉด ์ตœ์ข… ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ข…๋ฃŒ

์ด ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•œ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. setTimeout()์— ์˜ํ•ด ์ค‘๊ฐ„ ์ง€์—ฐ์ด ์ง„ํ–‰๋˜๋Š” ๋ถ€๋ถ„๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

compose(Zoo());
// Kuma catches by Edward

์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์ฝ”๋ฃจํ‹ด์œผ๋กœ, co

๋‚˜๋ฆ„ ์ž˜ ๋™์ž‘ํ•˜์ง€๋งŒ ํ๋ฆ„์„ ๋ณด๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“  ํ•จ์ˆ˜๋ผ์„œ ํ—ˆ์ˆ ํ•œ ๋ถ€๋ถ„์ด ๋งŽ๋‹ค. ์ด๋Ÿฐ ๋ถ€๋ถ„์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ co๋‹ค. co๋Š” ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์ฝ”๋ฃจํ‹ด์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์•ž์„œ ์ž‘์„ฑํ–ˆ๋˜ compose ํ•จ์ˆ˜์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•œ๋‹ค.

var co = require('co');
co(Zoo());
// Kuma catches by Edward

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ Promise ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์„œ callback์ด๋“  Promise๋“  ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋“  ๋ชจ๋‘ ์ž˜ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์‹ค์ œ๋กœ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํฐ ๋„์›€์ด ๋œ๋‹ค.

Koa

Koa๋Š” ์•ž์„œ ์ด์•ผ๊ธฐํ•œ co ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ ์šฉํ•˜๊ณ  ์žˆ๋Š” HTTP ๋ฏธ๋“ค์›จ์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ฒฝ๋Ÿ‰์— ๊ฐ„๋‹จํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ํŠน์ง•์œผ๋กœ ํ•œ๋‹ค. ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ ์•ž์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์†์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ์— ์•ž์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ koa๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

$ npm install --save koa

Hello World๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

var koa = require('koa');
var app = koa();

app.use(function* () {
  this.body = {"message": "Hello World"};
});

app.listen(3000);

์ด์ œ http://localhost:3000์— ์ ‘์†ํ•˜๋ฉด ํ•ด๋‹น json์ด ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์•ž์„œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋„ ํฌํ•จํ•ด๋ณด์ž.

var koa = require('koa');
var app = koa();

function catchEscapedAnimal() {
  return function(done) {
    setTimeout(function() {
      done(null, {name: 'Kuma', type: 'Bear'});
    }, 50);
  };
}

function* getTodaysZookeeper() {
  yield {status: 'loading'};
  return {status: 'loaded', name: 'Edward'};
}

function* Zoo() {
  var animal = yield catchEscapedAnimal();
  var zookeeper = yield getTodaysZookeeper();

  this.body = { message: animal.name + ' catches by ' + zookeeper.name };
}

app.use(Zoo);
app.listen(3000);

Koa์˜ ๋ชจ๋“  ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์€ ๋ฏธ๋“ค์›จ์–ด ๊ตฌ์กฐ๋กœ ์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์ž‘์„ฑํ•˜๊ฒŒ ๋œ๋‹ค. callback์€ ๋ฌผ๋ก  Promise ํŒจํ„ด๋„ ๋” ๊น”๋”ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์š”์ฒญ๊ณผ ์‘๋‹ต์€ ๋ชจ๋‘ this์— ์ฃผ์ž…๋˜์„œ ์ „๋‹ฌ๋˜๊ณ  ํ๋ฆ„์€ ์ฒซ ์ธ์ž์— next๋ฅผ ์ถ”๊ฐ€ํ•ด ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค. ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ๋‚ด์šฉ์ด ์žˆ์œผ๋ฉด ok๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

app.use(function* (next) {
  yield next;
  if(this.body) {
    this.body.ok = true;
  } else {
    this.body = { ok : false };
  }
});

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ† ํฐ ๊ฒ€์ฆ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

app.use(function* (next) {
  var requestToken = this.request.get("Authorization");
  var accessToken = yield AccessTokensModel.findAccessTokenAsync(token);
  if(accessToken) {
    yield next;
  } else {
    this.body = { error: 'invalid_token' };
  }
});

์„ธ๋ถ€์ ์ธ ๋‚ด์šฉ์€ koa ์›นํŽ˜์ด์ง€์—์„œ ๋‹ค๋ฃจ๊ณ  ์žˆ๋‹ค. ๋‹จ์ˆœํ•˜๊ณ  ๊ฐ„ํŽธํ•œ ๊ธฐ๋Šฅ์„ ์›ํ•œ๋‹ค๋ฉด ๊ผญ ์‚ดํŽด๋ณด์ž. ์‹ค์ œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๋•Œ๋Š” koa-bodyparser, koa-router์™€ ๊ฐ™์€ ํŒจํ‚ค์ง€๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค. ํŒจํ‚ค์ง€ ๋ชฉ๋ก์€ koa ์œ„ํ‚ค์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ œ๋„ˆ๋ ˆ์ดํ„ฐ๋„ ์ถฉ๋ถ„ํžˆ ํŽธํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ koa๋Š” ํ˜„์žฌ await/async ๋ฌธ๋ฒ•์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์Œ ๋ฒ„์ „ ๊ฐœ๋ฐœ์ด ์ง„ํ–‰๋˜๊ณ  ์žˆ๋‹ค. ๋” ๊ฐ€๋…์„ฑ๋„ ๋†’๊ณ  ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๋ฌธ๋ฒ•์ด๋ผ ๋” ๊ธฐ๋Œ€๋œ๋‹ค.


๋” ์ฝ์„ ๊ฑฐ๋ฆฌ