Front 개발을 하다보면 click
,keyboard
등등의 event
를 숙명적으로 사용하게 된다.
근데 이것들을 사용하다보면 이벤트가 두번 씩 실행되거나, 버블링 같은 다양한 문제를 직면하게 된다.
그리고 이벤트를 발생시키는 방법이 addEventListner
, onclick
두 가지가 있고 다른 글을 찾아봤을 때 onclick
을 사용할 땐 버블링을 막지 못하니 addEventListenr
를 사용하는게 좋다는 글들이 주로 보였다. 그래서 나도 계속 그렇게 사용을 해왔었다.
근데 최근에onclick
도 동일하게 막을 수 있고, Event
를 생성할 때 버블링을 잘 활용하면 Memory
의 사용을 줄일 수 있겠다는 생각이 들어서 테스트를 진행해보았다.
개발을 하면서 마주한 여러가지 상황에 대해서 서술을 해보려고 한다.
Event
생성은 누구나 알듯이 addEventListener
, onclick
등이 있다.
그리고 Event를 다루다보면 필연적으로 버블링(Bubbling)
현상과 태그의 기본 실행
같은 걸 마주한다.
버블링은 쉽게 이야기하면 옛날 오락실게임 '보글보글'을 생각하면 된다.
하나의 Tag에서 이벤트가 발생하면 거품처럼 하늘로 올라갈때까지 계~속 올라가는현상이다.
아래 코드펜의 코드를 보면 모든 tag
에 click Event (addEventListener)
를 걸어두었다.
제일 아래의 button
div를 눌리면 거품의 시작은 button
이고
nav
에서 클릭을 하면 nav
에서 부터 거품이 시작된다.
왜냐하면 button
를 눌렀다는것으느 btnWrap
을 눌린 것이기도하고 container
를 눌린것이기도 하고
html
을 눌린 것이기도 하고 window
를 눌린것이기도 하기 때문이다.
비유해서 설명하면 철수네 가족에는 아들 훈이가 있는데, 길가다가 훈이를 건드리면 철수네 가족 모두가 발작을 한다는 거..?
그리고 이런 버블링을 막을 수 있는 방법은 stopPropagation()
가 있다.
Propagation
의 사전적 의미는 번식, 전파를 뜻한다. 그러니까 전파는 멈추겠다고 선언하는 것.
element.addEventListener('click', (event) => event.stopPropagation());
onclick
의 경우 인라인 태그 내에서 혹은 동적으로 사용 될 수 있다.
event.stopPropagation();
를 해준 다음 실행할 function을 호출해주면 된다.
<div onclick="event.stopPropagation(); clickEvent()">button 1</div>
해당 element를 불러다가 onclick
시 이벤트를 정의해주면 된다.
<div id="test">test<div>
<script>
test.onclick = (event) => {
event.stopPropagation();
}
</sctript>
캡처링의 사전적의미를 보면 사로잡다
라는 뜻을 갖고 있다.
버블링
은 이벤트가 안에서 바깥으로 올라간 것이라면,
캡처링
은 반대로 바깥에서 구석으로 몰아붙이는 것
위에서 아래로 내리갈굼
하는 거라고 생각하면 될거 같다.
( '사로잡는다' 라고 하면 나는 동물들이 사냥할 때 포위망을 좁혀가는 형태의 이미지가 떠오른다.)
캡처링을 아직 실무에서 사용할 일이 없어서 사용해 본적은 없지만 캡처링을 실행시키려면
addEventListener
의 세번째 인자값(options)
을 건드려야한다.
해당 options에 대해서 자세히 알고 싶으면 MDN 홈페이지를 들어가면 자세히 볼 수 있다. (링크)
아무튼 캡처링을 실행하는 방법은 addEventListener('click', function , {captue : true}
로 해주면 된다.
기존 기능 막기하는것은 뭐라고 해야할지 몰라서 내가 그냥 서술한 거다.
만약 <a>
에 네이버를 연결하는 링크를 걸어뒀다고 했을 때 해당 태그를 선택하면 페이지가 전환된다. 왜냐면 그게 <a>
태그의 기능이니까!
<a class="btn" href="https://www.naver.com">naver</a>
그런 기본 동작을 막을 수 있는게 preventDefault()
이다.
그럼 이걸 어떻게 사용할 수 있는지 다양한 예시를 아래를 코드를 통해 확인할 수 있다.
addEventListener
를 사용할 경우 evnet.preventDefault()
를 해주면 되고
onclick
또한 동일하지만 Inline
에서 event
값을 넘겨줘야 하는 방법 하나와 return false
를 해주는 방법 두가지가 있다는 점에서 새롭게 다가왔다.
뭐 이런식으로 실무를 하면서 Event
를 다룰 때 겪을 수 있는 상황에 대비하는 것에 대해서 알아봤다.
그리고 버블링을 이용하면 메모리를 줄일 수 있겠다는 생각이 들어서 메모리 측면에서 어떤 차이가 있는지 확인해 보았다.
<div class="container">
<div class="btn">버튼 1</div>
<div class="btn">버튼 2</div>
<div class="btn">버튼 3</div>
<div class="btn">버튼 4</div>
<div class="btn">버튼 5</div>
<div class="btn">버튼 6</div>
<div class="btn">버튼 7</div>
<div class="btn">버튼 8</div>
<div class="btn">버튼 9</div>
</div>
위 코드와 같은 구조가 있다.
각 div
에 모두 총 9개의 EventListener
를 달았다. 그리고 크롬의 개발자 도구에서 Memory 사용량을 측정해 보았다.
위 테스트는 새로고침을 열번
했을 때 쌓이는 Listener 의 총 갯수를 보여준다.
총갯수가 108개
로 새로고침을 할 때마다 EventListener
가 쌓이는 것을 볼 수 있다.
그렇다면 위에서 확인했던 버블링(bubbling)을 활용하여 .container
에 EventListener
를 달면 어떻게 될까?
동작은 역시 동일하게 하는 것을 확인 할 수 있다.
그렇다면 메모리 사용량은 어떨까? 동일하게 새로고침을 열번 반복해 보았다.
결과는 최대 11개
의 EventListener
를 생성한 것을 볼 수 있다. 그러니 개선 전 코드보다 갯수를 10배 가량 줄일수 있었던 것이다.
자바스크립트도 내부적으로 가비지 컬렉션(Garbage Collection)
을 통해 내부적으로 사용하지 않는 메모리를 제거
한다고 알고는 있지만 그래도 개발자가 조절할 수 있는 선에서 메모리를 줄인다면 품질을 향상시키는데 많은 도움이 될 거 같다.
-
아무튼 이렇게 하는게 이벤트 위임 (event delegation)
인데 캡처링과 버블링을 활용하여 구현할 수 있다.
이렇게 동작이 되는 것은 event.target
이 내가 선택한 target
이기 때문에 해당 요소에 대해 이벤트를 실행시킬 수 있는 것이다.
자바스크립트의 세상은 정말 다양하고 넓은 것 같다. 알면 알수록 새롭고 공부할 게 너무 많다. 언제 자바스크립트 고수가 될지 모르겠지만 하나씩 하나씩 해쳐나가보자!
-
[참고자료]
https://www.youtube.com/watch?v=OmrxrHSrS2Y
https://ko.javascript.info/