이벤트 위임(Event Delegation), 왜 필요한 걸까?

김진아·2026년 4월 22일


JavaScript로 버튼 클릭 기능을 만들다 보면 보통 이렇게 시작한다.

const button = document.querySelector("button");

button.addEventListener("click", () => {
  console.log("클릭됨");
});

버튼이 하나일 때는 전혀 문제 없다.
그런데 목록이 생기고, 삭제 버튼이 여러 개 생기고, 나중에 새로운 항목이 추가되기 시작하면 점점 코드가 번거로워진다.

이럴 때 자주 등장하는 개념이 이벤트 위임(Event Delegation) 이다.
그런데 이벤트 위임을 제대로 이해하려면, 먼저 이벤트가 브라우저에서 어떻게 전달되는지부터 알아야 한다.


이벤트는 어떻게 전달될까?

어떤 요소를 클릭했다고 해서 이벤트가 그 요소에서만 끝나는 건 아니다.
브라우저에서는 이벤트가 특정한 흐름을 따라 전달된다.

이때 자주 같이 나오는 개념이 3가지 있다.

  • 캡처링(Capturing)
  • 타깃(Target)
  • 버블링(Bubbling)

이 세 단계를 거쳐 이벤트가 전달된다고 보면 된다.


1. 캡처링(Capturing)

캡처링은 이벤트가 바깥쪽 조상 요소에서 시작해서, 점점 안쪽 자식 요소로 내려가는 과정이다.

예를 들어 구조가 이렇게 있다고 해보자.

<div class="outer">
  <div class="inner">
    <button>클릭</button>
  </div>
</div>

버튼을 클릭했을 때 이벤트는 먼저 가장 바깥쪽에서부터 내려온다.

대충 이런 느낌이다.

  • document
  • html
  • body
  • .outer
  • .inner
  • button

이 내려오는 단계가 캡처링이다.

다만 우리가 평소에 addEventListener()를 사용할 때는 대부분 이 캡처링을 직접 의식하지 않는다.
기본값이 보통 버블링 단계에서 동작하도록 되어 있기 때문이다.


2. 타깃(Target)

이벤트가 실제로 발생한 요소에 도착한 순간이 타깃 단계다.

위 예시에서는 사용자가 실제로 클릭한 요소가 button이므로,
이 이벤트의 타깃은 button이다.

여기서 중요한 것이 바로 event.target이다.

button.addEventListener("click", (event) => {
  console.log(event.target);
});

event.target실제로 이벤트가 발생한 요소를 의미한다.

즉,

  • 버튼을 클릭하면 event.target은 버튼
  • 버튼 안에 span이 있다면 그 span을 클릭했을 때는 span

이 될 수 있다.

이 값은 이벤트 위임에서 정말 중요하게 쓰인다.


3. 버블링(Bubbling)

타깃에 도착한 이벤트는 거기서 끝나지 않고 다시 바깥쪽 부모로 올라간다.
이 과정을 버블링이라고 한다.

즉 버튼을 클릭하면 이벤트는 다시 이렇게 올라간다.

  • button
  • .inner
  • .outer
  • body
  • html
  • document

이 “위로 올라가는” 성질 덕분에, 부모 요소도 자식 요소의 클릭을 감지할 수 있다.

그리고 바로 이 버블링을 이용한 방식이 이벤트 위임이다.


이벤트 위임이란?

이벤트 위임은 자식 요소마다 이벤트를 붙이지 않고, 부모 요소 하나에 이벤트를 등록해서 처리하는 방식이다.

예를 들어 삭제 버튼이 여러 개 있는 목록이 있다고 해보자.

<ul id="todo-list">
  <li>
    공부하기
    <button class="delete-button">삭제</button>
  </li>
  <li>
    산책하기
    <button class="delete-button">삭제</button>
  </li>
</ul>

보통은 버튼마다 하나씩 이벤트를 붙이고 싶어진다.

const buttons = document.querySelectorAll(".delete-button");

buttons.forEach((button) => {
  button.addEventListener("click", () => {
    console.log("삭제");
  });
});

처음에는 잘 동작한다.
하지만 나중에 li를 새로 추가하면, 새로 생긴 버튼에는 이 이벤트가 자동으로 붙지 않는다.

왜냐하면 이 코드는 현재 존재하는 버튼들만 찾아서 이벤트를 등록했기 때문이다.


그래서 부모에 한 번만 등록한다

이벤트 위임을 사용하면 부모 요소인 ul에만 이벤트를 등록하면 된다.

const todoList = document.querySelector("#todo-list");

todoList.addEventListener("click", (event) => {
  if (event.target.classList.contains("delete-button")) {
    event.target.parentElement.remove();
  }
});

