고양이hyebin
stopPropagation의 충돌 발생! 이벤트 전파 자세히 알아보기
December 31, 2024

문제 상황

모달의 배경을 누르면 모달이 닫히는 기능은 이벤트 버블링을 이용하여 쉽게 구현할 수 있었습니다.

하지만 모달 안에서 Select 컴포넌트의 드롭다운 후 외부를 클릭했을 닫히도록 동일하게 구현하였더니, 드롭다운이 닫히지 않았습니다.

문제 분석

예상되는 원인은 모달 내부에서 e.stopPropagation()을 이미 사용하고 있었기 때문이었습니다.

모달 내부에서 e.stopPropagation()을 호출하면서 이벤트의 전파가 막힌 것으로 예상은 되었지만, 어떤 식의 흐름으로 막혔는지는 설명하기 어려웠습니다.

그래서 이벤트 전파에 대해 확실하게 파악하고 싶었습니다.

이벤트 전파 (이벤트 버블링&캡쳐링)

이벤트 흐름은 다음과 같습니다.

  • 최상위 요소부터 타겟 요소까지 이벤트가 전파 된 후 (이벤트 캡쳐링)
  • 타겟요소부터 이벤트가 전파되어 최상위 요소까지 올라갑니다. (이벤트 버블링)
이벤트 버블링과 캡쳐링
이벤트 버블링과 캡쳐링

이벤트 발생 흐름 파악하기

span 태그를 클릭했을 때 이벤트 발생 흐름을 파악해 봅시다.

span 태그를 클릭했을 때 이벤트 버블링과 캡쳐링
span 태그를 클릭했을 때 이벤트 버블링과 캡쳐링
  • span태그에 클릭을 하면, html 최상위 요소부터 자식 태그까지 이벤트가 타고 올라갑니다. (캡쳐링)
  • span요소의 이벤트가 실행되면 반대로 부모요소를 타고 내려가며 전파됩니다. (버블링)

자바스크립트 addEventListener()는 기본적으로 버블링으로 동작

버블링 동작
버블링 동작

addEventListener에 세번째 파라미터로 true를 넣어주면 캡쳐링으로 동작

캡처링 동작
캡처링 동작
  • 세번째 파라미터는 디폴트는 false로 버블링으로 되어있습니다.
  • 세번째 파라미터에 ture를 넣어주면 캡쳐링으로 동작합니다.

Event.eventPhase로 이벤트 흐름 단계 파악

Event.eventPhase로 이벤트 흐름 파악 가능
Event.eventPhase로 이벤트 흐름 파악 가능
  • 부모에서 자식까지 내려갈때 ( 캡쳐링될 때 ) 1
  • 현재 타겟에 도착했을 때 2
  • 타겟에서 부모까지 올라갈때 ( 버블링 될 때 ) 3

stopPropagation과 preventDefault의 차이점

  • stopPropagation(): 이벤트 전파를 막음. 즉, 이벤트가 부모나 다른 핸들러로 전달되지 않습니다..
  • preventDefault(): 기본 동작을 막음. 예를 들어, 링크 클릭 시 페이지 이동을 막거나, 폼 제출 시 페이지 리로드가 방지됩니다.

원인 파악

  1. 모달 배경을 클릭하면 닫히는 로직이 있음.
  2. Select 드롭다운이 열렸을 때, 드롭다운 바깥을 클릭하면 닫히는 로직이 있음.
  3. 근데, 둘 다 동시에 있으니까 문제가 발생
  4. Select 컴포넌트 드롭다운 바깥을 클릭했을 때, 모달 클릭 이벤트가 먼저 실행됨
  5. 모달의 e.stopPropagation(); 때문에 Select의 클릭 이벤트 로직이 실행되기 전에 막혀버림.

해결 방법

  1. stopPropagation()을 최소화, Select에서만 사용하기

    stopPropagation()를중복으로 사용하지 않게 하여 이벤트 흐름 방해를 최소화합니다.

  2. select에 캡쳐링 사용하기

    Select 컴포넌트에서 document.addEventListener("click", handleClickOutside, true)를 추가하면 캡쳐링으로 동작합니다. 이렇게 하면 Select의 로직이 제일 먼저 실행됩니다.

결론

이번 버그를 통해 이벤트의 흐름과 제어하는 방식에 대해서 깊게 생각해볼 수 있었습니다. 첫 번째 방법인 stopPropagation() 을 중복으로 사용하지 않는 방법도 가능했지만, 캡처링을 활용한 이벤트 처리를 통해 근본적은 원인을 파악하고 문제를 해결할 수 있었습니다!

참고 자료

https://youtu.be/0jtalJxrxhs?si=JBAo7YEwlcxbxVNx