클릭 폭탄에서 해방되기: 이벤트 위임으로 코드 효율 + 성능 두 마리 토끼 잡기

LinkDropper·2025년 9월 19일

Link Dropper

목록 보기
11/15
post-thumbnail

웹/프론트엔드 개발을 하다 보면, 특히 UI가 복잡해지거나 요소가 동적으로 생성되면서 이벤트 핸들러 수가 폭발적으로 늘어나는 문제에 직면하곤 합니다.
링크, 폴더, 리스트, 버튼 등 반복적 요소에 하나하나 이벤트를 붙이는 방식은 단기적으로는 작지만, 장기적으로 보면 유지보수·성능·버그 관점에서 골치 아픈 원인이 됩니다.

이 글에서는 이벤트 위임(Event Delegation)의 개념, 왜 쓰는지, 언제 쓰면 좋고 언제 피해야 하는지, 실제 코드 예제와 함께 링크/폴더 구조 같은 상황에서 적용할 수 있는 구체적인 해결 방안을 다룹니다.


📌 이벤트 위임이란 무엇인가?

이벤트 위임(Event Delegation)은 다음 개념들의 조합을 활용합니다:

  1. 이벤트 버블링 (Event Bubbling)
    자식 요소에서 발생한 이벤트가 DOM 트리 상위로 전파(flows up)되는 특성입니다.

  2. 상위 요소에 이벤트 리스너를 한 번만 등록
    여러 하위 요소(each child)에 개별적으로 onClick/onDoubleClick 등을 붙이는 대신, 상위(wrapper 또는 컨테이너) 요소에 이벤트 리스너를 걸어두고, 이벤트가 상위까지 올라왔을 때 어느 자식에서 일어났는지 평가하여 처리합니다.

  3. 대상(target) 판별 로직
    event.target, event.target.closest(selector), matches(), dataset 속성 등을 활용해 클릭된 요소가 어떤 타입(예: 링크 vs 폴더 vs 특정 버튼)인지 구분합니다.


⚠️ 반복된 방식의 문제점: 왜 이벤트 위임이 필요했나

링크(link)와 폴더(folder)에 각각 onClick, onDoubleClick 등을 직접 붙이는 기존 방식이 가진 구체적인 단점들은 다음과 같습니다:

항목문제 상세
핸들러 중복 및 메모리 낭비동일한 함수/로직이 여러 요소에 중복되어 붙기 때문에, 메모리 사용량 증가. 특히 리스트 항목이 많고 동적으로 추가/제거 되는 경우 부담 커짐.
동적 요소 관리의 번거로움스크립트로 요소가 추가되면 또 이벤트를 붙여야 함. 실수로 누락되면 해당 새 요소는 작동 안 함.
가독성 저하 / 코드 산만화JSX나 HTML/템플릿에 이벤트 속성이 잔뜩 생김. 이벤트 로직이 요소마다 흩어져 있어 전체 흐름 파악이 어려움.
성능 저하 가능성브라우저가 많은 이벤트 리스너를 다루느라 렌더링이나 이벤트 처리 시 비용이 높아짐. 특히 모바일 기기나 저사양 환경에서 체감 가능.

링크+폴더 예제에서는 “링크 클릭” / “폴더 더블클릭” 등의 동작이 반복 요소마다 있고, 어떤 경우엔 폴더가 동적으로 추가되기도 할 것임. 이런 구조에서는 위임이 유리합니다.


🛠 위임 전략 적용하기: 링크·폴더 구조 예시

아래는 링크/폴더 구조에 이벤트 위임을 적용하는 실전 예제입니다.

구조 가정

  • 여러 개의 아이템(item)이 있고, 각각 아이템은 “링크” 혹은 “폴더” 타입
  • 유저가 링크 클릭 또는 폴더 더블클릭을 할 때 별도 동작 수행
  • 폴더 더블클릭은 ‘더블클릭’ 이벤트로, 링크는 단일 클릭

HTML / JSX 구조

