throtling&debouncing - TECH-SHARING-STUDY/FE_STUDY GitHub Wiki

Event ๊ฐ์ฒด๋ž€? ์บก์ฒ˜๋ง๊ณผ ๋ฒ„๋ธ”๋ง, ์ด๋ฒคํŠธ ์œ„์ž„, ์“ฐ๋กœํ‹€๋ง & ๋””๋ฐ”์šด์‹ฑ

Event ๊ฐ์ฒด

  • ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ์ฒด
  • eventListener ํ•จ์ˆ˜์— ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋œ๋‹ค

Event ๋ฐœ์ƒ

  • ์‚ฌ์šฉ์ž ์•ก์…˜(๋งˆ์šฐ์Šค ํด๋ฆญ ๋“ฑ)
  • HTMLElement.click() ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ์ด๋‚˜ EventTarget.dispatchEvent() ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜๋„์ ์œผ๋กœ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ์Œ

ํ•˜๋‚˜์˜ Element๋Š” ๋‹ค์ˆ˜์˜ listener๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. (๋™์ผํ•œ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด์„œ๋„.)

Event Bubbling

ํŠน์ • ํ™”๋ฉด ์š”์†Œ์—์„œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ๋” ์ƒ์œ„์˜ ํ™”๋ฉด ์š”์†Œ๋“ค๋กœ ์ „๋‹ฌ๋˜์–ด ๊ฐ€๋Š” ํŠน์„ฑ

Event Capturing

์ด๋ฒคํŠธ ๋ฒ„๋ธ”๋ง๊ณผ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ์ง„ํ–‰๋˜๋Š” ์ด๋ฒคํŠธ ์ „ํŒŒ ๋ฐฉ์‹

div.addEventListener("click", eventHandler, {
  capture: true, // default ๊ฐ’์€ false
});

event.stopPropagation()

ํ•ด๋‹น event๊ฐ€ ์ „ํŒŒ๋˜๋Š” ๊ฒƒ์„ ๋ง‰์Œ. (๋‚˜๊นŒ์ง€๋งŒ ์‹คํ–‰)

event.stopImmediatePropagation()

element์— click ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์„ธ ๊ฐœ(eH1, eH2, eH3) ์žˆ๋‹ค๊ณ  ๊ฐ€์ •.

stopPropagation()์„ ์–ด๋””์„œ ํ•˜๋˜์ง€ ์„ธ๊ฐœ ๋‹ค ์‹คํ–‰ํ•˜์ง€๋งŒ, immediateํ•จ์ˆ˜ ์‹คํ–‰ํ•˜๋ฉด ์ดํ›„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊นŒ์ง€๋„ ๋™์ž‘ ์•ˆํ•จ

Event Delegation (์ด๋ฒคํŠธ ์œ„์ž„)

๋ฒ„๋ธ”๋ง/์บก์ณ๋ง์„ ํ™œ์šฉํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋ง ํŒจํ„ด.

element๋งˆ๋‹ค ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ , ๊ณตํ†ต ์กฐ์ƒ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋‹จ ํ•˜๋‚˜๋งŒ ๋‹ฌ์•„์„œ ์—ฌ๋Ÿฌ ์š”์†Œ์˜ ์ด๋ฒคํŠธ๋ฅผ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ.

todo๋ฆฌ์ŠคํŠธ ๊ฐœ๋ฐœํ• ๋•Œ ์–ด๋–ป๊ฒŒ ํ–ˆ๋Š”์ง€ ์ƒ๊ฐํ•ด๋ณด์ž.

์˜ˆ์‹œ) ์นด๋“œ๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. ๋งจ์ฒ˜์Œ์— ์ด๋ฒคํŠธ๋ฅผ ๋‹ค ๋‹ฌ์•˜๋‹ค โ‡’ ์ƒˆ ์นด๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด...? ๋˜๋‹ฌ์•„์•ผ๋œ๋‹ค
  2. ํด๋ž˜์Šค๋กœ ๊ด€๋ฆฌํ–ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์นด๋“œ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‹ฌ๋ ค์žˆ๋‹ค. โ‡’ ๋ง๋„์•ˆ๋˜์ง€๋งŒ ์นด๋“œ๊ฐ€ 10๋งŒ๊ฐœ๊ณ  ์นด๋“œ๋‹น ์ด๋ฒคํŠธํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๋ฉด ๋ฆฌ์†Œ์Šค๊ฐ€ ๋งŽ์ด ๋“ ๋‹ค.
  3. ์ด๋ฒคํŠธ ์œ„์ž„์„ ํ™œ์šฉํ–ˆ๋‹ค. โ‡’ ์ด๋ฒคํŠธ๋Š” ๊ณตํ†ต ์กฐ์ƒ์— ํ•˜๋‚˜๋งŒ ๋‹ฌ๋ ค์žˆ๋‹ค. ๊ฑ”๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ์–ด๋–ค ์นด๋“œ๊ฐ€ ํด๋ฆญ๋œ๊ฑด์ง€ ํŒ๋‹จํ•˜๊ณ , ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ƒˆ ์นด๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด๋„ ๋„๋–ก์—†๋‹ค. ์นด๋“œ๊ฐ€ ๋งŽ์•„์ ธ๋„ ์ƒ๊ด€์—†๋‹ค.

but, ์ด๋ฒคํŠธ๊ฐ€ ๋‹ค์–‘ํ•ด์ง€๋ฉด ๋‹ค์–‘ํ•ด์งˆ์ˆ˜๋ก ๋น„๊ต์—ฐ์‚ฐ์ด ๋งŽ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.

const getData = (target: HTMLElement): CardData => ({
  ids: getElementIds(target),

  columnElm: target.closest('.column'),

  cardElm: target.closest('.card'),
  cardFormElm: target.closest('.card.new'),

  createCardBtnElm: target.closest('.action-btn.new-card-btn'),
  deleteCardBtnElm: target.closest('.delete-card-btn'),
  editOkBtnElm: target.closest('.card-btn.edit'),
  createCardOKBtnElm: target.closest('.card-btn.add'),
  cancelBtnElm: target.closest('.card-btn.cancel'),
})

window.addEventListener('click', (e) => {
  const target = e.target as HTMLElement

  const data = getData(target)
  const {
    cardElm,
    createCardBtnElm,
    deleteCardBtnElm,
    editOkBtnElm,
    createCardOKBtnElm,
    cancelBtnElm,
  } = data

  if (createCardBtnElm) {
    createCardFormHandler(data)
    return
  }

  if (createCardOKBtnElm) {
    createCardHandler(data)
    return
  }

  if (editOkBtnElm) {
    const previousCardId = getPreviousCardNumber(cardElm)
    editCardHandler(data, previousCardId)
    return
  }

  if (cancelBtnElm) {
    cancelCreateOrEditHandler(data)
    return
  }

  if (deleteCardBtnElm) {
    deleteCardHandler(data)
    return
  }
})

Throttling, Debouncing

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋งŽ์€ ์—ฐ์‚ฐ(์˜ˆ : ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ ๋ฐ ๊ธฐํƒ€ DOM ์กฐ์ž‘)์„ ์ˆ˜ํ–‰(์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๊ณผ๋„ํ•œ ํšŸ์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ)ํ•˜๋Š” ๊ฒฝ์šฐ ์— ๋Œ€ํ•ด ์ œ์•ฝ์„ ๊ฑธ์–ด ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜์ค€์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ(๊ทธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋” ์ ๊ฒŒ ์‹คํ–‰ํ•˜๋ฉด ๋น ์ ธ ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ์Œ)์‹œํ‚ค๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ๊ธฐ์ˆ 

์™œ? JS์—ฐ์‚ฐ์ด ๋งŽ์•„์ง€๋ฉด ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์žก์•„๋จน์Œ โ†’ ํ”„๋ ˆ์ž„๋–จ์–ด์ง โ†’ ๋“œ๋“œ๋“œ๋“

