JS 이벤트 위임 적용

불꽃남자·2020년 9월 9일
0

오늘은 이벤트 위임을 현재 진행중인 grand busan 클론코딩 프로젝트에 적용시켜보려고 한다.

현재 내 프로젝트의 Header 컴포넌트의 메뉴 리스트 구성은 이렇게 되어있다.

//Header.js
//...

<HeaderList>
  {listArray.map((listItem, index) => {
    return (
      <HeaderListItem key={index} isOn={isOn} id={index}>
        <a
          href="/"
          onMouseEnter={(e) => {
            setIsHover(true);
            setIsOn(index);
          }}
        >
          {listItem.listItem}
        </a>
        {subMenuFn(listItem)}
      </HeaderListItem>
    );
  })}
</HeaderList>

//...

HeaderList라는 ul태그 안에 HeaderListItem이라는 li태그를 map으로 생성하고, li태그 하나하나에 onMouseEnter 이벤트 핸들러를 적용시키고 있는 모습이다.
나는 이 코드에 큰 의문을 느끼지 못 했다... 이벤트 위임을 알기 전까진 말이다!

이벤트 위임

이벤트 위임이란 무엇인가?
이벤트 캡쳐링과 이벤트 버블링을 이용해서, 하나의 조상요소에서 그 하위요소들의 이벤트를 모두 컨트롤하는 것을 이벤트 위임이라고 할 수 있다. 즉, 하위요소가 조상요소에게 이벤트를 위임하는 것이다.

그럼 내 코드를 리팩토링하며 더 자세히 알아보자.

이벤트 위임 적용

//Header.js
//...

<ul>
  {listArray.map((listItem, index) => {
    return (
      <li key={index} isOn={isOn} id={index}>
        <a
          href="/"
          onMouseEnter={(e) => {
            setIsHover(true);
            setIsOn(index);
          }}
        >
          {listItem.listItem}
        </a>
        {subMenuFn(listItem)}
      </ul>
    );
  })}
</li>

//...

이해를 돕기 위해 컴포넌트 명들을 HTML 태그명으로 변경했다.

이벤트 위임을 알고 난 후 이 코드를 다시보니 '이런 더러운 코드가 다 있나!' 싶다.
a 태그의 갯수만큼 onMouseEnter의 이벤트 리스너의 갯수도 늘어나고, 그만큼 리소스를 잡아먹고 있는 것이다. 그럼 이제 이벤트 위임을 적용해보겠다.

//Header.js
//...

<ul
  onMouseEnter={(e) => {
    setIsHover(true);
    console.log(e.target);
  }}
>
  {listArray.map((listItem, index) => {
    return (
      <li key={index} isOn={isOn} id={index}>
        <a href="/">{listItem.listItem}</a>
        {subMenuFn(listItem)}
      </li>
    );
  })}
</ul>

//...

우선 a 태그에 붙어있던 onMouseEnter를 제거했고 반복되는 li들의 공통 조상요소인 ul에 onMouseEnter 이벤트 리스너를 달아주었다. 이제 a태그에 mouseEnter 이벤트가 일어나면 어떻게 될까?
이벤트 위임 1
보다시피 a 태그에는 이벤트리스너가 없음에도 불구하고 ul 요소에서 이벤트를 감지하여 console.log(e.target) 을 실행해주고 있다.

그리고 발생하는 이슈

이벤트를 위임받은 요소에서 일어난 이벤트도 감지된다

이벤트 위임 2이벤트 위임 3
당연한 이야기다. ul 태그에 이벤트리스너를 등록했기 때문에, ul 태그 자체에서 일어나는 이벤트도 감지된다. 위의 사진은 ul 태그의 padding에 onMouseEnter 되었을 때의 모습이다.
이 문제는 user agent stylesheet의 ul태그의 padding값을 0으로 초기화하고 margin-left를 더 줘서 해결했다.
이벤트 위임 4

onMouseEnter가 작동하지 않는다?

이벤트 위임 5
a 태그에서 다른 a 태그로 마우스를 옮길 때 onMouseEnter이벤트가 감지되질 않는다. a 태그에 onMouseEnter 이벤트가 일어날 때 마다 ul 태그의 이벤트 리스너가 감지해야 하는 것이 아닌가?

라고 생각했었다. onMouse 이벤트에 관한 글을 읽기 전 까지는!
onMouseEnter 이벤트는 요소 밖으로 포인터가 나갔다 오지 않으면 계속 발생하지 않는다. 그리고 onMouseOver 이벤트는 요소 영역을 벗어나지 않아도 자식 요소에 들어갈 때에 발생한다.
즉 위의 경우에는 onMouseOver 이벤트 리스너를 달아주어야 한다는 것이다.
이벤트 위임 6
onMouseOver로 바꿔주자 감지가 잘 되는 모습

적용 완료

남은 일은 mouseOver된 a 요소의 부모인 li 요소의 index값을 읽어와서 setIsOn(index) 해주는 것 뿐이다.

<ul
  onMouseOver={(e) => {
    setIsHover(true);
    setIsOn(parseInt(e.target.parentElement.id));
    // isOn과 같은 id를 가지고 있는 li의 SubMenu 컴포넌트가 보여진다.
  }}
>
  {listArray.map((listItem, index) => {
    return (
      <li key={index} isOn={isOn} id={index}>
        <a href="/">{listItem.listItem}</a>
        {subMenuFn(listItem)}
      </li>
    );
  })}
</ul>

이벤트 위임 7
적용이 완료되었다!

마치며

요 근래 프론트엔드 기술 면접에 관련된 글들을 찾아보면서 그동안 내가 얼마나 프론트엔드에 대해 무지했는지 깨우치고 있다. 이벤트 위임이란 개념도 글을 읽기 전까지는 알지 못 했었다.
프론트엔드 개발자가 갖춰야 마땅한 지식임에도 까맣게 모르고 있었다는 사실이 부끄러웠고, 한 편으로는 이제 알게 되어서 굉장히 기쁘다.

또 이렇게 블로그에 공부한 내용을 게시하며 배워나가니 더 정확하고 꼼꼼하게 배우게 된다. 좋은 흐름이다. 이 흐름에 맞추어 앞으로 걸어나가겠다.

profile
프론트엔드 꿈나무, 탐구자.

0개의 댓글