블랙커피 스터디1 문벅스 step1 DOM조작과 이벤트 핸들링 회고

🎯 step1 요구사항 - 돔 조작과 이벤트 핸들링으로 메뉴 관리하기

  • 에스프레소 메뉴에 새로운 메뉴를 확인 버튼 또는 엔터키 입력으로 추가한다.
    • 메뉴가 추가되고 나면, input은 빈 값으로 초기화한다.
    • 사용자 입력값이 빈 값이라면 추가되지 않는다.
  • 메뉴의 수정 버튼을 눌러 메뉴 이름 수정할 수 있다.
    • 메뉴 수정시 브라우저에서 제공하는 prompt 인터페이스를 활용한다.
  • 메뉴 삭제 버튼을 이용하여 메뉴 삭제할 수 있다.
    • 메뉴 수정시 브라우저에서 제공하는 confirm 인터페이스를 활용한다.
  • 총 메뉴 갯수를 count하여 상단에 보여준다.
  • 추가되는 메뉴의 아래 마크업은 <ul id="espresso-menu-list" class="mt-3 pl-0"></ul> 안에 삽입해야 한다.

form eventListener - 엔터 || 버튼 클릭 이벤트 감지하기

미션 요구사항중,

에스프레소 메뉴에 새로운 메뉴를 확인 버튼 또는 엔터키 입력으로 추가한다.

라는 기능이 있어 버튼 클릭과 엔터키 입력을 각각 걸어줘야하나 고민하던중 한번에 관리할 수 있는 방법을 찾게 되었다.

 <form id="espresso-menu-form">
    <div class="d-flex w-100">
      <label for="espresso-menu-name" class="input-label" hidden>
        에스프레소 메뉴 이름
      </label>
      <input
        type="text"
        id="espresso-menu-name"
        name="espressoMenuName"
        class="input-field"
        placeholder="에스프레소 메뉴 이름"
        autocomplete="off"
      />
      <!-- type은 꼭 submit으로 !! -->
      <button
        type="submit"
        name="submit"
        id="espresso-menu-submit-button"
        class="input-submit bg-green-600 ml-2"
      >
        확인
      </button>
    </div>
</form>
const $form = document.querySelector("#espresso-menu-form");

// form에 eventListener 걸어주기
$form.addEventListener("submit", (e) => {
  e.preventDefault();

  if (!$input.value) {
    alert("값을 입력하세요.");
  } else {
    onAddMenuClick();
  }
});

이런식으로 form 자체에 eventListener를 걸어주면 button을 클릭하는것과 엔터를 치는 이벤트를 중복해서 등록할 필요 없다. 원샷투킬이 가능해진다!
단, 이때 주의할점은 button의 type이 꼭 submit이여야한다. <button type="button">으로 마크업하면 백날 이벤트리스너 등록해봤자 소용이 없다 ..
(+ if/else문으로 작성하지 않고, if문에서 조건이 일치하지 않으면 return시키고 다음 라인에 onAddMenuClick를 호출하는 식으로 작성할지 고민했는데 현재 코드에서는 그냥 if/else문이 더 깔끔한 것 같아서 일단은 그렇게 작성했다. 혹시 이 부분에 대해 피드백이 있으면 수정해야겠다.)

이벤트 위임 - 효율적으로 이벤트를 관리하기

요구사항중 메뉴를 수정하거나 삭제할 수 있는 기능을 만들어야 했는데, 처음에는 querySelector로 각각의 수정과 삭제 버튼을 찾은 다음 각 요소마다 이벤트리스너를 등록하려 했었다. 그런데 조금 비효율적인 것 같아서 찾아보던 중 이벤트 위임을 사용하면 조금더 효율적으로 코드를 짤 수 있어 적용해보았다.

<!-- 수정, 삭제 버튼을 클릭시 이벤트를 감지해야한다. -->
 <span class="w-100 pl-2 menu-name">${$input.value}</span>
    <button
      type="button"
      class="bg-gray-50 text-gray-500 text-sm mr-1 menu-edit-button"
    >
      수정
    </button>
    <button
      type="button"
      class="bg-gray-50 text-gray-500 text-sm menu-remove-button"
    >
      삭제
    </button>
</span>
$menuList.addEventListener("click", (e) => {
  // 수정 || 삭제를 구분하기 위해 innerText를 썼는데,
  // 더 좋은 방법이 있을지 궁금하다. 👀
  if (e.target && e.target.innerText == "수정") {
    const result = window.prompt("메뉴명을 수정하세요.");
    e.target.parentNode.firstElementChild.innerText = result;
  }

  if (e.target && e.target.innerText == "삭제") {
    window.confirm("정말 삭제하시겠습니까?") &&
      $menuList.removeChild(e.target.parentNode);
  }
}

부모 요소에 이벤트리스너를 적용하면 굳이 하나하나 적용 시켜줄 필요 없이 일타쌍피가 가능하다. 🔥

MutationObserver() - DOM 변경 감지하기

미션 요구 사항중 등록된 메뉴(아이템)의 갯수를 적어야 하는 문제가 있어서 알아보던 중 MutationObserver를 발견했다. 처음엔 변경을 감지하려는 DOM에다가 change 이벤트리스너를 등록하려 했는데, change 이벤트리스너는 폼 종류(예: input, selectbox)에만 등록이 돼서 MutationObserver를 활용하게 되었다.

const observer = new MutationObserver((mutationsList) => {
  const count = mutationsList[0].target.children.length;
  $menuCount.innerHTML = `${count}`;
});

observer.observe($menuList, {
  attributes: false,
  childList: true,
  subtree: false,
});

observer.observe뒤에 있는 객체는 변경을 감지할때 속성을 적는건데, 하위 요소의 속성 변경이나 노드의 내용이 변경되었을때 감지하는 등 다양한 옵션을 적을 수 있었다. 나는 자식 노드의 갯수만 체크하면 돼서 childList만 true로 값을 줬다.

총 아이템의 갯수가 잘 변한다. 👍✨

MutationObserver를 사용하지 않는 방법으로는, 자식 노드의 갯수를 카운팅하는 함수를 등록/삭제 이벤트 감지마다 호출하는 방법이 있을텐데 이번엔 MutationObserver를 사용해봤다. 이것도 뭐가 옳고 그른지는 잘 모르겠어서 피드백이 있으면 수정해야겠다. (피드백 적극 환영합니다.)

마치며

블랙커피 스터디에 들어간 이유가 회사에서는 리액트로만 개발하고, 바닐라 자바스크립트에서는 나의 지식을 빈약함을 느껴.. 바닐라 자바스크립트 스킬을 향상시키고 싶어 들어갔는데 좋은 선택인 것 같았다. 첫번째 미션은 비교적 말랑말랑하게 해결할 수 있어 안심이였다. (월요일 오후에 시작했는데 기한 못맞춰 제출할까 쫄았다 .. ^^ㅠ) 하지만.. 미션은 완료했지만 내가 코드를 잘 짠건지는 모르겠다. 항상 코드를 짤때마다 느끼는 불안함.. 뭔지 아시죠? 그냥 <어떻게든 굴러가기만 하는 코드>를 제출해버린것 같아서 두렵지만.. 오랜만에 바닐라 자바스크립트로 환기를 시켜줘서 좋았다. 앞으로도 바닐라 자바스크립트를 열심히 공부해야지..🔥

좋은 웹페이지 즐겨찾기