관련 지식
#javascript #design-pattern #event-delegation-pattern #bubbling

디자인 패턴이라는 개념은 특정 코딩 형태로 만들었을때 개발 측면이나 유지보수 측면에서 더욱 좋은 효과가 있다고 제시하는 개발 유형의 가이드라인 입니다.

언어의 특징에 따라 가이드 되는 디자인패턴의 종류가 조금씩 다를수 있고 동일한 디자인 패턴도 코딩 형태는 달라질수 있습니다. 그 중 이벤트 델리게이션 패턴은 자바스크립트에서 대표적인 디자인 패턴의 하나로 반드시 알아야 할 필요가 있습니다.

이벤트 델리게이션 패턴을 언제 쓸까?

구글 스프레드 시트의 경우 위와 같은 레이아웃을 가지고 있습니다. 각 셀은 클릭/더블클릭에 대해 동작하도록 기능이 만들어져 있고, 더블클릭 후엔 키보드 입력이 가능한 텍스트 필드로 변환이 되며, 키보드 입력에 대해 동작하는 이벤트가 적용되어 있습니다. 그럼 각 셀에 클릭 이벤트, 더블클릭 이벤트, 키보드 입력 이벤트까지 최소 3개의 이벤트를 등록해야 한다는 것을 추측할 수 있습니다.

문제는 스프레드 시트에서 몇개의 column과 row를 사용하게 될지 알 수가 없다는 것입니다. 일반적으로 100개 이상의 column을 사용하는 경우는 많지 않겠지만 row에 해당하는 데이터의 경우 100개가 될지 10000개 이상이 될지 알수가 없습니다.
그럴때 클릭 이벤트는 어떻게 적용해야 할까요?

td셀에 클릭시 배경을 넣는 예제

  1. $("td").on("click", function() {
  2. $(this).css("background-color", "yellow");
  3. });

다수의 개발자는 위와 같은 형태로 처리할 것입니다.(바닐라JS로 예시를 드는게 명확하지만 angular, react, vue 환경이 아니라면 JQuery를 사용할 것입니다) 매우 단순하게 만들어진 코드라 무심코 넘어갈수 있지만 성능은 절대 단순하지 않습니다. $("td") 는 화면 전체에 만들어진 td 태그의 개수만큼을 반환할것이고(DOM 탐색비용 높음) 각각의 셀에 대해 이벤트를 등록하므로 셀 개수만큼의 이벤트가 등록됩니다.

브라우저는 이벤트 발생 유무를 감지하기 위해 자원을 소모하는데 이벤트 갯수가 많아질수록 모니터링이 많아져 브라우저 성능은 저하하게 되므로 너무 많은 이벤트를 사용하면 안됩니다. 따라서 스프레드 시트와 같이 이벤트 갯수가 무한정 늘어날 수 있는 케이스는 절대 피해야 합니다.

이럴때 사용하는것이 Event Delegation Pattern 입니다. 이벤트 델리게이션 패턴은 이벤트 버블링을 이용한 기법으로 이벤트 버블링이 무엇인지 이해해야 합니다.

이벤트 델리게이션 패턴 적용 방법

간단하게 정리하면 이벤트 버블링은 하위태그에서 발생한 이벤트를 상위태그에서 감지하는 것으로 ‘table > tbody > tr > td’ 와 같은 태그 계층 구조에서 td에서 발생한 이벤트를 table 태그나 tbody 태그 등에서 감지할수 있게 하는것이 이벤트 버블링 입니다. 아래 소스가 이러한 이벤트 버블링을 이용한 이벤트 델리게이션 패턴 입니다.

  1. $("table tbody").on("click", function(e) {
  2. if(e.target.nodeName == 'TD') {
  3. var $cell = $(e.target);
  4. $cell.css("background-color", "yellow");
  5. }
  6. });

이전 소스보다 복잡해졌지만 이벤트 등록은 보다 명확합니다. tbody 태그에만 클릭 이벤트를 등록하여 하위 태그에서 버블링된 이벤트를 감지하고 이벤트 타겟에 배경을 적용하는 내용입니다.

이 방식은 td 태그의 갯수에 상관없이 tbody 태그 1개에만 적용하게 되므로 이벤트 등록 효율이 좋고 DOM 탐색비용도 매우 낮으므로 빠릅니다. 또한 동적으로 생성되는 모든 td 태그에 이벤트를 등록할 필요가 없으므로 효율적입니다.

이것은 tdli 태그 뿐 아니라 여러 태그가 섞인 구조에서도 사용 가능합니다.

  1. $('div').on('click', function(e) {
  2. if(e.target.nodeName == 'LI') {
  3. //li 클릭시 하고 싶은거
  4. }
  5. else if(e.target.nodeName == 'A') {
  6. //a 클릭시 하고 싶은거
  7. }
  8. else if(e.target.nodeName == 'SPAN') {
  9. //span 클릭시 하고 싶은거
  10. }
  11. });

일관성 없는 너무 많은 요소를 이벤트 델리게이션 패턴으로 묶을 경우 소스 복잡도가 높아져 주의가 필요하지만(body 태그에 적용하는것은 당연히 최악입니다.) 일관성있는 요소에 대한 이벤트 처리시에는 매우 유용합니다. 스프레드 시트, 채팅, 전체메뉴, 갤러리 등 이벤트가 몇 개가 등록될지 정확한 측정이 불가능한 화면에는 반드시 적용해야 합니다.

프레임워크 에서의 패턴 적용

JQuery의 이벤트 등록 함수는 이벤트 델리게이션 패턴을 지원해주고 있어서 아래처럼 작성 해볼수도 있습니다.

  1. $("table tbody").on("click", 'td', function(e) {
  2. var $cell = $(e.target);
  3. $cell.css("background-color", "yellow");
  4. });

참고
https://api.jquery.com/on/

그러나 vue.js 에서는 v-on 디렉티브를 이용하여 이벤트를 등록하는 경우 ‘이벤트 델리게이션 패턴’ 을 고민할 필요가 없습니다. v-on 디렉티브는 addEventListener() 와 비슷한 기능을 하지만 전혀 다른 것이며, 또한 자식 컴포넌트의 이벤트를 감지하지도 않습니다.

angular나 react는 그만의 방식이 있을것입니다. 관련 개발자 분들은 해당 프레임워크에서 이벤트 델리게이션 패턴을 어떤식으로 다루는지 확인해 보시면 좋을것 같습니다.