학습 공간 개념 · 판단 · 실험

Frontend Performance Lab

JavaScript

DOM 이벤트 전파와 위임

이벤트가 캡처·타깃·버블 단계를 거쳐 전파되는 원리를 이해하고, 공통 부모 요소에서 하위 요소의 이벤트를 효율적으로 위임받아 처리합니다.

이럴 때 필요해요

항목이 수시로 추가·삭제되는 동적 목록에서도 리스너 하나로 안정적인 상호작용을 구현할 수 있습니다.

어떻게 동작하나요?

할 일 목록(ul) 안에 있는 삭제 버튼(button)을 클릭하면, 클릭 이벤트는 그 버튼 하나에서만 끝나지 않고 부모(li, ul)와 문서 전체를 거쳐 흐릅니다. 구체적으로 DOM 이벤트는 최상위에서 실제 클릭된 요소를 향해 내려가는 '캡처 단계(Capturing)', 실제 대상에 도달한 '타깃 단계(Target)', 그리고 다시 부모 요소들을 따라 위로 올라가는 '버블 단계(Bubbling)'의 3단계를 거칩니다. 단, focus나 blur처럼 버블링되지 않는 이벤트도 있으므로 다루려는 이벤트의 전파 특성을 먼저 확인해야 합니다.

이 버블링 특성을 활용하면 버튼 100개에 리스너를 100번 등록하는 대신, 공통 부모인 목록(#list) 하나에만 리스너를 달아 모든 버튼 클릭을 처리할 수 있습니다. 이를 이벤트 위임(Event Delegation)이라고 합니다. 이때 event.currentTarget은 리스너가 등록된 부모(#list)를 가리키고, event.target은 실제로 클릭된 가장 안쪽 요소를 가리킵니다. 사용자가 버튼 안의 아이콘(svg, span)을 클릭했을 수도 있으므로 event.target.closest('button')을 사용해 의도한 버튼 요소를 안전하게 찾아냅니다.

이벤트를 제어할 때 자주 쓰는 stopPropagation()과 preventDefault()는 목적이 완전히 다릅니다. preventDefault()는 링크 이동이나 폼 제출처럼 브라우저가 기본으로 수행하는 동작을 취소하는 메서드이고, stopPropagation()은 이벤트가 상위나 하위 요소로 더 이상 전파되지 않게 막는 메서드입니다. 상위에서 동작하는 분석 코드나 모달 닫기 리스너가 먹통이 될 수 있으므로 stopPropagation()은 꼭 필요한 경우에만 신중히 사용합니다.

코드 예제

동작을 설명하는 읽기 전용 예제입니다.

example.js
const list = document.querySelector('#list');
list?.addEventListener('click', (event) => {
  if (!(event.target instanceof Element)) return;
  const button = event.target.closest('button[data-id]');
  if (button && list.contains(button)) console.log(button.dataset.id);
});
javascript · 읽기 전용6줄

공통 부모인 #list에 클릭 리스너를 한 번만 등록하고, 버튼 안쪽 아이콘이 클릭되더라도 closest로 data-id를 가진 버튼을 찾아 안전하게 처리합니다.

자주 헷갈리는 포인트

  • 실제 클릭된 안쪽 요소인 event.target과 리스너가 달린 요소인 event.currentTarget이 항상 같다고 가정하지 않습니다.
  • 브라우저 기본 동작을 막는 preventDefault()가 이벤트 버블링까지 멈춰 준다고 혼동하지 않습니다.

내 말로 설명해 보기

이벤트 위임을 사용하면 나중에 자바스크립트로 새로 추가한 버튼에도 별도 리스너 등록 없이 클릭이 동작하는 이유는 무엇인가요?

함께 읽으면 좋은 개념

관련 면접 질문 1개

이벤트 캡처·버블링과 이벤트 위임은 어떻게 연결되나요?

DOM 이벤트는 최상위 조상에서 실제 발생 요소(target)로 내려가는 캡처 단계, 타깃 단계, 다시 조상 요소들로 올라가는 버블링 단계를 거쳐 전파됩니다. 이벤트 위임은 이 버블링 특성을 활용해 하위 요소마다 일일이 리스너를 달지 않고 공통 부모 요소 하나에 리스너를 등록한 뒤, event.target이나 closest()로 실제 클릭된 대상을 판별해 처리하는 패턴입니다. 이렇게 하면 동적으로 추가되거나 삭제되는 자식 요소들도 하나의 리스너로 깔끔하게 관리할 수 있습니다.

꼬리질문과 참고자료 →

공식 참고 자료

← JavaScript