🎯 JavaScript Event Delegation 시뮬레이터
이벤트 위임을 통한 효율적인 이벤트 처리를 시각적으로 학습해보세요
🎯 이벤트 처리 모드
다른 방식으로 이벤트를 처리해보고 차이점을 확인해보세요
🔄 이벤트 전파 시뮬레이션
Event Capturing과 Bubbling 단계를 시각적으로 확인하세요
🏠 DOM 구조 시각화
📊 성능 메트릭
📝 실시간 이벤트 로그0개
🧪 실험해보기
🔬 실험 1: 성능 비교
- 개별 리스너 모드로 전환
- 새 Todo를 10개 이상 추가
- 성능 메트릭에서 리스너 개수 확인
- 위임 모드로 전환하여 비교
⚡ 실험 2: Bubbling 전파
- "Bubbling만" 모드 선택
- Todo 아이템 클릭
- 이벤트 로그에서 Bubbling 단계만 확인
- 하이라이트 애니메이션 관찰
🔄 실험 3: Capturing + Bubbling
- "Capturing + Bubbling" 모드 선택
- Todo 아이템 클릭
- 3단계 전파 과정 관찰
- 각 단계별 로그와 하이라이트 확인
📚 학습 가이드: Event Delegation
실제 코드와 함께 이벤트 위임의 작동 원리를 이해해보세요
🔍 이벤트 전파와 위임의 작동 원리
📊 이벤트 전파 3단계
⚡ Event Delegation 원리
- 이벤트 전파 활용: Capturing 또는 Bubbling 단계에서 부모 요소가 자식의 이벤트를 감지합니다.
- 단일 리스너 등록: 부모 컨테이너에 하나의 이벤트 리스너만 등록합니다.
- 타겟 식별: event.target으로 실제 클릭된 요소를 확인하고 event.currentTarget으로 리스너가 등록된 요소를 확인합니다.
- 동적 처리: 새로 추가된 요소도 자동으로 이벤트 처리가 가능합니다.
- 메모리 효율: 개별 리스너 대신 하나의 리스너로 모든 이벤트를 처리합니다.
💻 핵심 코드 분석
❌ 개별 이벤트 리스너 방식
// 각 Todo 아이템마다 리스너 등록
todoItems.forEach(item => {
const element = document.getElementById(item.id);
// 완료 버튼 리스너
const completeBtn = element.querySelector('.complete');
completeBtn.addEventListener('click', handleComplete);
// 삭제 버튼 리스너
const deleteBtn = element.querySelector('.delete');
deleteBtn.addEventListener('click', handleDelete);
});
// 문제점:
// - 요소마다 여러 개의 리스너 필요
// - 메모리 사용량 증가
// - 동적 요소에 수동으로 리스너 추가 필요✅ Event Delegation 방식
// 부모 컨테이너에 하나의 리스너만 등록
const todoContainer = document.getElementById('todoContainer');
// Bubbling 단계에서 처리 (기본값)
todoContainer.addEventListener('click', (e) => {
const target = e.target; // 실제 클릭된 요소
const currentTarget = e.currentTarget; // 리스너가 등록된 요소
const todoId = target.closest('[data-todo-id]')?.getAttribute('data-todo-id');
if (target.classList.contains('complete-btn')) {
handleComplete(todoId);
} else if (target.classList.contains('delete-btn')) {
handleDelete(todoId);
}
}, false); // false = Bubbling 단계 (기본값)
// Capturing 단계에서 처리하려면:
todoContainer.addEventListener('click', (e) => {
// 이벤트가 자식으로 전파되기 전에 먼저 처리됨
console.log('Capturing phase:', e.currentTarget);
}, true); // true = Capturing 단계
// 장점:
// - 하나의 리스너로 모든 이벤트 처리
// - 메모리 효율적
// - 동적 요소 자동 처리
// - Capturing/Bubbling 단계 선택 가능🎯 주요 개념
🔽 Event Capturing
이벤트가 Document에서 시작해서 타겟 요소까지 내려가는 과정입니다. addEventListener의 세 번째 매개변수를 true로 설정하면 활용할 수 있습니다.
🔼 Event Bubbling
이벤트가 타겟 요소에서 시작해서 Document까지 올라가는 과정입니다. 기본 동작이며, Event Delegation에서 주로 활용됩니다.
🎯 event.target vs currentTarget
target은 실제 클릭된 요소, currentTarget은 이벤트 리스너가 등록된 요소입니다. 위임에서는 target으로 실제 클릭 요소를 확인합니다.
⚡ 성능 최적화
하나의 이벤트 리스너로 여러 요소의 이벤트를 처리하여 메모리 사용량을 줄이고 성능을 향상시킬 수 있습니다.
🔄 동적 요소 처리
새로 추가된 요소에 별도의 이벤트 리스너를 등록할 필요 없이 자동으로 이벤트 처리가 가능합니다.
🎪 useCapture 매개변수
addEventListener(event, handler, useCapture)에서 useCapture가 true면 Capturing 단계, false면 Bubbling 단계에서 처리됩니다.
🌟 실제 사용 사례
📋 1. Todo 리스트
동적으로 추가/삭제되는 Todo 아이템들의 이벤트를 효율적으로 처리
// 테이블 행 클릭 처리
table.addEventListener('click', (e) => {
if (e.target.matches('.edit-btn')) {
editRow(e.target.closest('tr'));
}
});📊 2. 데이터 테이블
대량의 테이블 행에서 편집, 삭제 버튼 이벤트 처리
// 내비게이션 메뉴
nav.addEventListener('click', (e) => {
if (e.target.matches('a[data-page]')) {
loadPage(e.target.dataset.page);
}
});🎮 3. 게임 인터페이스
게임 보드의 여러 타일이나 버튼들의 클릭 이벤트 처리
// 게임 보드
gameBoard.addEventListener('click', (e) => {
const tile = e.target.closest('.tile');
if (tile) handleTileClick(tile);
});🏪 4. 쇼핑몰 상품 목록
상품 카드의 장바구니 추가, 찜하기 등의 버튼 이벤트 처리
// 상품 목록
productGrid.addEventListener('click', (e) => {
const productId = e.target.closest('[data-product-id]')?.dataset.productId;
if (e.target.matches('.add-to-cart')) {
addToCart(productId);
}
});🚀 성능 최적화 팁
✅ Best Practices
- 가장 가까운 공통 부모에 리스너 등록
- event.target으로 정확한 요소 식별
- closest() 메서드로 조상 요소 찾기
- 이벤트 타입별로 적절히 분리
⚠️ 주의사항
- 이벤트 전파가 중단되면 작동 안 함
- 너무 상위 요소에 등록하면 성능 저하
- 복잡한 선택자는 성능에 영향
- 필요한 경우 stopPropagation() 사용
1nnovator 김민성
JavaScript 학습 센터 개발자
🌟 이 프로젝트가 도움이 되셨다면 블로그에서 더 많은 개발 이야기를 확인해보세요!
🤖이 페이지는 생성형 AI의 도움을 받아 제작되었습니다.