모바일 웹 개발 중 한 번의 터치로 의도하지 않은 요소까지 클릭되는 현상, 이른바 고스트 클릭(Ghost Click) 문제를 경험한 적이 있다면 이 글이 실질적인 해결책이 될 것이다.
본 문서는 모달 중첩 환경에서 발생한 고스트 클릭 현상의 원인을 분석하고, Overlay Shielding 패턴을 통해 근본적으로 해결한 실무 사례를 정리한다.
이 문제는 단순한 버그가 아니라 모바일 브라우저의 클릭 이벤트 처리 방식에서 비롯된다.
touchend 이후 약 300ms 대기 후 click 이벤트를 발생시킴touchend)click 이벤트를 전송➡ 이를 Ghost Click, 혹은 클릭 이벤트 대물림 현상이라고 부른다.
Date.now() 기준으로➡ 확장성과 구조적 안정성이 떨어지는 임시방편
모달이 사라질 때, 투명한 오버레이를 잠시 유지하여
고스트 클릭 이벤트를 흡수한 뒤 제거한다.
<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>
모바일 웹은 touch 이벤트와 click 이벤트가 공존하는 환경이며, 이로 인해 데스크톱에서는 발생하지 않는 타이밍 이슈가 빈번하게 발생한다.
setTimeout 기반의 Overlay Shielding 패턴은 트릭이 아니라 브라우저 이벤트 메커니즘을 정확히 이해한 실무 해법이다.
모달 중첩 UI, 모바일 웹 클릭 오류, 고스트 클릭 문제로 고민 중이라면 이 방식은 가장 안정적이고 확장 가능한 해결책이 될 것이다.
오 실무 해결~