JavaScript의 마우스 이벤트: UI가 깜박이는 이유 (그리고 올바르게 고치는 방법)

발행: (2026년 1월 13일 오후 03:52 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

Hover interactions feel simple—until they quietly break your UI.
호버 인터랙션은 간단해 보이지만, 조용히 UI를 깨뜨릴 때가 있습니다.

Recently, while building a data table, each row had an “Actions” column that appears on hover. Most of the time it worked, but when moving the mouse slowly or crossing row borders the UI flickered, and sometimes two rows showed actions simultaneously. The culprit wasn’t CSS or rendering; it was the mouse‑event model.
최근 데이터 테이블을 만들면서 각 행에 마우스를 올렸을 때 나타나는 “Actions” 열을 추가했습니다. 대부분은 정상적으로 동작했지만, 마우스를 천천히 움직이거나 행 경계선을 넘을 때 UI가 깜빡였고, 때때로 두 개의 행에 동시에 액션이 표시되기도 했습니다. 문제의 원인은 CSS나 렌더링이 아니라 마우스 이벤트 모델이었습니다.

마우스 호버 이벤트 두 종류

EventBubblesFires when
mouseoverYes마우스가 요소 또는 그 자식 요소에 들어올 때
mouseoutYes마우스가 요소 또는 그 자식 요소를 떠날 때
mouseenterNo마우스가 해당 요소 자체에 들어올 때
mouseleaveNo마우스가 해당 요소 자체를 떠날 때

버블링과 비버블링 호버 이벤트의 구분은 UI 엔지니어링에서 가장 중요한 사항 중 하나입니다.

mouseover가 UI 상태에 위험한 이유

<tr>
  <td>Name</td>
  <td>
    <button>Edit</button>
    <button>Delete</button>
  </td>
</tr>

사용자 입장에서는 버튼 사이를 이동할 때 여전히 “행 위에 마우스를 올린 상태”라고 생각합니다. 브라우저 입장에서는 포인터가 다음과 같이 이동합니다:

→  →

각 전환마다 커서가 자식 요소를 통과하면서 새로운 mouseovermouseout 이벤트가 발생합니다. 그 결과:

  • 한 버튼에서 다른 버튼으로 이동하면 첫 번째 버튼에서 mouseout이 발생하고, 이는 버블링됩니다.
  • 사용자가 행을 떠나지 않았음에도 행은 “마우스가 떠났음” 신호를 받게 됩니다.

DOM 이동사용자 의도 사이의 불일치가 깜빡임을 유발합니다.

내 테이블이 깨진 이유

  • 각 행은 마우스를 올렸을 때 액션 버튼을 표시했습니다.
  • 행 사이에 1 px 경계선이 있었습니다.
  • 커서가 그 경계선을 넘을 때, 다음 행에 들어가기 전에 잠시 첫 번째 행을 벗어났습니다.

Resulting sequence:

  1. mouseout → 첫 번째 행의 액션을 숨깁니다.
  2. mouseover → 다음 행의 액션을 표시합니다.

타이밍이 충분히 빠르면 두 행이 동시에 활성화된 것처럼 보여 깜박임이 발생했습니다. 레이아웃은 정상였으며, 이벤트 모델이 단순히 사용자의 의도를 잘못 표현한 것이었습니다.

mouseenter가 해결되는가

mouseentermouseleave 버블되지 않으며 포인터가 실제로 요소 자체에 들어가거나 나갈 때만 발생합니다—자식 요소에는 적용되지 않습니다. 같은 움직임:

→  →

단일 mouseenter(tr)(그리고 나중에 단일 mouseleave(tr))만 트리거되어 잘못된 탈출을 없애고 깜박임을 방지합니다. 따라서 다음에 이상적입니다:

  • 테이블 행
  • 드롭다운 메뉴
  • 툴팁
  • 호버 카드
  • 커서가 요소 안에 머무르는 동안 활성 상태를 유지해야 하는 모든 UI

요약하면:

  • mouseenter사용자 의도
  • mouseoverDOM 탐색

언제 각각을 사용해야 할까

mouseenter / mouseleave를 사용할 때:

  • 호버에 따라 UI 상태를 토글할 때.
  • 자식 요소가 호버를 방해하지 않아야 할 때.
  • 안정성이 중요할 때.

예시

  • 행 작업
  • 네비게이션 메뉴
  • 프로필 카드
  • 툴팁

mouseover / mouseout를 사용할 때:

  • 들어가거나 떠난 자식 요소를 알아야 할 때.
  • 버블링을 활용해 동작을 위임할 때.

예시

  • 이미지 맵
  • 아이콘별 툴팁
  • 개별 요소에 대한 맞춤형 호버 효과

React Makes This More Subtle

React에서는 onMouseOveronMouseOut가 합성 이벤트 시스템에 래핑되어 있어, 전파와 재렌더링의 또 다른 레이어가 추가됩니다. 이는 깜박임과 레이스 컨디션을 증폭시켜, 호버 기반 UI를 올바르게 구현하기 어렵게 만들 수 있습니다.

실용적인 경험 법칙

If you are using mouseover to control UI visibility, you are likely building something fragile. Most hover‑based interfaces should be built with mouseenter / mouseleave because users hover 요소, not raw DOM nodes.

최종 생각

내 테이블에서 보였던 작은 깜박임은 버그가 아니라 브라우저의 이벤트 모델이 얼마나 깊은지를 상기시켜 주는 것이었습니다. 최고의 UI 엔지니어는 인간이 실제로 화면과 상호작용하는 방식을 반영하는 로직을 작성합니다. 종종 깜박이는 UI와 견고한 UI의 차이는 단 하나의 이벤트 이름에 불과합니다.

Back to Blog

관련 글

더 보기 »

메인 스레드는 당신 것이 아니다

Main Thread는 사용자의 리소스입니다. 사용자가 사이트나 앱을 방문하면, 브라우저는 단일 스레드를 할당해 JavaScript를 실행하고, 그들의…