pointermove가 안 찍히는 이유와 해결법 (iOS Safari 완전 정리)

모바일에서 pointermove가 안 찍히는 이유와 확실한 해결법 (리얼그리드 캘린더 개발 중 발견한 이슈)
최근 **리얼그리드(RealGrid)**의 **캘린더(Calendar)**를 개선하면서,
한 가지 의외의 현상을 발견했습니다.
사용자가 손가락으로 좌·우로 스와이프하면 월이나 연도가 바뀌는 기능을 개발 중이었는데,
데스크톱에서는 문제없이 작동하던 코드가 모바일 Safari에서는 전혀 반응하지 않는 거예요.
분명히 pointermove 이벤트를 감지하도록 구현했는데,
iPhone에서는 아무런 이벤트가 찍히지 않았습니다.
디버깅을 거듭한 끝에, 그 원인은 터치 이벤트의 기본 제스처 처리 방식에 있었습니다.
💡 왜 pointermove가 모바일에서 안 찍힐까?
모바일 환경에서 손가락을 움직이면, 브라우저는 먼저 이렇게 생각합니다.
- 이건 스크롤 제스처일까?
- 아니면 핀치 줌(확대/축소)일까?
- 혹은 단순히 화면 위를 터치한 걸까?
이 판단 과정에서 브라우저는 기본 제스처를 최우선으로 처리합니다.
즉, 스크롤로 인식되는 동작은 pointermove 이벤트로 개발자에게 전달되지 않습니다.
특히 iOS Safari는 이런 정책이 굉장히 보수적이라,
터치 제스처를 “스크롤 또는 네비게이션”으로 판단하면
pointermove가 아예 호출되지 않습니다.
결국, 우리는 기본 터치 제스처를 중단시켜야만
pointermove를 온전히 받을 수 있습니다.
🧭 확실한 해결책은 두 가지뿐이에요
1️⃣ CSS로 제스처를 통제하기 — touch-action: none;
.calendar-body {
touch-action: none;
-ms-touch-action: none; /* IE/Edge 레거시 대응 */
}이 속성은 “이 영역에서는 브라우저가 스크롤이나 줌을 하지 말고,
포인터 입력을 그대로 전달하라”는 의미입니다.
실제로 리얼그리드의 캘린더 본문(.calendar-body)에
touch-action: none;을 지정하자,
스와이프 시 pointermove 이벤트가 정상적으로 감지되기 시작했습니다. 🎉
이 방식은 단 한 줄로 제스처 충돌을 해결할 수 있고,
가장 표준적이며 성능 저하도 없습니다.
2️⃣ JS에서 직접 제어하기 — preventDefault() + { passive:false }
const calendar = document.querySelector('.calendar-body');
calendar.addEventListener('touchmove', (e) => {
e.preventDefault(); // 기본 스크롤 제스처 차단
}, { passive: false });이 방법은 자바스크립트 레벨에서 “이 터치는 내가 처리할게”라고 선언하는 방식이에요.
다만 반드시 { passive:false }를 명시해야 합니다.
그렇지 않으면 iOS Safari에서는 preventDefault()가 무시되어
효과가 없습니다.
⚙️ 어떤 방법을 써야 할까?
가능하다면 CSS의 touch-action으로 해결하는 것을 추천드립니다.
이건 선언형이고, 유지보수도 쉽습니다.
다만, 캘린더처럼 스와이프 제스처를 감지하면서도,
세밀하게 터치 좌표를 계산해야 하는 경우라면
preventDefault()를 병행하는 게 좋습니다.
리얼그리드 캘린더에서도
touch-action: none;으로 포인터를 열어준 뒤,
자바스크립트로 pointermove 좌표를 감지해
이동 거리 기준으로 월·연도를 변경하도록 구현했습니다.
🧩 리얼그리드(RealGrid) 실제 사례
당시 개발 중 사용했던 단순 예시는 다음과 같습니다.
calendar.addEventListener('pointerdown', (e) => {
startX = e.clientX;
});
calendar.addEventListener('pointermove', (e) => {
if (!startX) return;
const delta = e.clientX - startX;
if (delta > 80) changeMonth(-1); // 왼쪽 스와이프 → 이전 달
if (delta < -80) changeMonth(1); // 오른쪽 스와이프 → 다음 달
});데스크톱에서는 완벽하게 작동했지만,
iPhone Safari에서는 손가락을 움직여도
pointermove가 단 한 번도 호출되지 않았습니다.
하지만 아래 한 줄을 추가하자 모든 게 해결됐죠.
.calendar-body { touch-action: none; }이후 iOS, Android 모두에서 스와이프 동작이 완벽히 동기화되었습니다.
이 경험 덕분에 **리얼그리드의 다른 드래그형 UI (예: 차트, 셀 리사이징)**에서도
같은 원리를 적용해 안정성을 높일 수 있었습니다.
🧠 기억해두면 좋은 요약
| 구분 | 내용 |
|---|---|
| 문제 원인 | 브라우저가 터치 제스처(스크롤/줌)를 우선 처리 |
| 대표 증상 | iOS Safari에서 pointermove 미발생 |
| 1차 해결법 | touch-action: none; |
| 2차 해결법 | touchmove에서 preventDefault() + { passive:false } |
| 적용 예시 | 리얼그리드 캘린더 스와이프, 차트 드래그, 슬라이더 등 |
💬 마무리하며
이 문제는 단순한 브라우저 버그가 아니라,
브라우저가 사용자 경험(스크롤, 네비게이션)을 보호하기 위한 정책에 가깝습니다.
하지만 그 동작 원리를 이해하고 나면,
모바일에서도 pointermove를 100% 제어할 수 있습니다.
작은 차이지만,
touch-action: none; 한 줄로
스와이프 UX 전체가 달라질 수 있다는 걸 직접 체감했습니다.
이 글이 같은 문제로 고생하는 개발자분들께
작은 힌트가 되길 바랍니다 😊
#pointermove #touchmove #touchAction #preventDefault #모바일웹 #iOSSafari #AndroidChrome #RealGrid #캘린더개발 #프론트엔드 #웹이벤트