debounce

์—ฐ๋‹ฌ์•„ ํ˜ธ์ถœ๋˜๋Š”๊ฒƒ์ค‘์— ๋งˆ์ง€๋ง‰ or ์ œ์ผ ์ฒ˜์Œ ํ•จ์ˆ˜๋งŒ ํ˜ธ์ถœ

์ด๋ฒˆ์— ์ž๋™์™„์„ฑ์— ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Œ. input์˜ onChange์— apiํ˜ธ์ถœ ๊ฑธ๋ฉด '๋ฐ”๋‚˜๋‚˜๋ง› ์šฐ์œ ' ์น ๋•Œ ๋ชจ์Œ๊ฐฏ์ˆ˜+์ž์Œ๊ฐฏ์ˆ˜๋งŒํผ apiํ˜ธ์ถœ์ผ์–ด๋‚จ.

๋ฆฌ์†Œ์Šค ๋‚ญ๋น„์ผ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์‹ฑํฌ์— ๋Œ€ํ•œ ๋ณด์žฅ๋„ ๋ชปํ•จ.

throttle

์—ฐ๋‹ฌ์•„ ํ˜ธ์ถœ๋˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ผ์ •ํ•œ ์ฃผ๊ธฐ๋งˆ๋‹ค ๋ฐœ์ƒํ•˜๊ฒŒ ํ•จ

๋งŒ์•ฝ ์Šคํฌ๋กค์— debounce ๋‹ฌ๋ฉด ์Šคํฌ๋กค์ด ๋ฉˆ์ท„์„ ์ฏค ๋ฐœ์ƒํ•จ. ๋ฌดํ•œ์Šคํฌ๋กค ๊ตฌํ˜„ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด

์Šคํฌ๋กค ์Šค์œฝ์Šค์œฝ ๋๊นŒ์ง€ ๋‚ด๋ ค์„œ ๊ทธ๋งŒ๋’€์„ ๋•Œ apiํ˜ธ์ถœ๋จ. ์ด ๋•Œ throttle์ด ๋” ์ ํ•ฉํ•จ.

์Šคํฌ๋กค ๋‚ด๋ฆด๋•Œ ์ค‘๊ฐ„์ค‘๊ฐ„ 100ms๋งˆ๋‹ค ์ด๋ฒคํŠธ ๋ฐœ์ƒ์‹œ์ผœ์„œ ์ผ์ • ๋ฒ”์œ„์— ๋„๋‹ฌํ•˜๋ฉด apiํ˜ธ์ถœ!

โ‡’ ์‚ฌ์šฉ์ž๊ฐ€ Footer์— ๋‹ฟ๊ธฐ ์ „์— ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Œ

lodash ์ธ์Šคํ†จํ•˜๋ฉด ํŽธํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋‹ค...ใ…Žใ…Ž

๊ตฌํ˜„ํ•ด๋ณด์ž

debounce

const notionScroller = document.querySelector(
  ".notion-scroller.vertical.horizontal"
);

let interval = null;

notionScroller.addEventListener("scroll", (e) => {
  if (interval) {
    clearTimeout(interval);
  }

  interval = setTimeout(() => {
    console.log(
      notionScroller.scrollHeight -
        notionScroller.clientHeight -
        notionScroller.scrollTop
    );
  }, 100);
});

throttle

const notionScroller = document.querySelector(
  ".notion-scroller.vertical.horizontal"
);

let interval = null;

notionScroller.addEventListener("scroll", (e) => {
  if (!interval) {
    interval = setTimeout(() => {
      interval = null;
      console.log(
        notionScroller.scrollHeight -
          notionScroller.clientHeight -
          notionScroller.scrollTop
      );
    }, 100);
  }
});

์ฐธ๊ณ ๋ฌธํ—Œ

https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/

https://ko.javascript.info/bubbling-and-capturing

https://developer.mozilla.org/ko/docs/Web/API/Event

https://webclub.tistory.com/607