[Troubleshooting] 모바일 웹 고스트 클릭(Ghost Click) 해결 방법

박두팔이·2025년 12월 23일

[Troubleshooting] 모바일 웹 고스트 클릭(Ghost Click) 해결 방법

모달 중첩 클릭 이슈 원인 분석과 Overlay Shield 패턴

모바일 웹 개발 중 한 번의 터치로 의도하지 않은 요소까지 클릭되는 현상, 이른바 고스트 클릭(Ghost Click) 문제를 경험한 적이 있다면 이 글이 실질적인 해결책이 될 것이다.
본 문서는 모달 중첩 환경에서 발생한 고스트 클릭 현상의 원인을 분석하고, Overlay Shielding 패턴을 통해 근본적으로 해결한 실무 사례를 정리한다.


1. 문제 상황: 모바일 웹 모달 중첩 클릭 버그

발생 환경

  • 모바일 웹 브라우저 (iOS Safari / Android Chrome 공통)
  • Vue.js 기반 SPA
  • 모달이 중첩되는 UI 구조

증상

  • 퀴즈 모달 위에 오답 알림 모달(Alert Modal)이 표시됨
  • 사용자가 알림 모달의 [확인] 버튼을 터치
  • 알림 모달이 닫히는 순간, 뒤에 있던 퀴즈 선택지 버튼이 자동 클릭됨

UX 문제

  • 사용자는 단순히 오답 확인만 하려 했으나
  • 다음 문제의 선택지가 의도치 않게 선택됨
  • 명백한 UX 결함 및 신뢰도 하락 요인

2. 원인 분석: 모바일 고스트 클릭(Ghost Click)의 정체

이 문제는 단순한 버그가 아니라 모바일 브라우저의 클릭 이벤트 처리 방식에서 비롯된다.

2.1 모바일 브라우저의 300ms 클릭 지연

  • 모바일 브라우저는 터치가 더블 탭(확대) 인지 판단하기 위해
  • touchend 이후 약 300ms 대기click 이벤트를 발생시킴

2.2 이벤트 전파(Event Propagation) 문제

  1. 사용자가 [확인] 버튼을 터치 (touchend)
  2. 알림 모달이 즉시 DOM에서 제거됨
  3. 300ms 후, 브라우저가 해당 좌표에 click 이벤트를 전송
  4. 기존 타겟이 사라졌기 때문에
  5. 같은 좌표에 있던 하위 요소(퀴즈 버튼) 가 이벤트를 수신

➡ 이를 Ghost Click, 혹은 클릭 이벤트 대물림 현상이라고 부른다.


3. 해결 시도와 한계: Debounce 방식의 문제점

시도한 방법

  • 퀴즈 버튼 클릭 시 Date.now() 기준으로
  • 직전 클릭 후 400ms 이내의 클릭은 무시

한계

  • 모든 클릭 대상에 방어 로직 추가 필요
  • 컴포넌트가 늘어날수록 유지보수 난이도 급증
  • 근본 원인(이벤트 타이밍)을 해결하지 못함

확장성과 구조적 안정성이 떨어지는 임시방편


4. 최종 해결책: Overlay Shielding 패턴

핵심 아이디어

모달이 사라질 때, 투명한 오버레이를 잠시 유지하여
고스트 클릭 이벤트를 흡수한 뒤 제거한다.

전략 요약

  • DOM 제거를 300ms 이후로 지연
  • 시각적으로는 즉시 사라진 것처럼 처리
  • 실제 클릭 이벤트는 오버레이가 대신 수신

5. 구현 코드 예시 (Vue.js)

GlobalAlertModal.vue

<script setup>
import { ref } from 'vue'

const isClosing = ref(false)

function handleClose() {
  if (isClosing.value) return
  isClosing.value = true

  // 고스트 클릭 흡수용 쉴드 유지
  setTimeout(() => {
    appStore.clearGlobalAlert() // 실제 DOM 제거
    isClosing.value = false
  }, 350)
}
</script>

<template>
  <div
    v-if="globalAlert"
    class="modal-overlay"
    :class="{ 'is-closing': isClosing }"
    @click.self="handleClose"
  >
    <div class="modal-content" :class="{ 'fade-out': isClosing }">
      <!-- alert content -->
    </div>
  </div>
</template>

<style scoped>
.modal-overlay.is-closing {
  background: rgba(0, 0, 0, 0); /* 시각적으로 투명 */
  pointer-events: auto;        /* 중요: 클릭 차단 유지 */
  backdrop-filter: none;
}

.modal-content.fade-out {
  opacity: 0;                  /* 콘텐츠만 먼저 숨김 */
}
</style>

6. 이 방식이 실무적으로 우수한 이유

1) UX 손상 없음

  • 사용자는 즉시 모달이 닫힌 것처럼 인지
  • 시각적 지연 없이 자연스러운 전환

2) 근본적인 고스트 클릭 차단

  • 브라우저가 발생시킨 유령 클릭이 실제 UI 요소에 도달하기 전에 오버레이에서 소멸

3) 중앙 집중식 관리

  • 전역 모달 컴포넌트 한 곳만 수정
  • 프로젝트 전체 고스트 클릭 이슈 일괄 해결

7. 결론

모바일 웹은 touch 이벤트와 click 이벤트가 공존하는 환경이며, 이로 인해 데스크톱에서는 발생하지 않는 타이밍 이슈가 빈번하게 발생한다.

setTimeout 기반의 Overlay Shielding 패턴은 트릭이 아니라 브라우저 이벤트 메커니즘을 정확히 이해한 실무 해법이다.

모달 중첩 UI, 모바일 웹 클릭 오류, 고스트 클릭 문제로 고민 중이라면 이 방식은 가장 안정적이고 확장 가능한 해결책이 될 것이다.

profile
기억을 위한 기록 :>

1개의 댓글

comment-user-thumbnail
2026년 2월 24일

오 실무 해결~

답글 달기