frontend/javascript

🟨 2-14. 이벤트 버블링과 캡처링 완벽 이해 — 브라우저 이벤트 흐름의 핵심 메커니즘

mirabo01 2025. 11. 7. 08:52

 

1. 이벤트의 흐름이란 무엇인가

브라우저에서 한 요소를 클릭하면,
그 이벤트는 한 방향으로 흐르며 여러 단계에서 감지된다.

이 과정을 이벤트 전파(Event Propagation) 라고 부른다.
전파는 세 단계로 이루어진다.

① 캡처링 단계 (Capturing Phase)
② 타깃 단계 (Target Phase)
③ 버블링 단계 (Bubbling Phase)
  • 캡처링: 문서 최상단(window → document → html → body …)에서 이벤트가 내려옴
  • 타깃: 실제 클릭된 요소에서 이벤트 실행
  • 버블링: 다시 상위 요소로 이벤트가 전파되어 올라감

2. 기본 예시 — 클릭 이벤트의 흐름

<div id="parent">
  <button id="child">클릭</button>
</div>

<script>
  document.getElementById("parent").addEventListener("click", () => {
    console.log("부모 div 클릭!");
  });

  document.getElementById("child").addEventListener("click", () => {
    console.log("자식 button 클릭!");
  });
</script>

출력 결과 👇

자식 button 클릭!
부모 div 클릭!

✅ 이벤트는 child에서 발생한 뒤 → parent로 “버블링” 된다.
✅ 즉, 하위 요소에서 상위 요소로 전파된다.


3. 캡처링(Capturing) 단계란?

이벤트를 “위에서 아래로” 감지하려면,
addEventListener의 세 번째 인자를 true로 설정한다.

document.getElementById("parent").addEventListener(
  "click",
  () => console.log("부모 - 캡처링 단계"),
  true // 캡처링 모드
);

document.getElementById("child").addEventListener("click", () => {
  console.log("자식 - 타깃 단계");
});

출력:

부모 - 캡처링 단계
자식 - 타깃 단계

✅ 캡처링은 “상위 → 하위” 방향
✅ 버블링은 “하위 → 상위” 방향

즉, 이벤트는 캡처링 → 타깃 → 버블링 순으로 항상 흐른다.


4. 버블링(Bubbling) 단계

대부분의 DOM 이벤트는 버블링을 기본적으로 지원한다.
따라서 상위 요소에만 이벤트를 등록해도,
하위 요소의 이벤트를 감지할 수 있다.

document.getElementById("parent").addEventListener("click", (e) => {
  console.log("버튼 클릭 감지:", e.target.tagName);
});

✅ e.target은 실제 클릭된 요소를 가리킨다.
✅ e.currentTarget은 이벤트가 등록된 요소를 가리킨다.

이 차이를 이해하면 React의 이벤트 핸들링도 쉽게 이해된다.


5. 이벤트 전파 중단 (stopPropagation)

가끔 상위 요소로의 이벤트 전파를 막아야 할 때가 있다.
이때 사용하는 것이 e.stopPropagation()이다.

document.getElementById("parent").addEventListener("click", () => {
  console.log("부모 감지");
});

document.getElementById("child").addEventListener("click", (e) => {
  e.stopPropagation();
  console.log("자식 클릭 (전파 중단)");
});

출력:

자식 클릭 (전파 중단)

✅ stopPropagation()을 호출하면 이벤트가 더 이상 상위로 전달되지 않는다.
✅ 모달 닫기, 메뉴 내부 클릭 방지 등에서 자주 사용된다.


6. 이벤트 위임(Event Delegation)

이벤트 버블링의 특성을 이용하면,
하위 요소 각각에 이벤트를 붙이지 않고 상위 요소 하나에만 등록할 수 있다.

<ul id="menu">
  <li>HTML</li>
  <li>CSS</li>
  <li>JavaScript</li>
</ul>

<script>
document.getElementById("menu").addEventListener("click", (e) => {
  if (e.target.tagName === "LI") {
    console.log(`선택된 메뉴: ${e.target.textContent}`);
  }
});
</script>

✅ 동적으로 추가된 <li> 요소에도 자동으로 반응
✅ React가 내부적으로 이 방식을 사용한다 (Synthetic Event System)


7. 실제 브라우저 이벤트 흐름 시각화

예를 들어 다음 HTML이 있다고 하자 👇

<body>
  <div id="outer">
    <button id="inner">Click</button>
  </div>
</body>

이벤트 흐름:

[캡처링]
window → document → html → body → div#outer → button#inner

[타깃]
button#inner

[버블링]
button#inner → div#outer → body → html → document → window

즉, 이벤트는 내려갔다가 다시 올라오는 구조다.


8. stopImmediatePropagation()의 차이

stopPropagation()은 상위로 전파만 막는다.
하지만 같은 요소에 여러 이벤트가 걸려 있을 경우,
그 다음 핸들러까지 실행된다.

stopImmediatePropagation()은
현재 요소의 다른 핸들러 실행까지 모두 중단한다.

button.addEventListener("click", (e) => {
  e.stopImmediatePropagation();
  console.log("첫 번째 핸들러");
});

button.addEventListener("click", () => {
  console.log("두 번째 핸들러"); // 실행되지 않음
});

9. 실무 예제 — 모달 닫기 이벤트 제어

<div id="modal">
  <div id="content">
    <h2>모달 내용</h2>
    <button id="closeBtn">닫기</button>
  </div>
</div>

<script>
const modal = document.getElementById("modal");
const content = document.getElementById("content");
const closeBtn = document.getElementById("closeBtn");

// 배경 클릭 시 닫기
modal.addEventListener("click", () => {
  modal.style.display = "none";
});

// 모달 내부 클릭 시 닫히지 않게
content.addEventListener("click", (e) => e.stopPropagation());

// 닫기 버튼
closeBtn.addEventListener("click", () => {
  modal.style.display = "none";
});
</script>

✅ 배경을 클릭하면 닫힘
✅ 내부 클릭은 stopPropagation()으로 전파 차단

이 패턴은 거의 모든 모달, 드롭다운, 사이드바 UI에서 쓰인다.


10. 이벤트 버블링과 React의 관계

React에서는 실제 DOM 대신 “Virtual DOM”을 사용하지만,
이벤트는 여전히 버블링 기반으로 처리된다.

모든 이벤트 리스너는 실제로 document에 위임되어 있다.
즉, React의 onClick도 결국 “이벤트 위임(Event Delegation)”을 활용한다.

이 때문에, React에서도 e.stopPropagation()을 사용하면
상위 컴포넌트로의 이벤트 전파를 막을 수 있다.


11. 정리

개념 설명

캡처링 이벤트가 상위 → 하위로 전달되는 단계
버블링 이벤트가 하위 → 상위로 전달되는 단계
stopPropagation 이벤트 전파 중단
stopImmediatePropagation 현재 요소 내 모든 핸들러 중단
이벤트 위임 상위 요소 하나로 하위 이벤트 감지

12. 마무리

이제 이벤트 흐름의 전체 그림을 완전히 이해했다.
이걸 모르면 단순한 클릭조차 “왜 이렇게 작동하지?”라는 혼란이 생기지만,
이제 당신은 DOM이 어떻게 이벤트를 “순환”시키는지 정확히 안다.

자바스크립트의 이벤트는 ‘순간’이 아니라 ‘흐름’이다.
그 흐름을 제어할 수 있는 사람이 진짜 프론트엔드 개발자다.