본문 바로가기
TECH

이벤트 전파 🖐🏻

by Stella-Park 2025. 7. 4.
728x90

이벤트 전파(Event Propagation)는 웹 브라우저에서 이벤트가 DOM(Document Object Model) 트리 상에서 어떻게 전달되는지를 이해하는 데 중요한 개념이다.

크게 이벤트 캡처링(capturing), 이벤트 타겟(target), 이벤트 버블링(bubbling) 세 단계로 나뉘며, 이 과정에서 이벤트 리스너가 어떻게 동작하는지 알 수 있다.

 

이벤트 전파란?

이벤트 전파는 브라우저가 이벤트를 발생시킨 요소뿐 아니라 그 부모, 조상 요소에 대해서도 이벤트를 전달하는 과정이다.

예를 들어 버튼을 클릭하면 버튼뿐 아니라 그 버튼을 감싸고 있는 div, body, html까지 이벤트가 전달될 수 있다.

 

이 전파에는 두 가지 주요 경로가 있는데

  1. 캡처링(캡처 단계, Capturing phase)
  2. 버블링(버블 단계, Bubbling phase)

 

이벤트 캡처링 (Capturing phase)

  • 이벤트가 최상위 요소(document 또는 window)에서 시작해서 타겟 요소로 내려오면서 전달되는 단계
  • DOM 트리의 루트에서 이벤트가 발생한 요소까지 내려가며 각 노드에서 리스너를 실행할 기회를 준다.
  • addEventListener에서 세 번째 인자를 true로 설정하거나 { capture: true } 옵션을 줘야 이 단계에서 이벤트 리스너가 실행된다.
document.body.addEventListener('click', (e) => {
  console.log('body capturing');
}, true);

이 코드는 캡처링 단계에서 body의 클릭 이벤트를 감지한다.

 

이벤트 버블링 (Bubbling phase)

  • 이벤트가 타겟 요소에서 시작해서 상위 요소로 올라가며 전달되는 단계
  • 기본적으로 대부분의 이벤트는 버블링 단계에서 리스너가 실행된다.
  • addEventListener의 세 번째 인자를 생략하거나 false로 하면 버블링 단계에서 리스너가 실행된다.
document.body.addEventListener('click', (e) => {
  console.log('body bubbling');
})

이 코드는 클릭 이벤트가 버블링될 때 body에서 실행된다.

 

이벤트 타겟 (Target phase)

  • 캡처링이 끝나고, 이벤트가 실제 발생한 타겟 요소에서 리스너가 실행된다.
  • 이 시점에서는 e.target과 e.currentTarget이 같을 수 있다.
  • 캡처링과 버블링 모두 타겟 단계에서 리스너가 실행될 수 있다.

 

이벤트 전파 중단하기

이벤트 전파를 중단하고 싶을 땐

  • event.stopPropagation() → 캡처링이나 버블링을 멈춤 (하지만 현재 단계 리스너는 계속 실행)
  • event.stopImmediatePropagation() → 같은 요소에 걸린 다른 리스너 실행도 막음

 

캡처링과 버블링 순서

캡처링 단계]
document → html → body → ... → 타겟의 부모 → 타겟

[타겟 단계]
타겟

[버블링 단계]
타겟의 부모 → ... → body → html → document

 

추가 팁

  • 대부분의 이벤트 리스너는 버블링 단계에서 동작하도록 작성된다. (capture: false 또는 생략)
  • 캡처링 단계는 특수한 경우 (예: 보안, 특정 순서 보장) 아니면 잘 쓰이지 않는다.
  • event.composedPath()를 사용하면 이벤트가 거쳐간 경로를 배열로 볼 수 있다.

e.stopPropagation()은 이벤트 전파를 제어할 때 핵심적인 역할을 하는 메서드다.

 

e.stopPropagation()

e.stopPropagation()은 이벤트가 현재 요소에서 더 이상 상위(또는 하위) 요소로 전파되지 않도록 막는 메서드다.

  • 캡처링 단계에서는 상위 요소들로 내려가는 전파를 멈추고,
  • 버블링 단계에서는 상위 요소로 올라가는 전파를 멈춘다.

하지만:
👉 현재 요소에 걸린 다른 리스너는 계속 실행된다.

 

element.addEventListener('click', function (e) {
  e.stopPropagation();
  console.log('클릭 이벤트: 이 요소까지만 전파');
});

위 코드에서는 element를 클릭하면, 이 요소까지만 이벤트가 도달하고 더 이상 부모 요소들로는 이벤트가 전파되지 않는다.

 

캡처링과 버블링에서의 stopPropagation

1️⃣ 캡처링 단계에서

  • 이벤트가 위에서 아래로 내려오다가 stopPropagation()을 만나는 순간, 타겟으로 내려가는 전파가 멈춤
document.body.addEventListener('click', (e) => {
  console.log('body capturing');
  e.stopPropagation();
}, true);

 

이 경우 타겟과 버블링 단계는 아예 실행되지 않는다.

 

2️⃣ 버블링 단계에서

  • 이벤트가 타겟에서 발생하고 상위로 올라가면서 stopPropagation()을 만나면 더 이상 부모 요소로 이벤트가 전달되지 않음
child.addEventListener('click', (e) => {
  console.log('child click');
  e.stopPropagation();
});

child에서 멈추고, parent, body 등의 리스너는 실행되지 않는다.

 

stopPropagation()과 stopImmediatePropagation() 차이

메서드 부모(조상) 전파 차단 같은 요소의 다른 리스너 차단
stopPropagation() O X (같은 요소 리스너는 실행됨)
stopImmediatePropagation() O O (같은 요소 리스너도 실행 안됨)

 

button.addEventListener('click', (e) => {
  e.stopImmediatePropagation();
  console.log('첫 번째 리스너');
});

button.addEventListener('click', (e) => {
  console.log('두 번째 리스너');
});

👉 stopImmediatePropagation()이 호출되면 두 번째 리스너는 실행되지 않는다.

 

 

자주 하는 실수

1️⃣ return false와의 차이

  • return false는 jQuery에서 e.preventDefault() + e.stopPropagation()을 동시에 하는 역할을 했지만, 순수 DOM에서는 아무 효과가 없다.
  • 순수 JS에서는 명시적으로 e.stopPropagation()과 e.preventDefault()를 구분해서 호출해야 한다.

2️⃣ preventDefault()와 혼동

  • preventDefault()는 이벤트의 기본 동작(예: 링크 클릭 시 이동, form submit) 을 막는 것
  • stopPropagation()은 이벤트 전파를 막는 것

 

예시: 부모에 영향 주지 않기

<div id="parent">
  <button id="child">Click me</button>
</div>

document.getElementById('parent').addEventListener('click', () => {
  console.log('parent clicked');
});

document.getElementById('child').addEventListener('click', (e) => {
  e.stopPropagation();
  console.log('child clicked');
});

 

버튼 클릭시: child clicked
부모(parent clicked)는 출력되지 않음

 

사용처

  • 모달, 드롭다운 닫힘 이벤트: 모달 내부 클릭 시 외부의 닫힘 이벤트가 실행되지 않도록
  • 네비게이션 중첩 메뉴: 내부 메뉴 클릭 시 상위 메뉴 클릭 이벤트 차단
  • 특정 이벤트 흐름 제어: 일부 영역의 이벤트는 상위 요소로 전달되지 않도록 보장

 

이상 스텔라였습니다. ✍️

 

 

 

 

 

728x90