Generator in Practice - [2๋ถ€] ์‹ค๋ฌด ์˜ˆ์ œ


generator-in-practice

์š”๊ตฌ์‚ฌํ•ญ๊ณผ API๋ชฉ๋ก

ํ† ์ŠคํŠธ ๋“œ๋ผ์ด๋ธŒ ํ”„๋กœ์ ํŠธ์—์„œ ์‹ค์ œ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ๋˜ ๋ช…์„ธ ์ค‘ Generator ์ ์šฉ ํšจ์œจ์ด ์ œ์ผ ๋†’์•˜๋˜ ๋ช…์„ธ๋ฅผ ์กฐ๊ธˆ ์ˆ˜์ •ํ–ˆ๋‹ค. ํŒŒ์ผ ๋˜๋Š” ํด๋”๋ฅผ ์„ ํƒํ•œ ํ›„ ๋‹ค๋ฅธ ํด๋”๋กœ ์ด๋™ํ•  ๋•Œ์˜ ๋ช…์„ธ๋‹ค.

๋‹ค์Œ์€ ์›น ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ตฌํ˜„ ํ”„๋กœ์ ํŠธ์˜ ์ผ๋ถ€ ๊ธฐ๋Šฅ์ด๋‹ค.

ํŒŒ์ผ ๋ชฉ๋ก์—์„œ ์„ ํƒํ•œ ๋‹ค์ˆ˜์˜ ํŒŒ์ผ ๋ฐ ํด๋”๋ฅผ ๋‹ค๋ฅธ ํด๋”๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๋•Œ, ์ด๋™ ๋Œ€์ƒ ํด๋”์— ์ด๋ฆ„์ด ๊ฐ™์€ ํŒŒ์ผ ๋ฐ ํด๋”๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•  ๊ฒฝ์šฐ ๋ชจ๋“  ๊ฑด์— ๋Œ€ํ•ด์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ™•์ธํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด โ€˜์ƒˆ ํด๋”โ€™๋ผ๋Š” ์ด๋ฆ„์ด ์ค‘๋ณต๋  ๊ฒฝ์šฐ, โ€˜์ด๋™ํ•˜๋ ค๋Š” ํด๋”์— ์ด๋ฆ„์ด ์ค‘๋ณต๋œ ํด๋”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
[์ƒˆ ํด๋”(1)]์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝ ํ›„ ์ด๋™ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?โ€™๋ผ๋Š” ๋ฌธ๊ตฌ์˜ ํ™•์ธ์šฉ ๋””์ž์ธ ๋ ˆ์ด์–ด๋ฅผ ๋„์šด๋‹ค.

'ํ™•์ธ'์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์ด๋ฆ„์œผ๋กœ ์ด๋™์‹œํ‚จ๋‹ค. ์ด๋•Œ '์ดํ›„์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ'
์ฒดํฌ๋ฐ•์Šค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ดํ›„ ์ค‘๋ณต ๊ฑด์€ ๋ชจ๋‘ 'ํ™•์ธ' ์ฒ˜๋ฆฌํ•œ๋‹ค.

'์ทจ์†Œ'๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ทธ ํŒŒ์ผ์— ๋Œ€ํ•ด ์ทจ์†Œํ•œ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ '์ดํ›„์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ'
์ฒดํฌ๋ฐ•์Šค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ดํ›„ ์ค‘๋ณต ๊ฑด์„ ๋ชจ๋‘ '์ทจ์†Œ' ์ฒ˜๋ฆฌํ•œ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋“  ์ค‘๋ณต ๊ฑด์— ๋Œ€ํ•ด ํ™•์ธํ•˜๋ฉด ๋ชจ๋“  ๊ฑด์— ๋Œ€ํ•ด ์ด๋™ ์ฒ˜๋ฆฌํ•œ๋‹ค.

์„œ๋ฒ„์—์„œ ์ œ๊ณตํ•˜๋Š” API ๋ชฉ๋ก

GET  '/api/check-duplication'
[์ด๋ฆ„ ์ค‘๋ณต ํ™•์ธ]
 - ํŠน์ • ํด๋”์˜ id์™€ ์ƒ์„ฑํ•  ์ด๋ฆ„์„ ์ „๋‹ฌ
 - ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ƒˆ ์ด๋ฆ„๊ณผ ํ•จ๊ป˜ 409 ์‘๋‹ต
 - ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ 200 ์‘๋‹ต