<ul id="item-list">
  {items.map(item => (
    <li key={item.id}
        data-type={item.type}    // "link" 또는 "folder"
        data-id={item.id}>
      {item.type === "link"
        ? <a href={item.url} className="item-link"> {item.name} </a>
        : <span className="item-folder"> {item.name} </span>}
    </li>
  ))}
</ul>

이벤트 위임 코드 (클릭 + 더블클릭 분리)

const itemList = document.getElementById('item-list');

// 단일 클릭 처리
itemList.addEventListener('click', function(event) {
  const li = event.target.closest('li');
  if (!li) return;

  const type = li.dataset.type;
  const id   = li.dataset.id;

  if (type === 'link') {
    // 링크 클릭 동작
    console.log(`링크 클릭됨: ${id}`);
    // 여기서는 기본 동작(navigation) 허용하거나
    // event.preventDefault() 등 제어 가능
  }
  // 폴더 클릭 시 (폴더 단축 클릭) 다른 동작이 필요하다면 여기에
});

// 더블클릭 처리 (폴더 전용)
itemList.addEventListener('dblclick', function(event) {
  const li = event.target.closest('li');
  if (!li) return;

  const type = li.dataset.type;
  const id   = li.dataset.id;

  if (type === 'folder') {
    console.log(`폴더 더블클릭됨: ${id}`);
    // 폴더 더블클릭 처리
  }
});

추가 고려: 이벤트 간 충돌 처리

  • 단일 클릭과 더블클릭이 같은 요소에서 모두 가능할 때, 일반적으로 클릭 이벤트가 먼저 실행되고 더블클릭이 이어지는 구조여서 클릭 동작과 더블클릭 동작이 서로 간섭할 수 있음.

  • 해결책:

    1. 타이머 기반 분기
      더블클릭이 아닌 단일 클릭으로 보고 일정 시간 이후(예: 300ms) 클릭 로직 실행.

    2. 이벤트 취소
      더블클릭 이벤트가 발생했을 때 단일 클릭 이벤트의 실행을 막거나 무시하도록 로직 구성.

예시 (간단한 타이머 방식):

let clickTimer = null;
const clickDelay = 300;

itemList.addEventListener('click', function(event) {
  const li = event.target.closest('li');
  if (!li) return;

  const type = li.dataset.type;
  const id   = li.dataset.id;

  // 링크 단일 클릭만 처리
  if (type === 'link') {
    // 링크는 단일 클릭만 필요하다면 바로 처리
    handleLinkClick(id);
  } else if (type === 'folder') {
    // 폴더는 단일 클릭 vs 더블클릭 분기
    clearTimeout(clickTimer);
    clickTimer = setTimeout(() => {
      handleFolderSingleClick(id);
    }, clickDelay);
  }
});

itemList.addEventListener('dblclick', function(event) {
  const li = event.target.closest('li');
  if (!li) return;

  const type = li.dataset.type;
  const id   = li.dataset.id;

  if (type === 'folder') {
    clearTimeout(clickTimer);
    handleFolderDoubleClick(id);
  }
});

⚙ 이벤트 위임의 장·단점 & 실무적 고려사항

✅ 장점

  • 핸들러 수 감소 → 메모리 & 이벤트 리스너 관리 비용 절감
    반복되는 하위 요소가 많을수록 이득이 커짐. 동적 요소 생성에도 추가 처리 필요 없음.

  • 코드 일관성 / 유지보수성 향상
    이벤트 로직이 한 곳에 집중됨. 수정/추가/버그 수정 시 변경 지점을 최소화 가능.

  • 성능 개선
    많은 이벤트 리스너가 걸려 있는 경우, 브라우저 이벤트 처리 및 가비지 컬렉션 부담 감소.

  • 확장성
    요소 타입이 추가되더라도, 상위 로직 쪽에서 타입만 판별하여 처리하면 됨.

❌ 단점 / 주의할 점

