[TECH-QA] 이벀트 버블링(Event Bubbling)κ³Ό 이벀트 캑쳐링(Event Capturing)

이벀트 버블링(Event Bubbling)

이벀트 버블링(Event Bubbling)은 HTMLμ—μ„œ μ΄λ²€νŠΈκ°€ λ°œμƒν–ˆμ„ λ•Œ, ν•΄λ‹Ή μ΄λ²€νŠΈκ°€ λ°œμƒν•œ μš”μ†Œμ—μ„œ μ‹œμž‘ν•˜μ—¬ μƒμœ„ μš”μ†Œλ‘œ μ μ§„μ μœΌλ‘œ μ „νŒŒλ˜λŠ” ν˜„μƒμ„ μ˜λ―Έν•©λ‹ˆλ‹€. μ΄λŠ” DOM(Document Object Model)의 계측 ꡬ쑰λ₯Ό 따라 μ΄λ²€νŠΈκ°€ μ²˜λ¦¬λ˜λŠ” κΈ°λ³Έ λ™μž‘ λ°©μ‹μž…λ‹ˆλ‹€.

πŸ“ 이벀트 λ²„λΈ”λ§μ˜ λ™μž‘ 원리

이벀트 버블링은 μ‚¬μš©μžκ°€ νŠΉμ • μš”μ†Œμ—μ„œ 이벀트λ₯Ό λ°œμƒμ‹œμΌ°μ„ λ•Œ(예: λ²„νŠΌ 클릭), ν•΄λ‹Ή μš”μ†Œμ—μ„œ μ΄λ²€νŠΈκ°€ 처리된 ν›„ λΆ€λͺ¨ μš”μ†Œλ‘œ μ΄λ²€νŠΈκ°€ μ „λ‹¬λ˜λŠ” κ³Όμ •μž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ•ˆμ— 이 있고 λ²„νŠΌμ„ ν΄λ¦­ν•˜λ©΄, λ¨Όμ € μ—μ„œ μ΄λ²€νŠΈκ°€ 처리되고, κ·Έ λ‹€μŒ 둜 μ΄λ²€νŠΈκ°€ μ „νŒŒλ©λ‹ˆλ‹€. 이 과정은 DOM 트리의 μ΅œμƒμœ„ μš”μ†Œ(보톡 documentλ‚˜ window)에 도달할 λ•ŒκΉŒμ§€ κ³„μ†λ©λ‹ˆλ‹€.

πŸ“ 이벀트 μœ„μž„(Event Delegation)

이벀트 버블링을 ν™œμš©ν•˜λ©΄ 이벀트 μœ„μž„μ„ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이벀트 μœ„μž„μ€ κ°œλ³„ ν•˜μœ„ μš”μ†Œλ§ˆλ‹€ 이벀트 ν•Έλ“€λŸ¬λ₯Ό λ“±λ‘ν•˜λŠ” λŒ€μ‹ , 곡톡 λΆ€λͺ¨ μš”μ†Œμ— ν•˜λ‚˜μ˜ ν•Έλ“€λŸ¬λ₯Ό λ“±λ‘ν•˜μ—¬ ν•˜μœ„ μš”μ†Œμ—μ„œ λ°œμƒν•œ 이벀트λ₯Ό μ²˜λ¦¬ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€. μ΄λŠ” μ½”λ“œ νš¨μœ¨μ„±μ„ 높이고, λ™μ μœΌλ‘œ μΆ”κ°€λœ μš”μ†Œμ—λ„ 이벀트 처리λ₯Ό μ μš©ν•  수 있게 ν•΄μ€λ‹ˆλ‹€.

πŸ“ 이벀트 μ „νŒŒ λ°©μ§€

λ•Œλ‘œλŠ” μ΄λ²€νŠΈκ°€ μƒμœ„ μš”μ†Œλ‘œ μ „νŒŒλ˜λŠ” 것을 막고 싢을 λ•Œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό μœ„ν•΄ 이벀트 객체의 stopPropagation() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ μ΄λ²€νŠΈκ°€ 더 이상 버블링 λ˜λŠ” μΊ‘μ³λ§λ˜μ§€ μ•Šλ„λ‘ 쀑지할 수 μžˆμŠ΅λ‹ˆλ‹€. 단, stopPropagation()은 이벀트 μ „νŒŒλ§Œ 막을 뿐, κΈ°λ³Έ λ™μž‘(예: 링크 클릭 μ‹œ νŽ˜μ΄μ§€ 이동)을 λ§‰μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€. κΈ°λ³Έ λ™μž‘μ„ λ§‰μœΌλ €λ©΄ preventDefault()λ₯Ό μΆ”κ°€λ‘œ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

HTML ꡬ쑰

<div id="parent">
  <button id="child1">λ²„νŠΌ 1</button>
  <button id="child2">λ²„νŠΌ 2</button>
</div>

JavaScript μ½”λ“œ

