정렬된 입력 이벤트

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

요약

  • Chrome 60은 이벤트 빈도를 낮춰 버벅거림을 줄임으로써 프레임 타이밍의 일관성을 개선합니다.
  • Chrome 58에 도입된 getCoalescedEvents() 메서드는 지금까지 사용해 온 다양한 이벤트 정보를 제공합니다.

원활한 사용자 환경을 제공하는 것이 웹에는 중요합니다. 입력 이벤트를 수신한 후 시각 자료가 실제로 업데이트되는 시점 사이의 시간이 중요하며 일반적으로 작업량을 줄이는 것이 중요합니다. 지난 몇 차례 Chrome 출시를 통해 Google은 이러한 기기의 입력 지연 시간을 단축시켰습니다.

원활함과 성능을 위해 Chrome 60에서는 제공되는 정보의 세부 수준을 높이는 동시에 더 낮은 빈도로 이러한 이벤트가 발생하게 하는 변경사항을 적용하고 있습니다. Jelly Bean이 출시되고 Android의 입력을 정렬하는 Choreographer가 출시되었을 때처럼 모든 플랫폼의 웹에 프레임 정렬 입력을 제공합니다.

하지만 더 많은 이벤트가 필요한 경우도 있습니다. 따라서 Chrome 58에서는 getCoalescedEvents()라는 메서드를 구현했습니다. 이 메서드를 사용하면 애플리케이션이 수신하는 이벤트가 더 적더라도 포인터의 전체 경로를 검색할 수 있습니다.

먼저 이벤트 빈도에 대해 알아보겠습니다.

이벤트 빈도 낮추기

몇 가지 기본 사항을 이해해 보겠습니다. 터치스크린은 60~120Hz에서 입력을 전달하고, 마우스는 일반적으로 100Hz (최대 2000Hz)에서 입력을 전달합니다. 그러나 일반적으로 모니터의 화면 재생 빈도는 60Hz입니다. 그렇다면 그것은 실제로 무엇을 의미할까요? 즉, 실제로 디스플레이를 업데이트하는 것보다 더 빠른 속도로 입력을 수신합니다. 간단한 캔버스 회화 앱의 Devtools 성능 타임라인을 살펴보겠습니다.

아래 그림에서 requestAnimationFrame() 정렬 입력이 사용 중지된 상태에서는 프레임 시간이 일관되지 않은 프레임당 여러 처리 블록이 표시됩니다. 작은 노란색 블록은 DOM 이벤트 대상, 이벤트 전달, 자바스크립트 실행, 마우스 오버된 노드 업데이트, 레이아웃 및 스타일 재계산 등의 히트 테스트를 나타냅니다.

일관되지 않은 프레임 타이밍을 보여주는 성능 타임라인

그렇다면 시각적 업데이트를 야기하지 않는 추가 작업을 하는 이유는 무엇일까요? 궁극적으로 사용자에게 도움이 되지 않는 작업은 하지 않는 것이 이상적입니다. Chrome 60부터 입력 파이프라인은 연속 이벤트 전달(wheel, mousewheel, touchmove, pointermove, mousemove)을 지연시키고 requestAnimationFrame() 콜백이 발생하기 직전에 전달합니다. 아래 그림 (기능이 사용 설정된 상태에서)을 보면 프레임 시간이 좀 더 일관되고 이벤트 처리 시간은 더 짧습니다.

Canary 및 개발자 채널에서 이 기능을 사용 설정한 상태로 실험을 진행한 결과, 적중 테스트가 35% 감소하여 기본 스레드를 더 자주 실행할 수 있는 것으로 나타났습니다.

웹 개발자는 발생하는 모든 개별 이벤트 (예: keydown, keyup, mouseup, mousedown, touchstart, touchend)가 대기 중인 이벤트와 함께 즉시 전달되어 상대적인 순서가 유지된다는 점에 유의해야 합니다. 이 기능을 사용 설정하면 많은 작업이 일반 이벤트 루프 흐름으로 간소화되어 일관된 입력 간격이 제공됩니다. 이렇게 하면 Chrome의 이벤트 루프 흐름에 이미 간소화된 scrollresize 이벤트와 함께 연속 이벤트가 인라인으로 구현됩니다.

비교적 일관된 프레임 타이밍을 보여주는 성능 타임라인

이러한 이벤트를 사용하는 대다수의 애플리케이션은 더 높은 빈도로 사용되지 않는 것으로 확인되었습니다. Android는 이미 여러 해 동안 이벤트를 조정해 왔으므로 새로운 내용은 없지만 사이트의 데스크톱 플랫폼에서는 덜 세분화된 이벤트를 경험할 수 있습니다. 기본 스레드는 버벅거림이 있어 입력의 부드러움 문제를 일으킵니다. 즉, 애플리케이션이 작동할 때마다 제자리에서 점프가 발생할 수 있으므로 포인터가 한 지점에서 다른 지점으로 어떻게 이동하는지 알 수 없습니다.

getCoalescedEvents() 메서드

앞서 말했듯이 애플리케이션이 포인터의 전체 경로를 알고 싶어하는 드문 시나리오가 있습니다. 따라서 Chrome 58에서는 큰 점프와 이벤트 빈도가 줄어드는 문제를 해결하기 위해 getCoalescedEvents()라는 포인터 이벤트에 대한 확장 프로그램을 출시했습니다. 다음은 이 API를 사용하는 경우 기본 스레드의 버벅거림이 애플리케이션에서 어떻게 숨겨지는지 보여주는 예입니다.

표준 이벤트와 병합된 이벤트 비교

단일 이벤트를 수신하는 대신 이벤트를 일으킨 이전 이벤트의 배열에 액세스할 수 있습니다. Android, iOS, Windows는 모두 네이티브 SDK에 매우 유사한 API를 가지고 있으며 유사한 API를 웹에 노출하고 있습니다.

일반적으로 그리기 앱은 이벤트의 오프셋을 확인하여 점을 그렸을 수 있습니다.

window.addEventListener("pointermove", function(event) {
    drawPoint(event.pageX, event.pageY);
});

이 코드는 이벤트 배열을 사용하도록 쉽게 변경할 수 있습니다.

window.addEventListener("pointermove", function(event) {
    var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
    for (let e of events) {
    drawPoint(e.pageX, e.pageY);
    }
});

병합된 이벤트의 모든 속성이 채워지는 것은 아닙니다. 결합된 이벤트는 실제로 전달되지는 않지만 이동 중이기 때문에 적중 테스트가 진행되지 않습니다. currentTargeteventPhase와 같은 일부 필드에는 기본값이 있습니다. stopPropagation() 또는 preventDefault()와 같은 디스패치 관련 메서드를 호출하면 상위 이벤트에 영향을 미치지 않습니다.