티스토리 뷰

웹 프론트엔드 개발자들이 “JavaScript event delegation 오류 해결법”을 검색할 때 직면하는 대표적 문제는 다음과 같다. 동적으로 생성되는 DOM 요소에 이벤트가 정상적으로 붙지 않거나, 상위 요소에 이벤트 위임을 했음에도 클릭이 제대로 처리되지 않는 사례, 잘못된 event.target 검사로 인해 의도한 이벤트가 트리거되지 않는 경우 등이 있다. 이러한 문제는 DOM이 동적으로 바뀌는 현대 웹 애플리케이션에서 특히 빈번하며, 이벤트가 버블링되어 상위 요소로 전파된다 하더라도 개발자가 의도한 하위 요소로 핸들링 로직이 작동하지 않는 경우가 많다. 또한 “stopPropagation()” 또는 잘못된 selector 검사로 인해 이벤트 위임 자체가 무력화되는 경우도 흔하게 발생한다. 이로 인해 사용자는 클릭이 작동하지 않는 UI를 경험하게 되고, 개발자는 디버깅에 많은 시간을 소모하게 되는 심각한 생산성 저하를 겪는다.

 

이러한 문제의 본질은 이벤트 위임이 동작하는 DOM 이벤트 전파 메커니즘(버블링, 캡처링 등)을 정확히 이해하지 못한 상태에서 구현이 이루어지기 때문이다. 이벤트 위임은 모든 자식 요소에 리스너를 일일이 붙이는 것 대신 상위 요소 하나에만 리스너를 붙여 성능을 개선하고, 동적 요소에 대해 재바인딩 없이도 이벤트를 처리할 수 있도록 고안된 패턴이다. 그러나 잘못된 조건문, 잘못된 이벤트 타입, DOM 로딩 타이밍 오류 등이 복합적으로 겹치면서 예기치 않은 오류를 유발한다. 이런 오류는 개발자가 의도한 UX 흐름을 파괴함으로써 사용자 만족도를 저해하는 직접적 요인이 된다.

 

 

이벤트 위임 메커니즘과 오류 발생 원인

JavaScript에서 이벤트는 기본적으로 DOM 버블링(Event Bubbling) 과정을 통해 하위 요소에서 상위 요소로 전파된다. 즉, 사용자가 나 같은 하위 요소를 클릭하면, 해당 이벤트는 그 요소부터 시작하여 상위 요소를 따라 body, document까지 올라간다. 이 과정을 이해해야만 event delegation이 작동한다. 일반적인 이벤트 위임은 상위 요소 하나에 이벤트 리스너를 붙이고, 이벤트 객체의 event.target을 통해 실제 이벤트가 발생한 요소를 판별한다. 이를 통해 수천 개의 요소에 각각 이벤트를 등록하지 않고도 하나의 리스너로 처리할 수 있다.

 

오류 발생의 대표적인 원인은 다음과 같다. 첫째, 이벤트 타입이 버블링되지 않는 경우(예: focus, blur)에는 event delegation이 작동하지 않으며, 둘째, 상위 요소 리스너에서 event.target 검사 코드가 잘못되어 이벤트가 무시되는 경우가 있다. 또한 테스트 시 DOM이 완전히 로드되기 전에 이벤트 리스너가 설정되는 경우도 오류의 큰 원인이다. 마지막으로 동적 요소가 나중에 추가되면 기존에 querySelectorAll로 수집된 노드에 리스너를 붙였을 때, 새롭게 추가된 요소는 이벤트 위임 처리 대상에서 누락될 수 있다(비록 버블링은 되지만 조건 검사에서 누락됨). 이러한 문제는 대부분 이벤트 위임 패턴을 구현할 때 closest() 또는 matches()에 대한 조건 검사를 정교하게 작성하지 않았기 때문이다.

 

event delegation 오류 대응 비교

문제 유형 오류 원인 구체적 해결법 예상 개선 효과
동적 요소 추가 후 이벤트 없음 조건 검사 누락 parent.addEventListener('click', handler) + event.target.matches() 체크 동적 요소 100% 이벤트 적용
이벤트가 의도한 요소가 아님 event.target 직접 사용 event.target.closest(selector) 사용 정확한 요소 판별율 99%↑
이벤트가 너무 많이 발생 하위 요소마다 리스너 등록 상위 요소 하나만 리스너 등록 리스너 개수 90%↓ (DOM 100개 시)
로드 타이밍 문제 DOM 미완료 상태 이벤트 바인딩 DOMContentLoaded 이후 리스너 등록 오류 발생률 100%→10% 이하
  1. 기본 이벤트 위임 설정: 상위 요소(parent)의 참조를 저장하고, 한 번만 이벤트 리스너를 등록한다. 예: const list = document.getElementById('list');
  2. 정확한 요소 판별: if (!event.target.closest('.item-class')) return;처럼 closest()로 필요한 요소만 처리한다. 정확도는 99% 이상 유지해야 함.
  3. 버블링하는 이벤트만 사용: 'click', 'submit', 'keydown' 등 버블링하는 이벤트만 처리하고, 버블링하지 않는 이벤트는 대체 처리 로직을 작성한다.
  4. DOMContentLoaded 대기: document.addEventListener('DOMContentLoaded', ...)으로 DOM 완성 후 리스너를 등록한다. 이를 통해 누락 문제를 예방할 수 있다.

 

잘못된 상식과 주의사항

  • 이벤트 위임에서 event.target만 단순히 검사하는 것은 위험하다. DOM 구조가 복잡한 경우 자식 내부 요소가 실제 목표가 아닐 수 있다. 이때 closest()를 활용해 의도한 요소만 선별해야 한다.
  • 버블링이 되지 않는 이벤트(focus, blur 등)는 event delegation으로 다룰 수 없다. 대체 이벤트 흐름을 설계해야 한다.
  • 이벤트 위임은 성능 최적화에 유리하지만, 복잡한 조건문이 많아지면 오히려 단일 리스너가 병목이 될 수 있다. 가능한 한 간결한 조건 검사만 유지해야 한다.
  • 동적 요소 처리 시, 이벤트가 상위 요소로 버블링되더라도, 삭제 또는 추가된 요소의 상태를 주기적으로 검증해야 한다. 그렇지 않으면 레거시 DOM이 이벤트 로직에 영향을 줄 수 있다.

소중한 시간을 내어 제 기록을 살펴주셔서 감사합니다. 저 또한 더 정확한 정보를 전달하기 위해 정진하겠습니다.