먹짱(쇼핑몰 프로젝트) - 리팩토링(3) - 이벤트 위임

ryan·2022년 6월 5일
0

학습자료 : 1분 코딩 - 자바스크립트 이벤트 위임 핵심 정리

  • 이번 프로젝트에서는 데이터 배열을 가공해서 insertAdjacenthtml을 통해 다양한 노드로 생성하여 렌더링하고 반복문을 사용하여 각 요소마다 이벤트를 걸어주는 패턴을 상당히 많이 사용했다.
  • 그 과정에서 받은 피드백 중 하나가 모든 요소에 반복물을 이용해 이벤트를 걸어주는 것은 비효율적이고, 감시해야 하는 대상이 늘어나기 때문에 성능면에서도 좋지 않다는 것이였다.
  • 반복문 대신 이벤트 위임에 대해 알아보라는 말을 들었고 학습한 내용을 바탕으로 정리해봤다.

기존 코드

  • 기존에 작성했던 수량 조절 버튼 코드의 예시이다. 이벤트 위임에 적합한 형태가 아닐 수 있다.

문제점 분석

  • 수량 조절 버튼이 하나만 사용되는 상품 상세 페이지와는 다르게 장바구니 페이지에서는 상품마다 수량 조절 버튼이 생성된다. 그리고 감시하는 대상과 계속해서 늘어나게 된다. (이미지 참고)

  • +,-버튼 각각 이벤트를 달았지만 같은 콜백 함수를 사용하기 때문에 +버튼의 클릭 이벤트에서도 -버튼의 이벤트인지 판단하는 코드가 들어가는 비효율이 발생한다.

  • 그리고 DOM에 변경사항이 생기면 하위 요소마다 수정해야 한다는 유지보수 측면의 비효율도 발생할 것 같다.

const quantityControlBox = (doc) => {
  const $upButton = doc.querySelector('#quantityUp');
  const $downButton = doc.querySelector('#quantityDown');
  const $quantityInput = doc.querySelector('#quantityInput');
  $upButton.addEventListener('click', controlQuantity);
  $downButton.addEventListener('click', controlQuantity);

 // 하나의 콜백 함수로 구현했기 때문에, +를 눌러도 -인지 판단하는 코드가 들어간다.
  function controlQuantity(e) {
    if (e.target.id == 'quantityUp') {
      $quantityInput.value = Number($quantityInput.value) + 1;
    } else if (e.target.id == 'quantityDown' && $quantityInput.value != 1) {
      $quantityInput.value = Number($quantityInput.value) - 1;
    }

화면 예시


🚩 이벤트 위임

  • 위와 같이 요소마다 핸들러를 두지 않고, 여러 요소의 상위 요소에 하나의 핸들러를 두어 하위 요소의 이벤트를 관리하는 것을 이벤트 위임이라고 한다.

  • 이벤트 위임의 종류에는 이벤트 버블링, 캡처링이 있다.

이벤트 버블링

  • 특정 요소에서 핸들러가 동작했을 때 , 해당 요소의 상위 요소로 거슬러 올라가면서 상위 요소의 핸들러를 동작시키는 특성이다.
  • 아래와 같이 p태그를 클릭했을 때, p > div > form의 핸들러가 순차적으로 작동된다.
모던자바스크립트 예시
<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

이벤트 캡처링

  • 이벤트 캡쳐링은 상위 요소에서 이벤트가 발생했을 때 하위 요소로 내려가면서 이벤트가 발생하는 특성이다.

어떻게 활용할 수 있을까?

  • 간단한 예시로 테이블 요소가 있고 그 속에 수십만개의 data cell이 있고, data cell에 checkbox 요소를 넣고 checkbox의 이벤트를 감시해야 한다고 가정한다.

  • 이를 위해 수십만개의 이벤트를 넣고 체크박스를 하나하나 관리하는 건 당연히 비효율적이다. 여기서 table 요소에 하나의 핸들러를 두어 모든 셀의 이벤트를 감시하는 이벤트 위임을 사용하여 위와 같은 상황을 방지할 수 있다.

<table>
  <tr>
  </tr>
  <tr>
    <td class="n">...</td>
    <td class="n">...</td>
	...
</tr>
  <tr>
  ...
  </tr>
  ...
</table>

이벤트 위임 사용 전
const td = document.querySelectorAll('td')
td.forEach(e=>e.addEventListener(....)

이벤트 위임 사용 후
const table = document.querySelector('table')
table.addEventListener(...)      

이벤트 위임이 발생하는 것을 막고 싶다면?

stopPropagation

  • 하위 요소에서 발생한 이벤트가 상위 요소로 전달되는 이벤트 전파를 막을 수 있다.

stopImmediatePropagation

  • 하위 요소에서 발생한 이벤트를 제외한 나머지 이벤트도 함께 중단

preventDefault

  • 이벤트의 기본 동작을 중지

수정한 코드

const quantityControlBox = (doc) => {
  const $quantityBox = document.getElementById('updownBtnBox'); // 수량조절박스
  const $quantityInput = doc.querySelector('#quantityInput');
  $upButton.addEventListener('click', controlQuantity);
  $downButton.addEventListener('click', controlQuantity);

  function controlQuantity(e) {
    if (e.target.id == 'quantityUp') {
      $quantityInput.value = Number($quantityInput.value) + 1;
    } else if (e.target.id == 'quantityDown' && $quantityInput.value != 1) {
      $quantityInput.value = Number($quantityInput.value) - 1;
    }
  • +,-버튼이 아닌 박스 자체에 핸들러를 달아서 선언하는 dom요소를 줄일 수 있고, 감시하는 대상도 절반으로 줄어들었다.
  • 이 예시말고도 상당히 많은 부분에 이벤트 위임 방식을 통해 코드량을 상당히 많이 줄일 수 있었다.
profile
프론트엔드 개발자

0개의 댓글