POST '/api/move'
[ํŒŒ์ผ ๋ฐ ํด๋” ์ด๋™]
 - ์ด๋™ ๋Œ€์ƒ ํŒŒ์ผ ๋ฐ ํด๋”์˜ id ๋ชฉ๋ก๊ณผ ์ด๋™ ๋Œ€์ƒ ํด๋”์˜ id ์ „๋‹ฌ
 - ์˜ค๋ฅ˜๊ฐ€ ์—†์œผ๋ฉด ๋ชจ๋“  ์ด๋™์„ ์ฒ˜๋ฆฌ ํ›„ 200 ์‘๋‹ต

ํŒŒ์ผ ๋ฐ ํด๋” ์ด๋™ API์—์„œ ์ค‘๋ณต์„ ํ•จ๊ป˜ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ด์ง€๋งŒ, ์ด ๊ธ€์—์„œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ์•„๋‹ˆ๋ฏ€๋กœ ๋‹น์žฅ์€ ๋‘ ๊ฐœ์˜ API๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.

๋˜ ์‹ค์ œ API๋Š” ์ด๋™์˜ ๊ฒฐ๊ณผ๋ฅผ ์›น ์†Œ์ผ“์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์•Œ๋ฆฌ์ง€๋งŒ. ์„ค๋ช…์„ ์‰ฝ๊ฒŒ ํ•˜๋ ค๊ณ  ๋‹จ์ผ HTTP ์š”์ฒญ, ์‘๋‹ต์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•œ๋‹ค.

๋ถ„์„

๋จผ์ € ํ˜„์žฌ ์„ ํƒ๋œ ํŒŒ์ผ๊ณผ ํด๋”(์ดํ•˜ '๊ฐœ์ฒด' ๋ผ ํ•จ)์˜ ์ด๋ฆ„๊ณผ ์ด๋™ ๋Œ€์ƒ ํด๋”์˜ id๋ฅผ ์กฐํšŒํ•ด [์ด๋ฆ„ ์ค‘๋ณต ํ™•์ธ] API๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค. 200 ์‘๋‹ต์„ ๋ฐ›์œผ๋ฉด ๋ฐ”๋กœ [ํŒŒ์ผ ๋ฐ ํด๋” ์ด๋™] API๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋˜์ง€๋งŒ ์ค‘๋ณต ์˜ค๋ฅ˜๊ฐ€ ์‘๋‹ตํ•  ๊ฒฝ์šฐ ๋ชจ๋“  ์ค‘๋ณต์— ๋Œ€ํ•ด ์‚ฌ์šฉ์ž ์‘๋‹ต์„ ์ˆ˜์ง‘ํ•ด์•ผ ํ•œ๋‹ค.

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

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

์ •๋ฆฌํ•˜๋ฉด '์ด๋ฆ„ ์ค‘๋ณต ํ™•์ธ', '์ค‘๋ณต๋˜๋Š” ๋ชจ๋“  ํ•ญ๋ชฉ์˜ ์‚ฌ์šฉ์ž ์‘๋‹ต ์ˆ˜์ง‘', '๊ฐœ์ฒด ์ด๋™'์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค.

Callback

์š”๊ตฌ์‚ฌํ•ญ์˜ '๋””์ž์ธ ๋ ˆ์ด์–ด'๋ฅผ ์ฝœ๋ฐฑ ํŒจํ„ด์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. jsConfirm์€ ์›๋ž˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„์„œ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•˜์ง€๋งŒ. ์ค‘์š”ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์ฒ˜๋ฆฌํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

<div id="layer">
  <input type="checkbox" id="checkbox" />
  <button type="button" id="ok">ok</button>
  <button type="button" id="cancel">cancel</button>
</div>

<script>
  const layer = $("#layer");
  const checkbox = $("#checkbox");

  /**
   * @param {string} msg - confirm ์— ์ถœ๋ ฅํ•  ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
   * @param {function} cb - callback ํ•จ์ˆ˜
   */
  function jsConfirm(msg, cb = () => {}) {
    checkbox.prop("checked", false);

    layer.show().on("click", ev => {
      const target = $(ev.target);
      const checked = checkbox.prop("checked");

      if (target.is("#ok")) {
        layer.hide().off();
        cb({ confirmed: true, checked });
      } else if (target.is("#cancel")) {
        layer.hide().off();
        cb({ confirmed: false, checked });
      }
    });
  }
