티스토리 뷰
[챗지피티와 공부를 해보자] 이벤트 위임(Event Delegation)과 성능 최적화
이벤트 위임 (Event Delegation)
하위 요소에 개별적으로 이벤트 리스너를 추가하는 대신에 상위 요소에서 하나의 이벤트 리스너를 통해 이벤트를 감지하여 하위요소들의 이벤트를 처리하는 기법이다.
자바스크립트의 이벤트 버블링(Event Bubbling)을 이용하여 이벤트가 부모 요소까지 전파하는 특성을 활용했으며, 이벤트가 발생한 하위요소에서 직접 핸들러를 실행하는 것이 아니라 부모요소에서 이벤트를 감지하여 실행하는 방식이다.
예제
// 개별 이벤트 리스너
document.querySelectorAll('.btn').forEach((button) => {
button.addEventListener('click', () => {
console.log('버튼 클릭!');
});
});
// 이벤트 위임 리스너
document.querySelector('.button-container').addEventListener('click', (event) => {
if (event.target.classList.contains('btn')) {
console.log('버튼 클릭!');
}
});
이벤트 위임을 활용하는 이점과 성능 최적화 효과
- 메모리 절약 (이벤트리스너 개수 감소)
- 개별 요소마다 addEventListener를 추가하면 많은 이벤트 리스너가 등록되므로 메모리 사용량이 증가하고 이는 성능 저하를 발생할 수 있다.
- 이벤트 위임을 사용하면 부모 요소 하나에만 리스너를 등록하면 되므로 불필요한 리소스 낭비를 줄일 수 있다.
- 동적으로 생성되는 요소에도 적용 가능
- DOM 요소가 동적으로 추가되는 경우 addEventListener의 동작 방식으로 인해 새로 추가된 요소에는 이벤트가 적용되지 않는데, 이때 이벤트 위임을 사용하면 동적으로 생성된 요소도 자동으로 이벤트를 감지할 수 있다.
- 코드 유지 보수성과 성능 향상
- 중복된 이벤트 핸들러를 여러 요소에 추가할 필요가 없어서 코드가 간결해지고, 유지보수하기 쉬워진다.
- 부모요소에서 이벤트를 처리하면 불필요한 이벤트 리스너가 제거되면서 성능이 향상된다.
Q. addEventListener의 동작 방식
A. addEventListener는 DOM이 현재 존재하는 요소에만 이벤트 리스너를 바인딩한다.
스크립트가 실행될때 DOM에 존재하는 요소에 대해서만 이벤트가 등록된다. 따라서 동적으로 추가된 요소에는 이벤트가 적용되지 않는다.
언제 사용하면 좋을까?
- 동적으로 추가되는 요소들에 이벤트를 적용하고 싶을때 (리스트 등)
- 이벤트 핸들러를 등록해야하는 요소가 많을때 (테이블, 카드 등)
- 사용자 입력을 중앙에서 처리할 때 (폼, 필터 등)
- 이벤트 핸들러의 실행 횟수를 최소화 할 때 (성능 최적화 목적)
사용자 입력을 중앙에서 처리할 때 예시
// 개별 처리
document.querySelector('#name').addEventListener('input', (event) => {
console.log(`Name: ${event.target.value}`);
});
document.querySelector('#email').addEventListener('input', (event) => {
console.log(`Email: ${event.target.value}`);
});
// 이벤트 위임
document.querySelector('#form').addEventListener('input', (event) => {
const target = event.target;
if (target.tagName === 'INPUT') {
console.log(`${target.name}: ${target.value}`);
}
});
React에서의 이벤트 위임
React에서는 Virtual DOM이 있기 때문에 이벤트 위임이 자동으로 최적화 된다.
그럼에도, 각 요소에 이벤트를 직접 추가하는 것보다 부모 요소에서 관리하는 것이 더 유리할 수 있다. (유지보수?)
// 비효율적인 방법 (개별 리스너)
function ButtonList({ items }) {
return (
<div> {
items.map((item, index) => (
<button key={index} onClick={() => console.log(item)}>클릭</button>
))}
</div>
);
}
// 이벤트 위임 적용 (부모 요소에서 핸들링)
function ButtonList({ items }) {
const handleClick = (event) => {
if (event.target.tagName === 'BUTTON') {
console.log(event.target.innerText);
}
};
return (
<div onClick={handleClick}> {
items.map((item, index) => (
<button key={index}>{item}</button>
))}
</div>
);
}
이벤트 위임의 한계와 주의점
- 부모 요소에서 이벤트를 감지할 때 많은 요소를 탐색해야하는데, 이때 자식 요소가 많으면 오히려 성능 저하가 발생할 수 있다. 최대한 작은 범위의 부모 요소에서 위임을 처리하는 것이 좋다.
- 자식 요소의 특정 요소에서만 이벤트를 처리해야하는 경우에는 개별 리스너가 더 적합할 수 있다.
- 버블링 되지 않는 이벤트들은 이벤트 위임이 불가능 하다. (ex: focus, blur 등)
Q. 'React에서는 Virtual DOM이 있기 때문에 이벤트 위임이 자동으로 최적화 되는 경우가 많다'라는게 어떤 경우이고 뭐를 의미해?
A. 이는 React의 합성 이벤트(Synthetic Event) 시스템 덕분이다.
React에서는 Virtual DOM을 활용하여 이벤트 위임이 자동으로 최적화되는 경우가 많다.
이는 React의 합성 이벤트(Synthetic Event) 시스템 덕분이며, 합성 이벤트 시스템을 사용하여 브라우저의 네이티브 이벤트를 감싸고 관리한다.
일반적인 브라우저 환경에서는 addEventListener를 각 요소에 개별적으로 추가하는데, React에서는 Virtual DOM을 활용하여 모든 이벤트를 루트 컨테이너 중앙에서 관리한다.
React의 이벤트 시스템의 특징으로, 컴포넌트 내에서 개별적으로 이벤트 핸들러를 어디에 바인딩하더라도 이벤트를 최상위 요소(document or root)에서 한번만 등록 처리된다. 이후 React는 이벤트 버블링을 이용해 해당 요소로 전달하는 방식을 사용하고 있다.
즉, React는 이벤트 위임을 자동으로 수행하여 성능을 최적화 하고 있다.
합성 이벤트 (Synthetic Event) 특징
- 모든 이벤트는 React의 이벤트 풀(Pooling System)에서 관리
- 이벤트는 브라우저의 document나 루트 요소에서 한 번만 바인딩
- 이벤트 객체(event)는 메모리 최적화를 위해 재사용 가능하도록 설계됨
- React의 이벤트 핸들러는 브라우저 호환성을 보장하도록 표준화됨
일반적인 DOM 이벤트 처리 방식 (비효율)
이벤트 리스너를 각각 추가해야 하므로, 메모리 낭비 및 성능 저하가 발생할 수 있다.
버튼이 많으면 많을수록 이벤트 리스너가 계속 증가하여 유지보수 또한 어려울 수 있다.
document.querySelector('.btn1').addEventListener('click', () => {
console.log('Button 1 clicked');
});
document.querySelector('.btn2').addEventListener('click', () => {
console.log('Button 2 clicked');
});
React의 이벤트 위임 방식 (최적화)
부모요소 한 곳에만 onClick을 등록하고, 내부 버튼 클릭을 감지한다. (=모든 이벤트는 최상위 컨테이너에서 한 번만 처리)
불필요한 이벤트 리스너를 줄여 메모리 사용량 감소한다.
버튼이 많아져도 별도의 이벤트 등록이 필요 없다.
function ButtonGroup() {
return (
<div onClick={(event) => console.log(`Button ${event.target.textContent} clicked`)}>
<button className="btn1">1</button>
<button className="btn2">2</button>
</div>
);
}
'개념 > AI와 함께' 카테고리의 다른 글
[챗지피티와 공부를 해보자] 가상돔(Virtual DOM)과 React Fiber 구조 (0) | 2025.03.26 |
---|---|
[챗지피티와 공부를 해보자] 이벤트 루프와 Web APIs의 관계 (0) | 2025.03.06 |
[챗지피티와 공부를 해보자] 프로토타입 체인 (0) | 2025.02.25 |
[챗지피티와 공부를 해보자] 프로미스 체이닝 (Promise Chaining) (0) | 2025.02.12 |
[챗지피티와 공부를 해보자] 원시값(Primitive Value)과 참조값(Reference Value), 객체 복사(얕은 복사 Shallow Copy, 깊은 복사 Deep Copy) (0) | 2025.02.08 |
[챗지피티와 공부를 해보자] 이터러블(iterable)과 이터레이터(iterator) (0) | 2025.02.06 |
[챗지피티와 공부를 해보자] 구조분해할당 (Destructuring) (0) | 2025.02.03 |
[챗지피티와 공부를 해보자] 실행 컨텍스트(Execution Context) (2) | 2025.01.30 |