9.1 이벤트 위임
JavaScript에서 이벤트 작업은 성능 최적화 및 애플리케이션 아키텍처 개선을 위해 복잡한 관리가 필요할 수 있어.
JavaScript에서 이벤트 작업의 최적화는 성능이 뛰어난 웹 애플리케이션을 만들기 위해 아주 중요해, 특히 많은 수의 요소를 다룰 때. 이 맥락에서 이벤트 위임과 불필요한 핸들러 방지는 중요한 역할을 해.
이벤트 위임은 이벤트 핸들러가 각 자식 요소가 아닌 부모 요소에 설정되는 패턴이야. 이걸 통해 많은 수의 자식 요소에 대한 이벤트를 효율적으로 관리할 수 있어, 왜냐하면 핸들러가 각 요소가 아닌 부모에 대해 한 번만 호출되기 때문이야.
이벤트 위임 없이의 예제:
<!DOCTYPE html>
<html>
<head>
<title>이벤트 위임 예제</title>
</head>
<body>
<ul id="list">
<li>요소 1</li>
<li>요소 2</li>
<li>요소 3</li>
</ul>
<script>
const list = document.querySelector('ul');
const items = document.querySelectorAll('#list li');
items.forEach(item => {
item.addEventListener('click', function(event) {
const li = document.createElement('li');
li.innerText = "이벤트가 작동하지 않는 새 요소";
list.appendChild(li);
alert(`당신은 ${event.target.textContent}을 클릭했습니다`);
});
});
</script>
</body>
</html>
문제: 요소 목록이 동적으로 증가하면 각 새로운 요소에 핸들러를 추가해야 해.
해결책 — 이벤트 위임 예제:
<!DOCTYPE html>
<html>
<head>
<title>이벤트 위임 예제</title>
</head>
<body>
<ul id="list">
<li>요소 1</li>
<li>요소 2</li>
<li>요소 3</li>
</ul>
<script>
const list = document.getElementById('list');
list.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
const li = document.createElement('li');
li.innerText = "이벤트가 작동하는 새 요소";
list.appendChild(li);
alert(`당신은 ${event.target.textContent}을 클릭했습니다`);
}
});
</script>
</body>
</html>
장점:
- 핸들러가 부모 요소에만 추가됨
- 동적으로 추가된 요소도 자동으로 이벤트 핸들러를 지원함
9.2 불필요한 핸들러 방지
성능 저하를 방지하기 위해서는 이벤트 핸들러의 수를 최소화하는 것이 중요해, 특히 많은 수의 요소에 붙어있거나 자주 호출되는 경우. 최적화 방법 몇 가지:
1. 핸들러 수 줄이기: 이벤트 위임을 사용하여 핸들러 수를 줄이세요.
2. addEventListener에서 once 사용하기: 이벤트 핸들러가 한 번만 작동해야 하는 경우, 핸들러 추가시 { once: true }
옵션을 사용하세요.
예제:
<!DOCTYPE html>
<html>
<head>
<title>한 번의 이벤트 예제</title>
</head>
<body>
<button id="myButton">나를 클릭하세요!</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', function(event) {
alert('버튼을 클릭했습니다');
}, { once: true });
</script>
</body>
</html>
3. 디바운싱 및 스로틀링: 이러한 기술은 scroll
또는 resize
와 같은 자주 호출되는 이벤트 핸들러를 최적화하는 데 유용해.
9.3 디바운싱
디바운싱은 여러 연속적인 함수 호출을 하나로 묶어, 이벤트 흐름이 멈춘 후에만 실행되도록 만듭니다.
예제:
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
window.addEventListener('resize', debounce(() => {
console.log('창 크기가 조정되었습니다');
}, 300));
결과를 보려면 위젯 크기를 변경해 보세요
디바운싱 예제
이 예제에서 검색 함수는 사용자가 입력을 멈춘 후 300밀리초 후에만 호출됩니다.
<!DOCTYPE html>
<html>
<head>
<title>디바운싱 예제</title>
</head>
<body>
<div style="min-height: 55px">
<input type="text" id="searchInput" placeholder="검색어를 입력하세요...">
<div id="results"></div>
</div>
<script>
const searchInput = document.getElementById('searchInput');
const results = document.getElementById('results');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function performSearch(event) {
const query = event.target.value;
results.textContent = '검색 중: ' + query;
// 서버에 요청을 시뮬레이션합니다
setTimeout(() => {
results.textContent = '검색 결과: ' + query;
}, 500);
}
const debouncedSearch = debounce(performSearch, 300);
searchInput.addEventListener('input', debouncedSearch);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>디바운싱 예제</title>
</head>
<body>
<input type="text" id="searchInput" placeholder="검색어를 입력하세요...">
<div id="results"></div>
<script>
const searchInput = document.getElementById('searchInput');
const results = document.getElementById('results');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function performSearch(event) {
const query = event.target.value;
results.textContent = 'Searching for: ' + query;
// 서버에 요청을 시뮬레이션합니다
setTimeout(() => {
results.textContent = '검색 결과: ' + query;
}, 500);
}
const debouncedSearch = debounce(performSearch, 300);
searchInput.addEventListener('input', debouncedSearch);
</script>
</body>
</html>
9.4 스로틀링
트롤링 스로틀링은 지정된 시간 간격으로 함수가 한 번 이상 호출되는 것을 보장합니다.
예제:
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
window.addEventListener('scroll', throttle(() => {
console.log('창이 스크롤되었습니다');
}, 300));
다른 스로틀링 예제
이 예제에서 스크롤 핸들러는 200밀리초마다 한 번씩만 호출되며, 이는 자주 발생하는 스크롤 이벤트로 인해 브라우저 과부하를 방지하는 데 도움이 됩니다.
<!DOCTYPE html>
<html>
<head>
<title>스로틀링 예제</title>
</head>
<body>
<div id="scrollContainer" style="height: 200px; overflow-y: scroll;">
<div style="height: 1000px;"></div>
</div>
<div id="status">효과를 보려면 스크롤을 시작하세요</div>
<script>
const scrollContainer = document.getElementById('scrollContainer');
const status = document.getElementById('status');
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
function handleScroll(event) {
status.textContent = '스크롤 중... ' + new Date().toLocaleTimeString();
}
const throttledScroll = throttle(handleScroll, 200);
scrollContainer.addEventListener('scroll', throttledScroll);
</script>
</body>
</html>
9.5 수동 리스너를 통한 이벤트 최적화
수동 이벤트 리스너 (passive event listeners)는 성능 향상에 사용됩니다, 특히 스크롤 (scroll
) 이벤트를 처리할 때. 이벤트 핸들러가 수동으로 설정되면 preventDefault()
를 호출하지 않겠다고 의미하며, 이는 브라우저가 성능을 최적화할 수 있도록 합니다.
예제:
window.addEventListener('scroll', function(event) {
console.log('스크롤 중');
}, { passive: true });
GO TO FULL VERSION