</script>

๊ทธ๋‹ค์Œ์€ ์ค‘๋ณต ๊ฑด์— ๋Œ€ํ•ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ํ™•์ธ์„ ์Œ“์•„์•ผ ํ•œ๋‹ค. ๋จผ์ € ์ด๋ฆ„ ์ค‘๋ณต ๋ฐ์ดํ„ฐ์˜ Mock์„ ์ค€๋น„ํ•œ๋‹ค. ์ด ๊ฐ์ฒด๋Š” [์ค‘๋ณต ํ™•์ธ] API์˜ ์‘๋‹ต์ด๋ผ๊ณ  ๊ฐ€์ •ํ•  ๊ฐ์ฒด์ด๋‹ค.

// [์ด๋ฆ„ ์ค‘๋ณต ํ™•์ธ] API์˜ ์‘๋‹ต ๋ฐ์ดํ„ฐ (์š”์ฒญํ•œ ์ด๋ฆ„, ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ์ด๋ฆ„ ์Œ)
const dupList = [["foo", "foo(2)"], ["bar", "bar(2)"], ["baz", "baz(2)"]];

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

// ์ด ๋ณ€์ˆ˜์— ํ™•์ธ ํ•ญ๋ชฉ์„ ๋ชจ์€๋‹ค
let resolved = [];

function resolveDuplicates(idx) {
  const item = dupList[idx];

  jsConfirm(`msg${idx}`, ({ confirmed, checked }) => {
    if (confirmed) {
      // A 'ํ™•์ธ'
      resolved.push(item);
    }

    idx += 1;

    if (checked) {
      if (confirmed) {
        // B '์ดํ›„ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ', 'ํ™•์ธ'
        resolved = [...resolved, ...dupList.slice(idx)];
      } else {
        // C '์ดํ›„ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ', '์ทจ์†Œ'
      }
      return;
    }

    if (!dupList[idx]) {
      // D ๋ชจ๋“  ํ•ญ๋ชฉ ์™„๋ฃŒ
      return;
    }

    resolveDuplicates(idx);
  });
}
resolveDuplicates(0);

์•„์ง API ํ˜ธ์ถœ ์ฒ˜๋ฆฌ๋Š” ์‹œ์ž‘์กฐ์ฐจ ํ•˜์ง€ ์•Š์•˜๋‹ค. [์ค‘๋ณต ํ™•์ธ] API๋ฅผ ๋จผ์ € ํ˜ธ์ถœํ•œ ํ›„์˜ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ resolveDuplicates๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์œ„์˜ ์ฝ”๋“œ๋Š” ์ด๋ฏธ depth๊ฐ€ ์ฆ๊ฐ€ํ•œ ์ƒํƒœ์ผ ๊ฒƒ์ด๋‹ค.

๋˜ ์ฝ”๋“œ์˜ A, B, C, D ๋ถ€๋ถ„์—์„œ๋„ API ํ˜ธ์ถœ์„ ์œ„ํ•œ ์ฝœ๋ฐฑ์ด ์ถ”๊ฐ€๋  ๊ฒƒ์ด๋‹ค. ํ•จ์ˆ˜๋ฅผ ์ถ”์ถœํ•˜๋Š” ๋ฐฉ๋ฒ• ๋“ฑ์˜ ๋ฆฌํŒฉํ† ๋ง์„ ํ•œ๋‹ค๊ณ  ํ•ด๋„ ์ฝœ๋ฐฑ ํŒจํ„ด์œผ๋กœ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ๋งŒ๋“ค์–ด์ง€๋Š” ์ฝ”๋“œ๋Š” ์•Œ์•„๋ณด๊ธฐ ์–ด๋ ต๊ณ  ์œ ์ง€ ๋ณด์ˆ˜ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

Promise