이 코드가 가능한 이유는 간단하다.

  • 실제 클릭은 버튼에서 일어난다
  • 그 이벤트가 버블링되어 ul까지 올라온다
  • ul이 그 클릭을 듣고 처리한다

즉, 버튼이 아니라 부모가 대신 처리하는 것이다.


이벤트 위임에서 event.target이 중요한 이유

부모에 이벤트를 걸면 그 안에서 일어나는 모든 클릭이 올라온다.
그렇다면 부모는 어떻게 “삭제 버튼을 누른 것인지”, “그냥 리스트를 누른 것인지” 구분할까?

그때 쓰는 것이 event.target이다.

todoList.addEventListener("click", (event) => {
  console.log(event.target);
});

실제로 클릭된 요소를 보고, 원하는 대상인지 검사하는 방식이다.

if (event.target.classList.contains("delete-button")) {
  // 삭제 버튼일 때만 실행
}

즉 이벤트 위임은 이렇게 이해하면 쉽다.

부모는 이벤트를 듣고 있고,
event.target으로 실제 클릭된 자식을 확인한 뒤 필요한 동작만 실행한다.


그런데 클릭한 게 버튼 안쪽 요소라면?

실제 프로젝트에서는 버튼 안에 글자나 아이콘이 들어가는 경우가 많다.

<button class="delete-button">
  <span>삭제</span>
</button>

이 상황에서 사용자가 span을 클릭하면 event.targetbutton이 아니라 span이 될 수 있다.

그러면 이런 코드는 놓칠 수 있다.

if (event.target.classList.contains("delete-button")) {
  ...
}

그래서 이럴 때는 closest()를 자주 쓴다.


closest()를 같이 쓰면 더 안전하다

todoList.addEventListener("click", (event) => {
  const deleteButton = event.target.closest(".delete-button");

  if (!deleteButton) return;

  deleteButton.parentElement.remove();
});

closest()는 현재 요소에서 시작해서, 가장 가까운 조상 중 조건에 맞는 요소를 찾아준다.

즉,

  • 버튼 자체를 눌러도 찾을 수 있고
  • 버튼 안의 span을 눌러도 찾을 수 있다

그래서 이벤트 위임에서는 event.targetclosest()를 함께 쓰는 경우가 많다.


왜 이벤트 위임이 특히 유용할까?

1. 반복되는 요소를 한 번에 관리할 수 있다

삭제 버튼이 100개 있어도 부모 하나만 관리하면 된다.

2. 나중에 추가된 요소도 동작한다

이게 정말 중요하다.
예를 들어 목록에 새 항목을 추가해도:

todoList.innerHTML += `
  <li>
    장보기
    <button class="delete-button">삭제</button>
  </li>
`;

삭제 버튼이 따로 다시 이벤트 등록을 하지 않아도 그대로 동작한다.

부모는 그대로이고, 이벤트는 계속 버블링되어 올라오기 때문이다.

3. 코드가 덜 복잡해진다

비슷한 이벤트 처리 로직이 여기저기 퍼지지 않고, 한 곳에 모이게 된다.
그래서 유지보수도 더 쉬워진다.


언제 많이 쓸까?

이벤트 위임은 특히 이런 상황에서 자주 쓴다.

  • 투두 리스트
  • 댓글 목록
  • 장바구니 목록
  • 테이블 행 클릭
  • 수정 / 삭제 버튼이 반복되는 UI

반대로 버튼 하나만 있는 간단한 화면이라면, 굳이 이벤트 위임을 쓸 필요는 없다.


정리

이벤트는 브라우저에서 보통 다음 흐름으로 전달된다.

  1. 캡처링: 바깥 조상에서 안쪽 요소로 내려옴
  2. 타깃: 실제 이벤트가 발생한 요소에 도착
  3. 버블링: 다시 부모 방향으로 올라감

그리고 이벤트 위임은 이 중 버블링을 활용한 방식이다.

부모 요소 하나에 이벤트를 걸어두고,
event.target으로 실제 클릭된 요소를 확인해서 필요한 동작만 처리한다.

처음에는 버튼마다 직접 이벤트를 붙이는 방식이 더 익숙하지만,
동적으로 요소가 추가되는 화면을 만들기 시작하면 이벤트 위임이 왜 필요한지 확실히 느끼게 된다.

나도 처음엔 그냥 “부모에 걸면 편하구나” 정도로만 생각했는데,
실제로 목록 UI를 만들수록 이 방식이 훨씬 자연스럽고 관리하기 쉽다는 걸 느꼈다.

특히 삭제, 수정처럼 반복되는 버튼이 있는 화면이라면
이벤트 위임은 거의 한 번쯤 꼭 만나게 되는 개념인 것 같다.

0개의 댓글