기본적으로 터치 스크롤 속도 향상

Dave Tapuska
Dave Tapuska

스크롤 응답성은 모바일에서 웹사이트에 참여하는 사용자에게 중요하지만 터치 이벤트 리스너는 심각한 스크롤 성능 문제를 일으키는 경우가 많습니다. Chrome은 터치 이벤트 리스너가 패시브({passive: true} 옵션을 addEventListener()에 전달)되도록 허용하고 포인터 이벤트 API를 제공하여 이 문제를 해결해 왔습니다. 스크롤을 차단하지 않는 모델에 새 콘텐츠를 유도하는 데 유용한 기능이지만 개발자가 이해하고 채택하기 어려운 경우도 있습니다.

Google은 개발자가 브라우저 동작의 난해한 세부정보를 이해하지 않아도 기본적으로 웹이 빠르게 작동해야 한다고 생각합니다. Chrome 56에서는 개발자의 의도와 가장 일치하는 경우에 터치 리스너를 기본적으로 패시브로 설정합니다. 이를 통해 사이트에 미치는 부정적인 영향을 최소화하면서 사용자 환경을 크게 개선할 수 있을 것으로 기대합니다.

드물지만 이 변경으로 인해 의도치 않은 스크롤이 발생할 수 있습니다. 일반적으로 스크롤이 발생해서는 안 되는 요소에 touch-action: none 스타일을 적용하면 쉽게 해결할 수 있습니다. 자세한 내용, 영향을 받는지 확인하는 방법, 취할 수 있는 조치를 알아보세요.

배경: 취소 가능한 이벤트로 인해 페이지 속도가 느려짐

touchstart 또는 첫 번째 touchmove 이벤트에서 preventDefault()를 호출하면 스크롤이 방지됩니다. 문제는 리스너가 preventDefault()를 호출하지 않는 경우가 대부분이지만 브라우저는 이벤트가 완료될 때까지 기다려야 이를 확신할 수 있다는 점입니다. 개발자가 정의한 '패시브 이벤트 리스너'를 사용하면 이 문제를 해결할 수 있습니다. 이벤트 핸들러에서 {passive: true} 객체를 세 번째 매개변수로 사용하여 터치 이벤트를 추가하면 브라우저에 touchstart 리스너가 preventDefault()를 호출하지 않으며 브라우저가 리스너에서 차단하지 않고도 안전하게 스크롤을 실행할 수 있다고 알리는 것입니다. 예를 들면 다음과 같습니다.

window.addEventListener("touchstart", func, {passive: true} );

개입

주된 동기는 사용자가 화면을 터치한 후 디스플레이를 업데이트하는 데 걸리는 시간을 줄이는 것입니다. touchstart 및 touchmove의 사용을 파악하기 위해 스크롤 차단 동작이 발생하는 빈도를 확인하는 측정항목을 추가했습니다.

루트 타겟 (창, 문서 또는 본문)으로 전송된 취소 가능한 터치 이벤트의 비율을 살펴본 결과, 이러한 리스너의 약 80% 가 개념적으로 수동적이지만 그렇게 등록되지 않은 것으로 확인되었습니다. 이 문제의 규모를 감안할 때 이러한 이벤트를 자동으로 '수동'으로 설정하여 개발자 작업 없이 스크롤을 개선할 수 있는 좋은 기회가 있었습니다.

이에 따라 다음과 같이 개입을 정의했습니다. touchstart 또는 touchmove 리스너의 대상이 window, document 또는 body인 경우 passive이 기본적으로 true로 설정됩니다. 즉, 다음과 같은 코드가

window.addEventListener("touchstart", func);

다음과 동일합니다.

window.addEventListener("touchstart", func, {passive: true} );

이제 리스너 내의 preventDefault() 호출은 무시됩니다.

