안녕하세요호
웹 YB 김고은입니다.
이번주는 자바스크립트 딥다이브가 아닌 특별한 주제로 아티클을 작성하게 되었습니다.
이벤트 전파 및 이벤트 제어와 디바운스 그리고 스로틀을 다루고자 하는데, 기존과 달리 개념 위주보다는 예제와 함께 설명하면서
어떤식으로 이걸 활용해볼 수 있는지를 중심으로 이번주차 정리해보겠습니다.
이벤트 전파 및 이벤트 제어
이벤트 전파는 다음 2가지 양상으로 발생되게 됩니다.
☑️ 이벤트 버블링 (event bubbling) & 캡처링 (capturing)
이벤트 버블링이란 한 요소에 이벤트가 발생하면 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하고 최상단의 부모 요소를 만날 때까지 반복되면서 핸들러가 동작하는 현상 (마치 물속에서 거품이 위로 올라가는 듯해서 버블링이라고 한다)
쉽게 말해서, 자식요소 → 부모 요소로 전파!
캡처링은 버블링과는 반대로 최상위 태그에서 해당 태그를 찾아 내려감
근데 이 둘의 관계는 독립적이지 않는다는 사실!
Element 는 DOM 트리 내부에서, 누군가의 자식인 동시에, 부모이기도 하기 때문에 둘의 과정이 순차적으로 일어나기도 한다.
이때 순서는 다음과 같다.
캡처링 단계 => 타겟 단계 => 버블링 단계
단순 설명보단 코드를 보면 더 간단하다!
(마법의 소라챗둥 .. 지피티가 보여주는 한눈에 보이는 예제 )
<!DOCTYPE html>
<html>
<body>
<div id="parent">
<button id="child">딸깍 클릭 버튼</button>
</div>
</body>
</html>
document.addEventListener('click', () => console.log('Document - Capturing'), true);
document.addEventListener('click', () => console.log('Document - Bubbling'));
const parentDiv = document.getElementById('parent');
parentDiv.addEventListener('click', () => console.log('Div - Capturing'), true);
parentDiv.addEventListener('click', () => console.log('Div - Bubbling'));
const childButton = document.getElementById('child');
childButton.addEventListener('click', () => console.log('Button - Capturing'), true);
childButton.addEventListener('click', () => console.log('Button - Bubbling'));
만약 버튼을 누르게 된다면 ~~
가장 먼저 캡쳐링의 발생으로 인해, 최상위 요소인 document를 타고 내려와서 -> div 를 거쳐 -> button 으로 핸들러가 동작하게 된다
->어라, 그러면 완전 자손쪽 요소이면 최상위 요소인 document 부터 다 작동된다고요? 저 못본거 같은데 특히, 버블링은 막을 수 있는 메소드가 있었던것 같은데 캡쳐링은 어떻게 막나요?
사실 이벤트 리스너의 3번째 인수는, 캡처링 여부에 대한 인자로 디폴트로 false 이기 때문에 캡처링이 되지 않는게 일반적
다음으로 타켓인 버튼이 활성화되고,
이제, 버블링으로 다시금, 부모 요소로 이벤트가 전파되기 된다.
콘솔의 결과를 보면 다음과 같다.
Document - Capturing
Div - Capturing
Button - Capturing
Button - Bubbling
Div - Bubbling
Document - Bubbling
여기에서 잠깐, 버블링 막는 방법은 다음과 같다!
이벤트 전파 막기 (버블링 막기)
event.stopPropagation()
해당 타깃에서 document 객체를 만날 때까지 핸들러가 모두 호출되는데 코드를 작성하다보면 원하는 타깃에서만 이벤트를 발생하게 하고 싶을때 클릭한 타깃의 이벤트만 발생하고 상위 요소로 이벤트가 전파되는 것을 막을 수 있다.
이벤트 전파 그래서 어디에서 사용하는데 ~?
checkboxes.forEach((box) => {
box.addEventListener('click', checkingTop);
});
각 row의 checkbox를 반복문으로 돌려서, 이벤트 핸들러를 설정해줬었는데요
tableBody.addEventListener('click', checkingTop);
이벤트 위임 중, 버블링을 사용하여 모든 체크 박스에 이벤트를 달아줄 필요없이 테이블 전체에 걸어주게 되면
자식인 버튼에게 이벤트가 전파되기 때문에 더 효율적인 코드가 된다.
디바운스와 스로틀
둘다 이벤트를 제어해서 성능 최적화에 도움을 주는 기능이다. 먼저 디바운스부터 알아보자면,
디바운스
특정 이벤트가 연속해서 발생할때 에 , 지연시간을 부여해서 일정 시간 동안
이벤트가 더 발생하지 않으면 마지막 이벤트를 실행시키는 기능!
불필요한 호출 완전히 제거할 수 있다는 점에서 성능 최적화가 가능하다 (useCallback 과 유사)
스로틀
특정 이벤트가 연속해서 발생할 때, 일정 간격으로 한 번씩만 이벤트를 실행
마찬가지로 호출 빈도를 낮춰줄 수 있기에 성능적으로 이득을 볼 수가 있다.
디바운스를 통한, 유효성 검사
const handleChange = (e) => {
setJoinState('');
const { name, value } = e.target;
joinFormRef.current[name] = value;
debounceValidate(name, value);
};
// 300ms 이상 멈추면 유효성 검사 반영
const debounceValidate = useCallback(
debounce((name, value) => {
const errorMsg = validateField(name, value, joinFormRef.current);
setErrors((prev) => ({
...prev,
[name]: errorMsg,
}));
}, 300),
[],
);
onChange로 이벤트를 걸었기 때문에, 입력어가 작성되는 도중에 handleChange로 계속 호출이 되지만 유효성 검사의 경우
오류 메시지가 빨간 글씨로! 출력되기 때문에 유저가 아직 필드를 다 입력하지 않았을때,
자칫 오류 메시지를 띄우는 것을 방지하고자 디바운스를 사용했다!
추가로, usecallback 를 활용해서, 다른 필드에서도 사용되는 handleChange 안에, debounceValidate가 재생성되지 않도록
함수 메모이제이션했다.
'자스핑' 카테고리의 다른 글
[week 06] 이벤트의 모든 것 🎪 (0) | 2024.12.11 |
---|---|
[Week 06] 이벤트 전파 및 이벤트 제어 & 디바운스와 스로틀 (0) | 2024.12.11 |
[Week 05] 프로토타입과 클래스 상속, 객체지향 (0) | 2024.12.04 |
[week 05] 프로토타입 (0) | 2024.12.04 |
[Week 05] 프로토타입과 클래스 상속 (2) | 2024.12.04 |