Event delegation은 event bubbling을 활용하는 패턴입니다. 버튼이 여러 개 있을 때 각 버튼마다 click listener를 붙이는 대신, 부모 요소에 listener를 하나 붙입니다. 클릭 이벤트는 target에서 시작해 부모로 bubble되기 때문에 부모 listener에서 event.target을 보고 어떤 버튼이 클릭됐는지 판단할 수 있습니다.
이 방식은 listener 수를 줄이고, 나중에 동적으로 추가된 자식 요소도 별도 listener 등록 없이 처리할 수 있다는 장점이 있습니다.
꼬리질문
조금 더 깊게 물어본다면
답변 뒤에 이어질 수 있는 질문들을 하나씩 열어볼 수 있어요.
event.target과 event.currentTarget은 어떻게 다른가요?
event.target은 실제 이벤트가 발생한 가장 안쪽 요소에 가깝고, event.currentTarget은 현재 listener가 붙어 있는 요소입니다. delegation에서는 부모에 listener가 있으므로 currentTarget은 부모이고, target은 클릭된 자식일 수 있습니다.
이 차이를 모르면 delegation 코드에서 엉뚱한 요소를 처리할 수 있습니다.
stopPropagation을 쓰면 어떻게 되나요?
이벤트가 더 이상 상위 요소로 전파되지 않게 막을 수 있습니다. 자식에서 stopPropagation을 호출하면 부모의 delegated listener가 실행되지 않을 수 있습니다.
그래서 delegation에 의존하는 구조에서는 propagation을 막는 코드가 있는지 조심해야 합니다.
모든 이벤트에 delegation을 쓸 수 있나요?
아닙니다. 이벤트마다 bubbling 여부가 다르고, 입력 이벤트처럼 개별 요소 상태와 강하게 연결된 경우에는 직접 listener가 더 단순할 수 있습니다.
패턴을 무조건 적용하기보다 이벤트 성격과 코드 복잡도를 같이 봐야 합니다.
React의 event handler도 event delegation과 관련이 있나요?
React는 내부적으로 event system을 통해 이벤트를 위임하고 합성 이벤트를 제공합니다. 최신 버전에서 구현 세부는 달라질 수 있지만, 여러 handler를 선언해도 React가 직접 DOM listener를 효율적으로 관리한다는 관점으로 이해하면 됩니다.
DOM event delegation 개념과 React의 event system은 구분해서 봐야 합니다.
부가 설명
const list = document.querySelector(".todo-list");list.addEventListener("click", (event) => { const button = event.target.closest("button[data-id]"); if (!button) return; console.log(button.dataset.id);});
각 button에 listener를 붙이지 않고 list 하나에서 처리합니다. closest를 쓰면 button 안의 icon이나 span을 클릭해도 원하는 button을 찾을 수 있습니다.
event delegation은 DOM event의 propagation을 이해하고 있어야 자연스럽게 설명할 수 있습니다. 이벤트는 capture 단계, target 단계, bubble 단계를 거칠 수 있고, 대부분의 UI 이벤트는 bubble됩니다.
다만 모든 이벤트가 bubble되는 것은 아니고, 너무 상위에 listener를 붙이면 관심 없는 이벤트까지 처리해야 해서 조건 분기가 복잡해질 수 있습니다.
한 줄 정리
event delegation은 bubbling을 이용해 부모 하나에서 여러 자식의 이벤트를 처리하는 패턴입니다.