-
모바일에서 웹 캐러셀 중첩 스크롤 처리 (safari)Front-end 2025. 7. 29. 16:29반응형

Next.JS를 이용해서 웹 개발을 했고, 모바일 사이즈에 맞춰서 반응형도 준비를 했다.
실제로 여러 기기 테스트를 해봤을 때 잘 나오는 것을 확인했고, 이제 나가면 되겠지 싶었다.
그래서 내가 있는 층의 직원들을 대상으로 베타 테스트를 진행했는데 "모바일에서 스크롤이 잘 안되는 부분이 있어요" 라는 얘기를 들었다.
스크롤이 안 될 이유가 없을텐데? 하면서 봤는데 공통점은 모두가 아이폰 유저였고 safari를 기본 브라우저로 사용하는 분들이었다.
그리고 스크롤이 안 된다고 말했던 위치는 "캐러셀(Carousel)" 부분 이었다.
나는 평생 안드로이드만 사용해왔고, 주로 Chrome을 사용하는 유저다보니 iOS 스타일의 방식(모달, 팝업, 경고창, 이동 등) 이 익숙치 않다. 이번에도 웹에서 다양한 모바일 사이즈에 넣어볼 수 있는 방법이 있어서 시각적인 방법을 사용해 확인을 했다.
즉, 직접 터치를 해서 스크롤 한게 아니라 "마우스 휠"을 사용해서 테스트를 해보았는데 잘 되는 것을 확인했고, 내 스마트폰(Android)을 이용해서 Chrome에서 테스트 해봤을 때도 잘 되었다. 그러다보니 안일하게 놓친 부분이 생겼던 것 같다.
인터넷을 좀 찾아보니 Safari 모바일에서 스크롤 관련 이슈들이 종종 있는 것 같다.
사실 이 부분은 직장 내 이사님이 AI를 이용해서 만들어보려다가 AI가 해결을 못 해주고 있다고 맡긴 부분이었다.
그러다보니 정확한 코드도 모르고 어떻게 구현되어있는지도 모른 상태로 시작 되었다.
문제 상황
구현하려던 기능은 다음과 같다.
1. 유저 간략 정보 캐러셀 카드를 터치하면 뒷면이 나타남 (3D flip animation)
2. 뒷면에는 스크롤 해서 볼 수 있는 유저 상세 정보가 표시됨
3. 뒷면에서 내부 스크롤을 해서 보다가 스크롤이 끝나면 자연스럽게 외부 스크롤로 전환
데스크톱에서는 마우스 휠로 완벽하게 작동했지만, Safari 모바일에서 터치 스크롤 시에만 문제가 발생했다.// 문제가 된 초기 구조 <div className="carousel-container"> <div className="flip-card" onClick={handleFlip}> {/* 앞면 */} <div className="front-card">...</div> {/* 뒷면 - 독립적인 스크롤 컨테이너 */} <div className="back-card"> <div className="scrollable-content overflow-y-auto"> {/* 긴 콘텐츠 */} </div> </div> </div> </div>
왜 마우스 휠은 되고 터치는 안 될까?
1. 이벤트 처리 메커니즘의 차이
마우스 휠:// 브라우저가 자동으로 처리 element.addEventListener('wheel', (e) => { // 내부 스크롤 불가능 시 자동으로 부모로 이벤트 전파 });
- wheel 이벤트는 즉시 실행되며 자연스럽게 이벤트 버블링된다
- 내부 스크롤이 불가능하면 자동으로 상위 요소로 전파된다.
터치 스크롤:// 개발자가 직접 제어해야 함 element.addEventListener('touchstart', handleTouchStart); element.addEventListener('touchmove', handleTouchMove); element.addEventListener('touchend', handleTouchEnd);
- touchstart → touchmove → touchend 시퀀스 전체를 관리해야 함
- 제스처 해석, velocity 계산, momentum 스크롤링 등 복잡한 과정
2. Safari의 네이티브 터치 최적화
Safari는 터치 스크롤을 하드웨어 수준에서 최적화한다고 하는데 JavaScript가 터치 이벤트에 개입하면 이 네이티브 최적화와 충돌이 발생하는 경우가 있다고 한다.onTouchMove={(e) => { e.stopPropagation(); // 외부 스크롤 차단! }}
해결 하기 위한 시도시도 1 : 복잡한 터치 이벤트 로직
const handleTouchMove = (e) => { const currentY = e.touches[0].clientY; const deltaY = startY - currentY; const isAtTop = scrollElement.scrollTop <= 0; const isAtBottom = scrollElement.scrollTop + scrollElement.clientHeight >= scrollElement.scrollHeight - 1; // 스크롤 경계에서만 외부 스크롤 허용 if ((isAtTop && deltaY < 0) || (isAtBottom && deltaY > 0)) { return; // 외부 스크롤 허용 } e.preventDefault(); // 내부 스크롤만 허용 };결과: 내부 스크롤도 막히거나, 외부 스크롤 전환이 부자연스러움
시도 2 : CSS overscroll-behavior 의존.scrollable-content { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
결과: Safari에서 overscroll-behavior 지원이 제한적
시도 3: 더 정교한 경계 감지const handleTouchMove = (e) => { const touchDelta = startY - currentY; const currentScrollTop = scrollableElement.scrollTop; const maxScrollTop = scrollableElement.scrollHeight - scrollableElement.clientHeight; const isAtTop = currentScrollTop <= 0; const isAtBottom = currentScrollTop >= maxScrollTop - 1; const isTryingToScrollUp = touchDelta > 0; const isTryingToScrollDown = touchDelta < 0; if ((isAtTop && isTryingToScrollDown) || (isAtBottom && isTryingToScrollUp)) { return; // 외부 스크롤 허용 } e.preventDefault(); };
결과: 여전히 부자연스럽거나 내부 스크롤이 막힘해결책: 브라우저 네이티브 동작 최대 활용하고 , 복잡한 JavaScript 로직을 모두 제거 및 CSS 기반 해결책으로 회귀
최종 해결 코드// 간단한 CSS 기반 접근 useEffect(() => { if (!isFlipped) return; // 전체 페이지 터치 최적화 document.body.style.touchAction = 'manipulation'; return () => { document.body.style.touchAction = ''; }; }, [isFlipped]); <div className="h-full overflow-y-scroll" style={{ WebkitOverflowScrolling: 'touch', overscrollBehaviorY: 'auto' // 자연스러운 스크롤 체이닝 }} > {/* 스크롤 가능한 콘텐츠 */} </div> /* globals.css - 전역 Safari 최적화 */ body { overscroll-behavior-y: none; -webkit-overflow-scrolling: touch; } html { overscroll-behavior: none; scroll-behavior: smooth; }
1. 브라우저 네이티브 동작을 최대한 따라가자
복잡한 JavaScript 로직으로 터치 이벤트를 제어하려 하지 말고, 가능한 한 브라우저의 기본 스크롤 동작을 활용하는게 구현에 도움이 되는 것 같다. 만약 커스텀을 하겠다고 하면 그만큼 시간을 들여서 더 섬세하게 해야한다.
2. CSS를 우선 적용해보자복잡한 터치 이벤트 제어를 하기보다는 요즘은 CSS만 사용해도 되는 것들이 많아지고 있는 만큼 CSS로 최대한 제어를 해보자
// ❌ 복잡한 터치 이벤트 제어 addEventListener('touchmove', complexLogic); // ✅ 간단한 CSS 해결 style={{ overscrollBehaviorY: 'auto' }}
3. Safari는 특별하다. 조금 더 신경쓰자.
평생 삼성 갤럭시를 써왔지만 삼성 인터넷을 잘 쓰지 않는다. 하지만 그렇다고 해서 iPhone 유저들 또한 Safari를 사용하지 않을거라는 생각은 오산이다.
또한, Safari의 터치 스크롤 최적화는 다른 브라우저와 다르다는 것을 인지하고, Safari 전용 CSS 속성과 동작을 고려한 개발이 필요하다.
웹 개발에서 "간단해 보이는 기능"이 실제로는 복잡한 브라우저 동작과 연관되어 있는 경우가 많은 것 같다.
특히 데스크탑 웹에서 개발했지만 사용은 모바일에서 하는 경우가 많으므로 모바일 터치 이벤트를 신경써야하고 이는 데스크톱의 마우스 이벤트와 완전히 다른 패러다임을 인지하자!
이번 경험을 통해 "때로는 브라우저가 제공하는 기본 동작을 믿고 맡기는 것이 최선의 선택일 수 있겠다" 라는 생각을 했다.반응형'Front-end' 카테고리의 다른 글
프론트엔드 폴더 스트럭처 구성 종류 (폴더링 패턴) (0) 2025.09.11 프론트엔드 고화질 이미지 다루는 방법 (1) 2025.06.24 강력새로고침 하면 불러오는데 일반새로고침은 불러오지 못한다? _ service worker (0) 2025.06.04 Shadcn Dialog에서 Textarea 내부 Enter 키가 동작하지 않는다면? (1) 2025.01.10 React 프론트엔드 개발자의 상태관리 기법과 이유 (4) 2024.05.23