항목설명
이벤트 버블링이 안 되는 경우stopPropagation()이 사용되었거나, Shadow DOM 내 특정 이벤트, 혹은 캡슐화된 컴포넌트 등에서는 이벤트가 상위로 전달되지 않을 수 있음.
이벤트 타입에 따른 충돌클릭/더블클릭/드래그/마우스 무브 등의 이벤트가 복합될 경우, 단일 이벤트와 더블클릭 간 타이밍 문제.
우선순위 / 제어권 문제특정 자식 요소에만 이벤트가 작동해야 할 경우(예: 보안적으로 중요한 버튼, 접근성 고려), 위임이 오히려 복잡도를 높일 수 있음.
이벤트 타깃 판별의 복잡성event.target이 자식 요소 안쪽의 태그일 경우, closest()나 커스텀 식별자(데이터 속성, 클래스) 설정 필요. 잘못 사용하면 의도치 않은 요소가 이벤트를 갖게 됨.

🔍 언제 위임 vs 직접 등록이 더 좋은가?

아래 상황들을 비교하며 판단하면 됩니다:

조건위임이 적합한 경우직접 등록이 나은 경우
반복적 요소 수수십 ~ 수백 개 이상 반복 요소 있음아주 소수이거나 단 하나뿐인 요소
동적 생성 여부새로운 요소가 자주 추가/삭제됨마크업이 고정되어 있고 자주 변하지 않음
이벤트 종류클릭, 더블클릭, 키보드 이벤트 등 버블링 가능한 이벤트focus, blur, submit(폼), scroll, touchmove 등 버블링 제한 혹은 민감한 제어 필요 이벤트
예상 충돌 가능성자식 간 이벤트 우선순위 충돌이 적거나 쉽게 구분 가능복잡한 UI 로직, 많은 상호작용, 제어 권한 필요 시 직접 붙이는 것이 명확함
성능 요구 수준저사양 환경(모바일, 오래된 브라우저), 대량 데이터 렌더링 필요단순 페이지, 요소 적음, 최적화보다 명확성이 더 중요한 경우

🔧 실무 적용 시 체크리스트

  • DOM 구조를 설계할 때 식별자(identifier) 설정: 클래스명, data- 속성, 태그명 등을 미리 정리해두면 이벤트 타깃 판별이 쉬움
  • closest()matches() 사용법 숙지
  • 더블클릭 vs 클릭 분기 필요 시 타이밍, 이벤트 취소(logic) 고려
  • stopPropagation(), stopImmediatePropagation() 등이 이벤트 흐름을 끊는 곳이 없는지 점검
  • Shadow DOM, 웹 컴포넌트 사용 시 이벤트 전파 특성 이해
  • 개발자 도구(Chrome DevTools 등)의 Performance / Memory 탭을 활용해 핸들러 수, 이벤트 처리 지연 등의 지표 측정

✅ 결론

  • 이벤트 위임은 반복적이고 동적인 UI 구성에서 성능, 유지보수, 확장성을 개선해 주는 매우 유용한 패턴입니다.
  • 하지만 모든 경우에 만능은 아니므로, 위에서 언급한 고려사항들을 기반으로 “언제 위임할 것인가, 언제 직접 이벤트를 붙일 것인가”를 판단하는 것이 중요합니다.
  • 특히 링크 + 폴더 구조처럼 반복되는 항목이 많고 사용자 인터랙션 종류가 제한적이라면, 지금 바로 이벤트 위임으로 리팩터링 해보시는 걸 추천드립니다.

🧪 링크 드라퍼, 지금 베타 테스트 중입니다

링크 드라퍼는 단순한 저장 툴이 아닙니다.
정리하고, 수정하고, 다시 꺼내보게 만드는 링크 관리 도구를 지향하고 있습니다.

• 🔗 빠르고 간편한 링크 저장
• 🧠 저장한 링크를 폴더별로 정리
• 🌐 폴더를 친구에게 공유 가능
• ⚡ 크롬 익스텐션 원클릭 저장

👉 링크 드라퍼 사용하러 가기
👉 크롬 웹스토어에서 설치하기

💬 카카오톡 채널 추가하고 소식 받기

서비스 업데이트
기능 꿀팁
카카오톡 채널을 통해 빠르게 받아보세요!
👉 카카오톡 채널 추가하기

profile
“기록하는 습관을 도구로 만들다 — 두 개발자의 링크 드라퍼 구축기”

0개의 댓글