아래 그래프는 사용자가 화면을 터치하여 스크롤한 시점부터 디스플레이가 업데이트된 시점까지 스크롤 중 상위 1% 에 해당하는 스크롤에 걸린 시간을 보여줍니다. 이 데이터는 Android용 Chrome의 모든 웹사이트에 적용됩니다. 개입이 사용 설정되기 전에는 스크롤의 1% 가 400ms가 조금 넘게 걸렸습니다. 이제 Chrome 56 베타에서 250ms를 약간 넘게 줄였으며, 이는 약 38%의 감소입니다. 향후 모든 touchstarttouchmove 리스너의 기본값을 passive true로 설정하여 50ms 미만으로 줄일 수 있기를 바랍니다.

상위 1% 스크롤 시간 그래프

손상 및 안내

대부분의 경우 중단이 발생하지 않습니다. 그러나 중단이 발생하면 가장 일반적인 증상은 원치 않는 스크롤이 발생하는 것입니다. 드물지만 touchend 리스너에서 preventDefault()가 누락된 경우 개발자가 예상치 못한 클릭 이벤트를 발견할 수도 있습니다.

Chrome 56 이상에서는 개입이 활성 상태인 이벤트에서 preventDefault()를 호출하면 DevTools에 경고가 기록됩니다.

touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

애플리케이션은 preventDefault 호출이 defaultPrevented 속성을 통해 영향을 미쳤는지 확인하여 실제 환경에서 이 문제가 발생할 수 있는지 확인할 수 있습니다.

영향을 받는 페이지의 대부분은 가능하면 언제든지 touch-action CSS 속성을 적용하여 비교적 쉽게 수정할 수 있습니다. 요소 내에서 모든 브라우저 스크롤 및 확대/축소를 방지하려면 요소에 touch-action: none를 적용합니다. 가로 캐러셀이 있는 경우 사용자가 여전히 정상적으로 세로로 스크롤하고 확대/축소할 수 있도록 touch-action: pan-y pinch-zoom를 적용하는 것이 좋습니다. 터치 이벤트가 아닌 포인터 이벤트를 지원하는 데스크톱 Edge와 같은 브라우저에서는 이미 touch-action을 올바르게 적용해야 합니다. 터치 작업을 지원하지 않는 모바일 Safari 및 이전 모바일 브라우저의 경우 터치 리스너는 Chrome에서 무시하더라도 preventDefault를 계속 호출해야 합니다.

더 복잡한 경우에는 다음 중 하나를 사용해야 할 수도 있습니다.

  • touchstart 리스너가 preventDefault()를 호출하는 경우 연결된 touchend 리스너에서도 preventDefault()가 호출되어 클릭 이벤트 및 기타 기본 탭 동작의 생성을 계속 억제하도록 합니다.
  • 마지막으로 (권장하지 않음) {passive: false}를 addEventListener()에 전달하여 기본 동작을 재정의합니다. 사용자 에이전트가 EventListenerOptions를 지원하는지 기능 감지해야 합니다.

결론

Chrome 56에서는 많은 웹사이트에서 스크롤이 훨씬 더 빠르게 시작됩니다. 이 변경사항으로 인해 대부분의 개발자가 경험하게 되는 유일한 영향입니다. 경우에 따라 개발자가 의도하지 않은 스크롤을 발견할 수 있습니다.

모바일 Safari의 경우 여전히 이렇게 해야 하지만 웹사이트는 touchstarttouchmove 리스너 내부에서 preventDefault()를 호출하는 데 의존해서는 안 됩니다. Chrome에서 더 이상 이러한 호출이 보장되지 않기 때문입니다. 개발자는 터치 이벤트가 발생하기 전에 브라우저에 알리기 위해 스크롤 및 확대/축소를 사용 중지해야 하는 요소에 touch-action CSS 속성을 적용해야 합니다. 탭의 기본 동작 (예: 클릭 이벤트 생성)을 억제하려면 touchend 리스너 내에서 preventDefault()를 호출합니다.