์ฝœ๋ฐฑ ์ค‘์ฒฉ ๋ฌธ์ œ๋Š” ์˜ค๋ž˜์ „๋ถ€ํ„ฐ ๋“ฑ์žฅํ•œ Promise๋ฅผ ์ด์šฉํ•˜๋ฉด ์–ด๋Š ์ •๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. Promise๋ž€ ๋ฏธ๋ž˜์˜ ์–ด๋–ค ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด์ด๋‹ค. Kyle Simpson์€ ํ–„๋ฒ„๊ฑฐ๋ฅผ ์ฃผ๋ฌธํ•˜๊ณ  ๋ฐ›๋Š” ๋Œ€๊ธฐํ‘œ๋กœ ๋น„์œ ํ–ˆ๋Š”๋ฐ ์›ƒ๊ธฐ์ง€๋งŒ ์ •ํ™•ํ•œ ๋น„์œ ๋‹ค. Promise๋Š” ์–ด๋–ค ๊ฐ’ ํ•˜๋‚˜๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

'๋””์ž์ธ ๋ ˆ์ด์–ด'์˜ ํ™•์ธ, ์ทจ์†Œ๋„ ์ด Promise๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. '๋””์ž์ธ ๋ ˆ์ด์–ด'๊ฐ€ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌํ˜„ํ•ด ๋ณด์ž. ์ด ๊ณผ์—…์€ ์ด ๊ธ€์˜ ๋์— Generator๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ฐ‘๊ฑฐ๋ฆ„์ด๊ธฐ๋„ ํ•˜๋‹ค.

/**
 * @param {string} msg - confirm ์— ์ถœ๋ ฅํ•  ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
 * @returns {Promise}
 */
function jsConfirm(msg) {
  // Return Promise
  return new Promise((resolve, reject) => {
    checkbox.prop("checked", false);

    layer.show().on("click", ev => {
      const target = $(ev.target);
      const checked = checkbox.prop("checked");

      if (target.is("#ok")) {
        layer.hide().off();
        resolve({ confirmed: true, checked });
      } else if (target.is("#cancel")) {
        layer.hide().off();
        resolve({ confirmed: false, checked });
      }
    });
  });
}

jsConfirm์€ ์ด์ œ Promise ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์‚ฌ์šฉ์ž์˜ ์‘๋‹ต์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด resolve๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์žˆ๋‹ค. Promise์˜ ๊ธฐ๋ณธ ์ŠคํŽ™์— ๊ถ๊ธˆํ•œ ๋…์ž๋Š” ์•„๋ž˜ ๋งํฌ๋ฅผ ํ™•์ธ ๋ฐ”๋ž€๋‹ค.

์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ค‘๋ณตํ•ญ๋ชฉ ์ฒดํฌ๋ฅผ Promise๋กœ ๋ฆฌํŒฉํ† ๋ง ํ•ด ๋ณด์ž. ๋ชฉ๋ก์— ๋Œ€ํ•ด ์ˆœ์ฐจ์ ์œผ๋กœ jsConfirm์„ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋ฏ€๋กœ Promise๋ฅผ ์ด์–ด ๋ถ™์ด๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ๋ฐฐ์—ด์˜ ์š”์†Œ์— ๋Œ€ํ•ด์„œ Promise๋ฅผ ์ˆœ์ฐจ ์ฒ˜๋ฆฌํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค.

let resolved = [];

dupList
  .reduce((promise, item, idx) => {
    return promise.then(() => {
      return jsConfirm().then(({ confirmed, checked }) => {
        if (confirmed) {
          // A 'ํ™•์ธ'
          resolved.push(item);
        }

        idx += 1;

        if (checked) {
          if (confirmed) {
            // B '์ดํ›„ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ', 'ํ™•์ธ'
            resolved = [...resolved, ...dupList.slice(idx)];
          } else {
            // C '์ดํ›„ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ', '์ทจ์†Œ'
          }

          // Promise ์ฒด์ธ์„ ์™„์ „ํžˆ ํƒˆ์ถœํ•˜๊ธฐ ์œ„ํ•จ
          throw new Error("checked");
        }
      });
    });
  }, Promise.resolve())
  .then(() => {
    // D ๋ชจ๋“  ํ•ญ๋ชฉ ์™„๋ฃŒ
    console.log(resolved);
  });