// λΆ€λͺ¨ μš”μ†Œμ— 이벀트 ν•Έλ“€λŸ¬ 등둝 (이벀트 μœ„μž„)
document.getElementById('parent').addEventListener('click', function(event) {
  console.log('λΆ€λͺ¨ μš”μ†Œμ—μ„œ 이벀트 감지:', event.target.id);
  
  // νŠΉμ • μ‘°κ±΄μ—μ„œ 이벀트 μ „νŒŒ 쀑지
  if (event.target.id === 'child1') {
    console.log('child1 클릭 μ‹œ μ „νŒŒ 쀑지');
    event.stopPropagation();
  }
});

// κ°œλ³„ μš”μ†Œμ— ν•Έλ“€λŸ¬ μΆ”κ°€ (λΉ„κ΅μš©)
document.getElementById('child1').addEventListener('click', function() {
  console.log('child1μ—μ„œ 직접 처리');
});

πŸ“ 버블링 확인

  • "λ²„νŠΌ 1"(child1)을 ν΄λ¦­ν•˜λ©΄ λ¨Όμ € child1에 λ“±λ‘λœ ν•Έλ“€λŸ¬κ°€ μ‹€ν–‰λ˜μ–΄ "child1μ—μ„œ 직접 처리"κ°€ 좜λ ₯λ©λ‹ˆλ‹€.
  • 이후 μ΄λ²€νŠΈκ°€ λΆ€λͺ¨(parent)둜 λ²„λΈ”λ§λ˜μ–΄ "λΆ€λͺ¨ μš”μ†Œμ—μ„œ 이벀트 감지: child1"이 좜λ ₯λ©λ‹ˆλ‹€.
  • μΆ”κ°€λ‘œ stopPropagation()이 ν˜ΈμΆœλ˜λ―€λ‘œ 더 이상 μƒμœ„λ‘œ μ „νŒŒλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

πŸ“ 이벀트 μœ„μž„ 확인

  • "λ²„νŠΌ 2"(child2)λ₯Ό ν΄λ¦­ν•˜λ©΄ child2μ—λŠ” κ°œλ³„ ν•Έλ“€λŸ¬κ°€ μ—†μ§€λ§Œ, λΆ€λͺ¨ μš”μ†Œμ— λ“±λ‘λœ ν•Έλ“€λŸ¬κ°€ 이λ₯Ό 감지해 "λΆ€λͺ¨ μš”μ†Œμ—μ„œ 이벀트 감지: child2"λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€.
  • 이λ₯Ό 톡해 κ°œλ³„ λ²„νŠΌλ§ˆλ‹€ ν•Έλ“€λŸ¬λ₯Ό λ“±λ‘ν•˜μ§€ μ•Šμ•„λ„ λΆ€λͺ¨μ—μ„œ λͺ¨λ“  클릭 이벀트λ₯Ό μ²˜λ¦¬ν•  수 μžˆμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“ μ‹€μ œ ν™œμš© μ˜ˆμ‹œ

  • 예λ₯Ό λ“€μ–΄, λ™μ μœΌλ‘œ λ²„νŠΌμ΄ μΆ”κ°€λ˜λŠ” λ¦¬μŠ€νŠΈκ°€ μžˆλ‹€κ³  κ°€μ •ν•  λ•Œ, 각 λ²„νŠΌμ— ν•Έλ“€λŸ¬λ₯Ό μΆ”κ°€ν•˜λŠ” λŒ€μ‹  λΆ€λͺ¨
      μš”μ†Œμ— ν•Έλ“€λŸ¬λ₯Ό λ“±λ‘ν•˜λ©΄ μƒˆλ‘œ μΆ”κ°€λœ λ²„νŠΌλ„ μžλ™μœΌλ‘œ μ²˜λ¦¬λ©λ‹ˆλ‹€.

    이벀트 캑쳐링(Event Capturing)

    이벀트 캑쳐링(Event Capturing)은 μ΄λ²€νŠΈκ°€ μ΅œμƒμœ„ μš”μ†Œμ—μ„œ μ‹œμž‘ν•΄ ν•˜μœ„ μš”μ†Œλ‘œ λ‚΄λ €κ°€λŠ” λ°©μ‹μœΌλ‘œ, λ²„λΈ”λ§κ³ΌλŠ” λ°˜λŒ€ λ°©ν–₯으둜 μž‘λ™ν•©λ‹ˆλ‹€. HTMLμ—μ„œλŠ” 기본적으둜 버블링이 주둜 μ‚¬μš©λ˜μ§€λ§Œ, ν•„μš”μ— 따라 캑쳐링도 ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    πŸ“ 캑쳐링 μ‚¬μš© 예제

    document.getElementById('parent').addEventListener('click', function() {
      console.log('캑쳐링 λ‹¨κ³„μ—μ„œ 감지');
    }, { capture: true }); // 캑쳐링 λͺ¨λ“œ ν™œμ„±ν™”
    { capture: true } μ˜΅μ…˜μ„ μΆ”κ°€ν•˜λ©΄ μ΄λ²€νŠΈκ°€ 캑쳐링 λ‹¨κ³„μ—μ„œ μ²˜λ¦¬λ©λ‹ˆλ‹€. 이 경우, λΆ€λͺ¨μ—μ„œ λ¨Όμ € μ΄λ²€νŠΈκ°€ κ°μ§€λœ ν›„ μžμ‹μœΌλ‘œ μ „λ‹¬λ©λ‹ˆλ‹€.