오랜만에 바닐라 자바스크립트로 투두리스트를 만들던 중 완료여부를 표시하는 체크박스가 동작하지 않았다.
클릭할때마다 이벤트가 1,3,7,15...번씩 중복 실행되는 현상이 생겨 콘솔창을 확인해보니
투두항목을 업데이트 할 때마다 체크박스 요소에 이벤트리스너가 계속해서 추가되고 있었다.
- 초기코드
처음 렌더링할때 아래처럼 리스트를 순회하면서 직접 내부 동작 요소에도 직접 이벤트를 바인딩했다.
이후 각 리스트에 변경이 있을때는 해당 li 요소를 찾아서 새롭게 바인딩 하고
이벤트도 새로 추가하는 방식으로 만들었었다.
const todoList = $('#todoList');
todoData.forEach(todo => {
const li = createTodoItemElement(todo);
// 각 li의 체크박스에 직접 이벤트 바인딩
li.querySelector('.todo-checkbox').addEventListener('click', () => {
toggleTodoComplete(todo.id);
});
// 수정/삭제 버튼도 마찬가지로 각각 바인딩
...
todoList.appendChild(li);
});
이 리스트 수정 후 다시 바인딩 하는 과정이 문제였는데,
텍스트, 수정삭제버튼 부분은 아예 기존 요소를 삭제하고 만들던 반면에,
체크박스는 데이터의 불리언 값에 따라 기존 요소에 표시만 다르게 만들었기 때문에 이벤트리스너가 계속 추가되었다.
체크박스도 새로 요소를 만들어야하나 하다가
등록되어있는 이벤트 리스너를 삭제하면 기존 코드에서 큰 변경없이 수정할 수 있을것 같았다.
시도1 : getEventListeners
기존에 이벤트를 익명함수로 등록했기 때문에 이벤트를 삭제할 방법이 없었는데
getEventListeners()를 사용하면 해당 요소에 등록된 이벤트리스너를 배열로 불러올 수 있었다.
const checkboxClickEventListener = getEventListeners(checkbox).click[1].listener;
removeEvent(checkbox, click, checkboxClickEventListener);
이렇게 해결이 된 줄 알았으나 콘솔창을 확인해보니
"Uncaught ReferenceError: getEventListeners is not defined"
라는 오류가 생겨있었다.
찾아보니 getEventListeners()는 크롬 개발자 도구 콘솔에서만 사용할수 있는 디버깅 함수였다.
결국 기존 로직을 바꾸더라도 다른 방법을 찾아야 했다.
시도2 : 이벤트위임
이벤트 위임에 대해 어렴풋하게만 알고있었지만 직관적인 방법이 아닌것 같아 적용을 꺼려했는데
여러 구글링을 하다보니 동적으로 추가해야하는 상황에는 이벤트 위임이 훨씬 효율적인 방법이라고 나와있었다.
기존처럼 각각의 <li>에 직접 이벤트를 바인딩하는 것이 아니라
<li>의 부모요소 <ul>에 클릭 이벤트리스너 한번 설정해놓은 다음
이때 일어난 이벤트버블링을 추적해서
실제 클릭한 요소를 e.target과 closest()로 찾아 각각의 이벤트를 처리하는 방법을 사용했다.
https://sunnyscript.tistory.com/199
이벤트 버블링, 캡쳐링
이벤트 버블링이벤트가 발생했을 때 가장 안쪽 타겟요소에서부터 시작해서 부모요소로 거슬러올라가며 전파되는 단계.브라우저의 기본 이벤트 전파 방식.타깃리스너 실행 이후 전파된다.버튼
sunnyscript.tistory.com
그 결과 돔요소를 바꿔서 바인딩할때마다 이벤트를 지우거나 다시 추가할 필요가 없어졌다.
첫 렌더링 이후에 자식요소들이 변경되어도 클릭이벤트때 타겟 요소를 추적하면서 정확하게 이벤트를 적용하므로
동작도 잘 되었다.
다만 e.target.closest()로 요소를 찾아 가는 부분이 조금 번거롭지 않나 했는데
리액트에서 이런 고민을 한적이 없었던것 같아 좀 더 알아봤다.
리액트에서는 이벤트 위임을 신경쓰지 않아도 된다.
리액트에서는 최초 렌더링을 할때 내부적으로 이벤트를 최상위 루트DOM(id='root'컴포넌트)에 한번만 바인딩하고
이벤트가 발생하면 자체적으로 어떤 컴포넌트에서 발생했는지를 추적해서 해당 핸들러만 실행한다.
그래서 각각의 JSX요소에 onClick, onChange등의 핸들러를 추가하기만 해도 아무 문제가 없었다.
내부적으로 이벤트 버블링 속성을 활용한 위임처리를 해주고 있었기 때문에 편하게 사용할 수 있었던 것이다.
자바스크립트 이벤트를 등록하다가 헤매서 당황했는데
그래도 이번 기회에 자바스크립트 이벤트 처리방식과 프레임워크의 역할에 대해 깊이 이해할 수 있었다.
새삼 그동안 참 편하게 코딩해왔구나 하는 생각을 하게 됐다.
'고군분투' 카테고리의 다른 글
프로젝트 생성 명령어 npm init, npm create, npx 비교 (0) | 2025.01.29 |
---|---|
String(숫자)와 숫자.toString() 의 차이 (0) | 2022.10.12 |
[VS code 확장팩] CSS Peek - css적용 쉽게 하기 (0) | 2022.10.08 |
[VS code 확장팩] Live Preview - 화면분할 미리보기 (0) | 2022.10.08 |
onclick ="close()" 는 작동이 안되는 코드다 (0) | 2022.10.07 |