์ „๋ณด๋‹ค ํ›จ์”ฌ ๋ณด๊ธฐ ์‰ฌ์›Œ์กŒ๋Š”๊ฐ€? ๊ฐœ์ธ์ ์œผ๋กœ ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง„ ์ ์€ ์—†์–ด ๋ณด์ธ๋‹ค. ์žฌ๊ท€ ์ฝ”๋“œ๊ฐ€ ์—†์–ด์ง€๊ณ  ๋ชจ๋“  ํ•ญ๋ชฉ์˜ ์™„๋ฃŒ ์ฒ˜๋ฆฌ๋ฅผ ๋งจ ๋งˆ์ง€๋ง‰ then ์ฒด์ธ์—์„œ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ๊ณผ, catch๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹จ ์  ์ •๋„์ด๋‹ค. ์ด catch๋Š” Promise ์ฒด์ธ์˜ ์–ด๋Š ๊ณณ์—์„œ๋ผ๋„ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ชจ์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž ์˜ค๋ฅ˜, XHR์˜ค๋ฅ˜ ๋“ฑ๋“ฑ์„ ํ•œ ๊ณณ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

์‚ฌ์‹ค ๊ทธ๋ฆฌ ์•Œ์•„๋ณด๊ธฐ ์‰ฌ์šด ํŽธ์€ ์•„๋‹ˆ๋‹ค. Promise ์ž์ฒด์— ์ดํ•ด๊ฐ€ ์—†๋Š” ๊ฐœ๋ฐœ์ž์—๊ฒŒ๋Š” ๋”๋”์šฑ ๊ทธ๋Ÿด ๊ฒƒ์ด๊ณ  ๋ฌด์—‡๋ณด๋‹ค๋„ ์•ž์„œ ์ด์•ผ๊ธฐํ–ˆ๋˜ '๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ๋™๊ธฐ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์“ธ ์ˆ˜ ์žˆ๋‹ค'๋ผ๋Š” ์ด์•ผ๊ธฐ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์—†๋‹ค.

Generator ๋ช…์„ธ๊ฐ€ ๋‚˜์˜ค๊ธฐ ์ „๊นŒ์ง€๋งŒ ํ•ด๋„ ์ด ๋ฐฉ๋ฒ•์ด ์ตœ์„ ์ด์—ˆ์ง€๋งŒ ์ง€๊ธˆ์€ ์•„๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ† ๋งํ•ด ๋ณด์ž.

Generator

1๋ถ€์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ Promise Runner์™€ ์ด์ „ ์žฅ์—์„œ ๊ตฌํ˜„ํ–ˆ๋˜ Promise base jsConfirm์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

run(function*() {
  let resolved = [];

  for (let i = 0, len = dupList.length; i < len; i += 1) {
    const item = dupList[i];
    const { confirmed, checked } = yield jsConfirm();

    if (confirmed) {
      // A ํ™•์ธ
      resolved.push(item);
    }

    if (checked) {
      if (confirmed) {
        // B '์ดํ›„ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ', 'ํ™•์ธ'
        resolved = [...resolved, ...dupList.slice(i + 1)];
      } else {
        // C '์ดํ›„ ๋ชจ๋“  ํ•ญ๋ชฉ์— ์ ์šฉ', '์ทจ์†Œ'
      }

      break;
    }
  }

  // D ๋ชจ๋“  ํ•ญ๋ชฉ ์™„๋ฃŒ
  console.log(resolved);
});

Promise Runner ํ•จ์ˆ˜ ๋•๋ถ„์— jsConfirm๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” Promise์˜ ์‘๋‹ต์ด ์˜ฌ ๋•Œ๊นŒ์ง€ ์ผ์‹œ ์ •์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๊ทธ ๋•๋ถ„์— ์ผ๋ฐ˜์ ์ธ for ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ๋„ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

์ฝ”๋“œ์˜ ์ฃผ์„ ํ‘œ๊ธฐ๋Š” A, B, C, D๋กœ ๋‚˜๋‰˜์–ด ์žˆ์ง€๋งŒ, ๊ฒฐ๊ณผ์ ์œผ๋กœ D๋กœ ์ˆ˜๋ ดํ•˜๊ธฐ ๋•Œ๋ฌธ์— [ํŒŒ์ผ ๋ฐ ํด๋” ์ด๋™] API๋Š” D ์œ„์น˜์—์„œ ์ผ๊ด„์ ์œผ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ณ„์†ํ•ด์„œ API ํ˜ธ์ถœ๊นŒ์ง€ ๊ตฌํ˜„ํ•ด ๋ณธ๋‹ค.

