
자바스크립트는 기본적으로 이벤트 주도(Event-Driven) 방식으로 동작합니다. 브라우저 환경에서 사용자 클릭, 키보드 입력, 페이지 로드 등 다양한 사건(Event)에 반응하여 로직을 실행하는 것이 핵심이죠.
우리는 보통 다음과 같은 방식으로 이벤트를 등록합니다.
button.addEventListener('click', () => { console.log('버튼 클릭됨!'); });
하지만 애플리케이션의 규모가 커지면 단순히 이벤트를 다는 것만으로는 부족합니다. 메모리 누수(Memory Leak)를 방지하기 위해 이벤트를 적절히 제거해야 하고, 수많은 이벤트가 중첩된 복잡한 DOM 구조에서 이벤트 전파(Propagation)를 정교하게 제어해야 할 상황이 반드시 찾아옵니다.
이벤트 관리의 복잡성을 해결하기 위해 우리는 addEventListener의 세 번째 인자인 Options 객체와 최신 자바스크립트의 AbortController를 적극적으로 활용해야 합니다.
과거에는 removeEventListener를 위해 익명 함수 대신 기명 함수를 정의하고 이를 관리해야 하는 번거로움이 있었지만, 현대적인 방식은 더 선언적이고 안전한 대안들을 제공합니다.
이벤트를 깊이 있게 다루기 위해서는 DOM의 이벤트 전파 흐름을 먼저 이해해야 합니다.
addEventListener의 세 번째 인자로 객체를 넘겨 이벤트의 동작을 미세하게 조정할 수 있습니다.
element.addEventListener('click', handler, { capture: true, // 캡처링 단계에서 이벤트 실행 once: true, // 딱 한 번만 실행하고 자동으로 제거 passive: true // scroll 성능 최적화 (preventDefault 호출 안 함을 명시) });
once: true: "모달 닫기"나 "최초 1회 실행"이 필요한 경우 유용합니다. 직접 제거하는 로직을 짤 필요가 없어 코드가 깔끔해집니다.passive: true: 스크롤(Scroll)이나 터치 이벤트에서 브라우저가 preventDefault() 호출을 기다리지 않게 하여 스크롤 성능을 획기적으로 개선합니다.여러 개의 이벤트를 한꺼번에 제거해야 할 때, 기존 방식은 상당히 고통스럽습니다. 이때 AbortController를 사용하면 마치 전원을 끄듯 일괄 종료할 수 있습니다.
const controller = new AbortController(); const { signal } = controller; // 여러 요소에 동일한 signal 전달 window.addEventListener('resize', handleResize, { signal }); document.addEventListener('keydown', handleKey, { signal }); btn.addEventListener('click', handleClick, { signal }); // 원할 때 한 번에 모두 제거! controller.abort();
이 방식은 특히 React의 useEffect cleanup이나 SPA의 페이지 전환 시 등록된 모든 글로벌 이벤트를 한 번에 정리할 때 매우 강력합니다.
passive 옵션을 통해 부드러운 스크롤 인터랙션을 제공합니다.once나 signal을 사용하여 선언적으로 이벤트를 관리할 수 있습니다.signal 옵션 등은 구형 브라우저(IE 등)에서 지원되지 않으므로 폴리필이나 트랜스파일링 확인이 필요합니다.자바스크립트 개발자에게 addEventListener는 가장 친숙하지만, 그만큼 오용하기 쉬운 도구입니다. 캡처링/버블링의 원리를 정확히 이해하고, AbortController와 같은 현대적인 관리 기법을 익힌다면 한 차원 높은 수준의 프론트엔드 아키텍처를 설계할 수 있을 것입니다.
참고 자료: