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

데이브 타푸스카
데이브 타푸스카

스크롤 반응성은 모바일에서 사용자의 웹사이트 참여에 매우 중요하다는 점을 알고 있지만, 터치 이벤트 리스너는 종종 스크롤 성능에 심각한 문제를 일으킵니다. 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의 사용을 이해하기 위해 스크롤 차단 동작이 얼마나 자주 발생했는지 확인하는 측정항목을 추가했습니다.

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

이러한 이유로 인해 개입을 다음과 같이 정의했습니다. 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 리스너에 수동적 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와 같은 브라우저에서는 이미 터치 작업을 올바르게 적용해야 합니다. 터치 작업을 지원하지 않는 모바일 Safari 및 이전 모바일 브라우저의 경우 Chrome에서 터치 작업을 무시하더라도 터치 리스너는 preventDefault를 계속 호출해야 합니다.

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

  • touchstart 리스너가 preventDefault()를 호출하는 경우 클릭 이벤트 및 기타 기본 탭 동작 생성을 계속 억제하려면 연결된 터치엔드 리스너에서preventDefault()도 호출해야 합니다.
  • 마지막으로 {passive: false}를 addEventListener()에 전달하여 기본 동작을 재정의하지 않는 것이 좋습니다. 사용자 에이전트가 EventListenerOptions를 지원하는지 여부를 감지해야 합니다.

결론

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

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