function* moveObjects(destId, targetIdList) {
  // [์ด๋ฆ„ ์ค‘๋ณต ํ™•์ธ] API ํ˜ธ์ถœ
  const dupList = yield axios.get("/api/check-duplicate?dest...");

  // [์ค‘๋ณต๋˜๋Š” ๋ชจ๋“ ํ•ญ๋ชฉ์˜ ์‚ฌ์šฉ์ž ์‘๋‹ต ์ˆ˜์ง‘]
  let resolved = [];
  for (let i = 0, len = dupList.length; i < len; i += 1) {
    const item = dupList[i];
    const { confirmed, checked } = yield jsConfirm();

    if (confirmed) {
      // A ํ™•์ธ
      resolved.push(item);
    }

    if (checked) {
      if (confirmed) {
        resolved = [...resolved, ...dupList.slice(i + 1)];
      }

      break;
    }
  }

  // [๊ฐœ์ฒด ์ด๋™] API ํ˜ธ์ถœ
  yield axios.post("/api/move", { resolved /* request data */ });
}

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

์š”๊ตฌ์‚ฌํ•ญ์„ moveObjects Generator ํ•จ์ˆ˜ ํ•˜๋‚˜๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. Callback, Promise๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ํ• ๊นŒ? ๊ทธ๋ƒฅ try...catch๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์‹คํ–‰์„ ๋ณด์žฅํ•˜๋Š” finally๋Š” ๋ค์ด๋‹ค.

function* moveObjects(destId, targetIdList) {
  try {
    /* ๊ตฌํ˜„ */
  } catch (err) {
    // API ํ˜ธ์ถœ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
    // ๊ธฐํƒ€ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
  } finally {
    // ํ•„์š”์˜ ๊ฒฝ์šฐ ๊ตฌํ˜„ (React์˜ ๊ฒฝ์šฐ ์ด์ „ ์ƒํƒœ๋กœ ๋ณต์› ๋“ฑ...)
  }
}

์‚ฌ์‹ค ์œ„์˜ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ ค๋ฉด run ์ฝ”๋“œ๋Š” ์•ฝ๊ฐ„ ์ˆ˜์ •์ด ํ•„์š”ํ•˜๋‹ค. 1๋ถ€์—์„œ ์†Œ๊ฐœํ–ˆ๋˜ Promise Runner๋ฅผ ์ˆ˜์ •ํ•ด ๋ณด์ž

function run(gen) {
  const iter = gen();

  (function iterate({ value, done }) {
    if (done) {
      return value;
    }

    if (value.constructor === Promise) {
      value
        .then(data => iterate(iter.next(data)))
        .catch(err => iter.throw(err)); // ์—ฌ๊ธฐ๊ฐ€ ์ถ”๊ฐ€๋œ ์ฝ”๋“œ
    } else {
      iterate(iter.next(value));
    }
  })(iter.next());
}

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

try {
  jQuery.ajax({
    success: () => {
      //์—ฌ๊ธฐ์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด๋„
    }
  });
} catch (err) {
  // ์—ฌ๊ธฐ์„œ ์žก์„ ์ˆ˜ ์—†๋‹ค.
}

run ๋‚ด๋ถ€์˜ iterate ์‹คํ–‰ ํƒ€์ด๋ฐ๊ณผ Promise ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ๊ทธ ์˜ˆ๋‹ค. ๊ทธ๋ž˜์„œ Promise์˜ ์˜ค๋ฅ˜๋ฅผ ์ง์ ‘ Iterator์˜ throw๋กœ ์ „๋‹ฌํ•˜๋„๋ก ์ˆ˜์ •ํ•œ ๊ฒƒ์ด๋‹ค. ์ด์ œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ํฌํ•จํ•ด Generator๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

