개발새발 로그

React - addEventListener와 onClick을 같이 사용해도 되나요? 본문

React

React - addEventListener와 onClick을 같이 사용해도 되나요?

이즈흐 2024. 8. 24. 20:20

📖들어가며...

개인 프로젝트로 Tooltip 기능을 구현하던 중 만난 상황에 대해서 정리해보려고 한다.

정말 간단한 문제인데 자칫하면 실수할 법한 상황이라 공유하면 좋을 것 같다는 생각이 들었다.

 

먼저 나는 Tootip이 click, focus, hover 기능이 한 컴포넌트에서 모두 기능할 수 있도록 구현하려고 했다.

그래서 아래와 같은 코드가 작성되었다.

props로 받는 eventType에 따라 이벤트를 다르게 등록한다.

이렇게 등록해서 click, focus, hover 모두 가능하게 하려고 했다.

 

click은 따로 window.addEventListener로 click된 tooltip이 닫히도록 했다.

이때 click에서 예외 상황이 발생할 수 있다.

만약 click으로 tooltip이 나오는 경우 tooltip 내부 요소를 클릭했을 때는 tooltip이 닫혀서는 안된다.

 

위 조건을 만족하기 위해서는 간단하게 자식 요소에 event.stopPropagation()을 수행하게 하면됐다.

tooltip요소에 위 처럼 이벤트를 등록한다.

하지만 위 처럼 작성해도 tooltip을 클릭했을 때 tooltip이 없어지게 된다.

 

왜 그런 것인지는 아래 예시를 통해 알아보자.

 

 

🤔어떻게 작동할까요?

아래 코드를 보자

자식 요소를 클릭하면 어떻게 출력될까?

간단히 설명하면

부모 Div 요소addEventListener로 이벤트를 등록했고,

자식 Div 요소onClick으로 이벤트를 등록했다.

 

예상되는 출력결과는?

만약 자식 요소를 클릭하게 되면 어떻게 될까?

자식 요소 이벤트 발생!
부모 요소 이벤트 발생!

위 처럼 로그가 출력되는 것일까?

 

하지만 아래와 같이 출력된다.

 

 

🤔왜 그런 것일까?

예상하기로는 자식 요소의 이벤트가 발생하고, 이벤트 버블링으로 인해 부모 요소의 이벤트가 발생하는 순서일 것이다.

하지만 예상대로 작동하지 않았다.

 

이는 리액트 이벤트네이티브 이벤트의 차이에서 나오는 상황이다.

자세한 설명은 해당 링크에서 답변으로 잘해주고 있다.

전 React 매니저 Sophie Alpert 아래와 같이 말했다고 한다.

React는 이벤트를 최상위에서 처리하기 때문에, React의 이벤트 핸들러와 네이티브 이벤트 핸들러 간의 실행 순서를 보장할 수 없습니다.
즉, React의 onClick 핸들러와 addEventListener가 등록된 핸들러는 서로 다른 방식으로 작동할 수 있습니다.

 

간단하게 말하면 

리액트는 이벤트 핸들러를 해당 이벤트 핸들러를 추가한 각각의 DOM 요소에 부탁하는 것이 아니라, 이벤트 타입 당 하나의 핸들러를 루트에 부착한다. - 출처

 

링크는 내가 예전에 조사한 자료를 첨부했다.(이렇게 조사까지 했으면서 실수했다.)

 

그럼 결과적으로 addEventListener은 리액트의 이벤트 관리에서 벗어나게 된다.

그리고 onClick으로 등록한 이벤트는 root에 등록된다.

따라서 addEventListener에 등록된 이벤트가 실행되고, 그 이후 상위에 등록된 onClick 이벤트가 발생하는 것이다.

 

이를 만약 아래와 같이 onClick으로만 일관되게 작성하면 어떻게 될까?

어떻게 실행될까?

그럼 아래와 같이 출력된다.

우리가 예상한 순서대로 이벤트가 발생한다.

 

이를 통해서 addEventListener로 이벤트를 등록하는 것은 지양해야 되겠다고 느꼈다.

 

 

 

 

 

 

 


 

 

 

 

 

🤔이건 어떻게 동작할까요? - onMouseEnter()

아래 코드를 보자

자식요소에 마우스를 올리면 어떻게 될까요?

만약에 자식요소에만 마우스를 올리면 어떻게 될까?

 

아래와 같이 예상하는 분들도 있을 것이다.

자식 이벤트
부모 이벤트

 

 

하지만 아래와 같이 출력된다.

사실 당연한 결과!

사실은 당연하게도 부모요소가 자식요소를 감싸고 있기 때문에

당연하게도 부모 이벤트가 먼저 발생하는 것이 맞다.

 

 

그러면 아래와 같이 자식 요소가 css 스타일로 인해 밖에 있는 경우는 어떻게 될까?

부모 자식 관계는 그대로인데 css로 인해 밖으로 나왔다.
tooltip에서 쓸법한 스타일이다.

 

사실 위 처럼 css로 자식요소가 밖에 있더라도 이전과 동일하게 출력된다.

이전과 동일하다.

 

이 상황이 사실 내가 겪은 문제다.

 

tooltip이 화면에  떠있지만 opacity로 인해 보이지만 않는 상태일 때 

보이지 않는 공간에 마우스를 올려도 tooltip이 나오게 된다.

 

🤔그럼 e.stopPropagtion()으로 자식 요소에서 버블링이 일어나지 않게 하면 되지 않나?

아래 코드처럼 바꿔보자

 

하지만 이전과 동일하게 작동한다.

 

자식 요소에 마우스를 올려도 부모 이벤트가 일어나지 않게 해야하는데 

stopPropagation을 사용해도 예상되는 결과가 나오지 않는다.

 

그래서 나는 아래와 같이 visibility를 사용했고,

이때 transition의 시간차를 둬서 opacity 효과가 제대로 작용하도록 했다.

이러면 tooltip이 사라질 때도 원활하게 transition이 적용된다.

그러면 내가 원하던 tooltip요소에 마우스가 hover되어도 나타나지 않게 된다.

요소가 visibility: hidden; 상태인 경우, 사용자가 해당 요소에 마우스를 올리거나 클릭하는 등의 상호작용을 할 수 없습니다.
이는 요소가 보이지 않기 때문에 사용자에게는 클릭 가능한 영역으로 인식되지 않기 때문입니다.

 

이해가 안된다면 작동 예시를 보자

DOM에서는 존재하지만 마우스를 올려도 안나타난다.

 

하지만 아래와 같은 문제가 발생한다.

tooltip이 나타나면 그 이후부터 hover가 가능해진다.

위처럼 hover 이후 tooltip이 나타나면 transition의 시간차로 인해 해당 부분을 계속해서 hover할 수 있게 된다.

 

이 부분을 어떻게 수정해야할지는 계속 찾아보려고한다..

 

 

 

📘마치며..

이렇게 내가 Tooltip을 구현하면서 만난 상황들을 정리해봤다.

처음에 왜 stopPropagation이 적용이 안될까? 라는 생각에 빠져 이런 리액트의 이벤트 환경을 생각하지 못했다.

 

기존에 내가 공부한 내용이더라도 문제를 넓게보지 못하면 이런 상황이 발생하게 되는 것 같다.

그래서 3시간동안 헤매버렸다.

 

hover되는 부분인 onMouseEnter는 추후 수정해보려고 한다.

저 문제는 정말 거슬려서 어떻게든 해결하고 싶다..ㅠ

 

728x90
반응형
LIST