정렬된 입력 이벤트

Dave Tapuska
Dave Tapuska

요약

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

웹에서는 원활한 사용자 환경을 제공하는 것이 중요합니다. 입력 이벤트를 수신한 시점과 시각화가 실제로 업데이트되는 시점 사이의 시간이 중요하며 일반적으로 작업을 적게 하는 것이 중요합니다. 지난 몇 번의 Chrome 출시에서 이러한 기기의 입력 지연 시간을 줄였습니다.

원활함과 성능을 위해 Chrome 60에서는 이러한 이벤트가 더 낮은 빈도로 발생하는 동시에 제공되는 정보의 세부사항이 증가하도록 변경하고 있습니다. Jelly Bean이 출시되어 Android에서 입력을 정렬하는 Choreographer를 가져왔을 때와 마찬가지로 모든 플랫폼의 웹에 프레임 정렬 입력을 가져옵니다.

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

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

이벤트 빈도 낮추기

몇 가지 기본사항을 알아보겠습니다. 터치 스크린은 60~120Hz의 주파수로 입력을 전송하고 마우스는 일반적으로 100Hz (최대 2,000Hz까지 가능)의 주파수로 입력을 전송합니다. 하지만 모니터의 일반적인 새로고침 빈도는 60Hz입니다. 이것이 의미하는 바는 무엇일까요? 즉, 디스플레이를 실제로 업데이트하는 속도보다 더 높은 속도로 입력을 수신합니다. 간단한 캔버스 그리기 앱의 devtools에서 성능 타임라인을 살펴보겠습니다.

아래 그림에서 requestAnimationFrame() 정렬 입력이 사용 중지된 경우 프레임당 일치하지 않는 프레임 시간으로 여러 처리 블록을 볼 수 있습니다. 노란색 작은 블록은 DOM 이벤트의 타겟, 이벤트 전달, 자바스크립트 실행, 마우스 오버된 노드 업데이트, 레이아웃 및 스타일 재계산과 같은 항목의 히트 테스트를 나타냅니다.

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

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

Canary 및 Dev 채널에서 이 기능을 사용 설정하여 실험을 진행한 결과, 기본 스레드가 더 자주 실행될 수 있도록 히트 테스트가 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()와 같은 전달 관련 메서드를 호출해도 상위 이벤트에는 영향을 미치지 않습니다.