JS & React 이벤트 동작 방식(Bubbling, Capturing)

김은호·2023년 5월 1일
0
post-thumbnail

Intro

React-Three-Fiber을 최근에 공부하고 있는데 docs의 이벤트 부분을 보던 중 event bubbling이라는 단어가 보였다. 처음 JS를 배울 때 공부한 개념이었는데 시간이 지나니 가물가물해서 이참에 다시 정리하려고 한다.

Bubbling

하위 Element에서 발생한 클릭 이벤트가 최상단 Element까지 전달되는 현상

<body>
    <div id="div">
      <span id="span">
        <button id="button">Button</button>
      </span>
    </div>
</body>
<script>
    const div = document.getElementById('div');
    const span = document.getElementById('span');
    const button = document.getElementById('button');
    console.log(div);

    div.addEventListener('click', () => {
      console.log('I am Div!');
    });

    span.addEventListener('dbclick', () => {
      console.log('I am Span!');
    });

    button.addEventListener('click', () => {
      console.log('I am Button!');
    });
</script>

위 코드의 경우 button의 클릭 이벤트만 발생시켰는데 상위 요소들의 클릭 이벤트도 발생한다. span의 경우 더블클릭 이벤트이므로 발생하지 않았다.

즉 하위 요소에서 특정 이벤트가 발생했다면, 상위 요소로 올라가면서 같은 이벤트가 있다면 상위 요소의 이벤트도 발생시키는 것을 Event Bubbling이라고 한다.

Capturing

Event Capturing은 Bubbling과 반대 방향으로 진행된다.

<body>
	<div id="div1">
		<div id="div2">
			<div div3="div3" />
		</div>
	</div>
</body>
<script>
	const divs = document.querySelectorAll('div');
    divs.forEach(div => div.addEventListener('click', (e) => {
    	console.log(e.target.id);
    }, 
    {capture: true}) // Capturing 설정
</script>

위 코드에서 id="div2"인 div를 클릭했다고 하자. 최상단 요소가 html일 때, html부터 div2까지 내려오며 각 요소에 클릭 이벤트가 있다면 발생시킨다.

즉 Capturing은 Bubbling과 비슷하지만, 방향이 반대로 동작한다.

전파 막기

내가 클릭한 요소에서만 이벤트를 발생시키고 싶은 경우가 있을것이다.

<body>
    <div id="div">
      <span id="span">
        <button id="button">Button</button>
      </span>
    </div>
</body>
<script>
    const div = document.getElementById('div');
    const span = document.getElementById('span');
    const button = document.getElementById('button');
    console.log(div);

    div.addEventListener('click', () => {
      console.log('I am Div!');
    });

    span.addEventListener('dbclick', (e) => {
      e.stopPropagation(); // 전파 막기
      console.log('I am Span!');
    });

    button.addEventListener('click', () => {
      console.log('I am Button!');
    });
</script>

위 경우 버튼을 클릭하면 타고 올라가다가 span에서 stopPropagation()을 만나 전파가 중지된다.

즉 stopPropagation()이 있는 태그가 방화벽 역할을 하고, 거기서 전파가 중단된다.

stopPropagation(): 이벤트의 전파를 방지
preventDefault(): 이벤트가 발생할 때, 브라우저의 기본 동작을 막음) submit을 하였을 때 새로고침 되거나 anchor을 클릭했을 때 브라우저가 이동하는 것을 막음

이벤트 위임

Bubbling을 이용한 기법으로, 하위 요소에 각각 이벤트를 등록할 필요없이 상위 요소에 이벤트를 등록하여 하위 요소에 이벤트를 발생시키는 기법이다.

event.target?

이벤트를 등록할 때 event를 매개변수로 주고 event.target으로 동작을 제어한다. 여기서 event.target은 이벤트를 발생시킨 엘리먼트이다.

this: 현재 이벤트가 발생하고 있는 곳
event.target: 최초 이벤트가 실행된 곳

Bubbling은 하위 요소에 이벤트가 발생하면 위로 올라가며 같은 이벤트를 발생시키는 것이라고 하였다.
그러면 하위 요소마다 각각 이벤트를 등록할 필요 없이, 상위 요소에 이벤트 하나만 등록시키면 일일이 등록할 필요가 없어진다.

그리고 상위 요소에서는 event.target으로 하위 요소를 컨트롤할 수 있게 된다.

장점

  • 코드가 간결 & 메모리 절약
  • 동적으로 하위 요소를 추가할 때 일일이 이벤트를 등록할 필요가 없어짐

React에서의 이벤트 위임

React의 장점중 하나는 Virtual DOM을 이용하여 DOM에 직접 접근하지 않고, 둘의 차이를 비교하여 바뀐 부분의 DOM만 업데이트 하는 것이다.

그래서 querySelector 등을 이용하여 DOM으로 직접 접근하여 addEventListener로 이벤트를 등록하는 Vanila JS와는 달리 React는 그냥 태그에 이벤트를 등록한다.

그러면 React에서도 메모리 절약을 위해 ref로 직접 DOM에 접근하여 이벤트 위임을 구현하면 좋지 않을까?

정답은 X이다. React는 이미 자체적으로 DOM에서 react를 관리하는 root node에 이벤트 리스너를 등록하게 설계되어 있어서 일일이 ref로 DOM에 직접 접근하여 이벤트 위임을 해도 성능상 이점이 없다.

0개의 댓글