์„œ๋น„์Šค ์ง€์› ๋ฒ”์œ„๊ฐ€ Chrome, Firefox ์ตœ์‹  ๋ฒ„์ „์ด๋ฉด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. 8/8์ผ ํ˜„์žฌ Generator๋ฅผ es5๊ธฐ๋ฐ˜ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ๋Š” Facebook ์˜ regenerator๊ฐ€ ์œ ์ผํ•˜๋‹ค. ์ด regenerator๋Š” runtime๊ณผ transpiler๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š”๋ฐ runtime์€ ์••์ถ• ์‹œ 1KB ๋ฏธ๋งŒ์ด๋ฏ€๋กœ ๋ถ€๋‹ด์ด ์—†๋‹ค.

์ด๋ฏธ ์„œ๋ฒ„์— ๋งŽ์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๊ฐ€ ์žˆ๊ณ  ์ด ์ค‘ Generator๋ฅผ ์ ์šฉํ•˜๊ณ  ์‹ถ์€ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

// ์„œ๋น„์Šค ์ฝ”๋“œ
<script>
  // ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์„ค์ •
  window.ne = { toastDrive: {} };
</script>
<script src="src/js/generators.js"></script>
<script>
  ne.toastDrive.logList(["a", "b"]);
</script>

์•„๋ž˜ generators.src.js ๋Š” ํŠธ๋žœ์ŠคํŒŒ์ผ๋˜๊ธฐ ์ „ Generator ํ•จ์ˆ˜๋“ค์ด ๋ชจ์—ฌ์žˆ๋Š” ํŒŒ์ผ์ด๋‹ค.

// src/js/generators.src.js

function run(gen) {/* runner */}

ne.toastDrive.logList = function(dupList) {
  run(function*() {
    for (let item of dupList) {
      console.log(item);
      yield item;
    }
  });
};

์ด์ œ regenerator๋ฅผ ์ด์šฉํ•ด ์ด ํŒŒ์ผ์„ ํŠธ๋žœ์ŠคํŒŒ์ผํ•œ๋‹ค.

// regenerator ์„ค์น˜
npm install -g regenerator

// --include-runtime์„ ํ†ตํ•ด ์˜์กด ๋ชจ๋“ˆ์„ ํฌํ•จํ•˜์—ฌ ํŠธ๋žœ์ŠคํŒŒ์ผ
regenerator --include-runtime src/js/generators.src.js > src/js/generators.js

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ํ•„์š”ํ•œ ๋ถ€๋ถ„์—๋งŒ Generator๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ € regenerator์‹คํ–‰ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ฐฐํฌ ์‹œ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜๋ฉด ์ˆ˜๋™์œผ๋กœ ๋ฐฐํฌ ์ „ ์‹คํ–‰ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

๊ฒฐ๋ก 

Generator๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด '๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ๋™๊ธฐ ์ฝ”๋“œ์ฒ˜๋Ÿผ' ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ณต์žกํ•œ ๋น„๋™๊ธฐ ํ๋ฆ„์„ ์•Œ์•„๋ณด๊ธฐ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ  ์ด๋Š” ํ๋ฆ„์ด ๋ณต์žกํ•˜๋ฉด ๋ณต์žกํ• ์ˆ˜๋ก ๋น›์„ ๋ฐœํ•œ๋‹ค.

Promise, Generator, BabelJS ์˜ ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ•์„ ์ตํ˜€์•ผ ํ•˜์ง€๋งŒ. ์ถฉ๋ถ„ํžˆ ๊ทธ๋Ÿด๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค. ๋งŒ์•ฝ ์กฐ๊ธˆ ๋” ์š•์‹ฌ์ด ์žˆ๋‹ค๋ฉด ์ด ๊ธ€์—์„œ ๊ฐ„๋žตํ•˜๊ฒŒ ๋‹ค๋ฃจ์—ˆ๋˜ Callback, Promise, Generator ์ˆœ์„œ์˜ ํŒจํ„ด์˜ ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ๊ณผ ๋‹จ์ , ๋‹ค์Œ ๋ฐฉ๋ฒ•์ด ๊ฐ€์ ธ๋‹ค์ฃผ๋Š” ์ด์ ์„ ํŒŒ์•…ํ•œ๋‹ค๋ฉด ์–ด๋–ค JavaScript ์š”๊ตฌ์‚ฌํ•ญ๋„ ์–ด๋ ต์ง€ ์•Š๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

๊น€๋ฏผํ˜•2016.08.05
Back to list