1. 소개
1.1. 개요
이 섹션은 규범적인 내용이 아닙니다.
사용자가 웹사이트와 상호작용할 때, 자신의 행동이 빠르게 웹사이트에 변화를 일으키기를 기대합니다. 실제로 연구에 따르면 100ms 이내에 처리되지 않는 모든 사용자 입력은 느리다고 간주됩니다. 따라서 입력 이벤트가 해당 기준을 달성하지 못했을 때의 성능 타이밍 정보를 노출하는 것이 중요합니다.
이벤트 지연을 모니터링하는 일반적인 방법은 이벤트 리스너를 등록하는 것입니다.
이벤트가 생성된 시점은 이벤트의 timeStamp
로
얻을 수 있습니다.
추가로 performance.now()
를
이벤트 핸들러 로직의 시작과 끝에서 호출할 수 있습니다.
하드웨어 타임스탬프에서 이벤트 핸들러 시작 시점의 타임스탬프를 빼면 개발자는 입력 지연—입력이 처리되기 시작하는 데 걸리는 시간—을 계산할 수 있습니다.
이벤트 핸들러 시작 시점의 타임스탬프와 끝 시점의 타임스탬프의 차이를 통해 개발자는 이벤트 핸들러에서 수행된 동기 작업의 양을 계산할 수 있습니다.
마지막으로 입력이 동기적으로 처리되는 경우, 이벤트가 처리된 후 다음 페인트까지의 기간은 사용자 경험 측정에 유용한 지표가 됩니다.
이 방식에는 몇 가지 근본적인 결함이 있습니다. 첫째, 이벤트 리스너가 필요하므로 페이지 로드 초기에 이벤트 지연을 측정할 수 없습니다. 그 시점에는 리스너가 등록되지 않았기 때문입니다. 둘째, 입력 지연만 관심 있는 개발자도 원래 리스너가 없던 이벤트에 새로운 리스너를 추가해야 할 수 있습니다. 이는 이벤트 지연 계산에 불필요한 성능 오버헤드를 발생시킵니다. 마지막으로, 이 방식으로는 이벤트로 인해 발생한 비동기 작업의 측정이 매우 어렵습니다.
이 명세는 이러한 문제를 해결하는 이벤트 지연 모니터링의 대안을 제공합니다. 사용자 에이전트가 타임스탬프를 계산하므로, 성능 측정을 위해 이벤트 리스너가 필요하지 않습니다. 이는 페이지 로드 아주 초기에 발생하는 이벤트도 포착할 수 있음을 의미합니다. 또한 느린 이벤트에 대한 가시성을 높일 수 있어, 분석 제공자가 모든 이벤트에 대해 패치 및 구독을 시도할 필요가 없습니다. 웹사이트의 성능도 불필요한 이벤트 리스너로 인한 오버헤드 없이 유지됩니다. 마지막으로, 이 명세는 개발자가 이벤트 처리 직후 발생하는 렌더링의 타이밍 정보를 상세히 얻을 수 있도록 합니다. 이는 이벤트에 의해 트리거된 웹사이트 수정의 오버헤드를 측정하는 데 유용할 수 있습니다.
1.2. 상호작용
이 섹션은 규범적인 내용이 아닙니다.
하나의 사용자 Interaction
(때때로 Gesture라고도 함)은 일반적으로 여러 물리적 하드웨어 입력 이벤트로 구성됩니다.
각 물리적 입력 이벤트는 사용자 에이전트가 여러 UIEvent
를
디스패치하게 만들 수 있고,
각각은 여러 커스텀 이벤트 리스너를 트리거하거나 별도의 기본 동작을 트리거할 수 있습니다.
예를 들어, 터치스크린 디바이스에서 단일 사용자 "탭" 상호작용은 실제로 일련의 물리적 입력 이벤트로 이루어져 있습니다:
-
터치 시작,
-
아주 약간의 터치 이동,
-
터치 종료.
이 물리적 입력 이벤트들은 일련의 UIEvent
를
디스패치할 수 있습니다:
-
...그리고 중간에 Focus 이벤트, Input 이벤트 등이 추가로 있을 수 있습니다.
이 개별 UIEvent
들은
각각
자세한 타이밍 측정에 유용한 자신의 PerformanceEventTiming
엔트리 보고 후보가 됩니다.
참고: pointermove
및 touchmove
는 현재 이벤트 타이밍 대상에 포함되지 않습니다.
그러나 이 명세는 PerformanceEventTiming
들을
Interaction
으로
interactionId
를
통해 그룹화하는 메커니즘도 정의합니다.
이 메커니즘은 Interaction to
Next Paint (INP)라는 페이지 반응성 지표를 정의하는 데 사용할 수 있습니다.
1.3. 최초 입력
이 섹션은 규범적인 내용이 아닙니다.
가장 첫 번째 사용자 Interaction
은
사용자 경험에 불균형적으로 큰 영향을 미치며, 대체로 상대적으로 느린 경우가 많습니다.
이런 맥락에서 Event Timing API는 Window
의
최초 입력에 대한 타이밍 정보를 노출합니다.
여기서 최초 입력은 interactionId
가
0이 아닌
첫 번째 PerformanceEventTiming
엔트리로 정의됩니다.
대부분의 PerformanceEventTiming
과
달리,
최초 입력 엔트리는 제공된 durationThreshold
를
초과하지 않아도 보고되며,
기본 지속 시간 임계값인 104ms를 초과하지 않아도 버퍼링됩니다.
이 메커니즘은 First Input Delay
(FID)라는 페이지 반응성 지표를 정의하는 데 사용할 수 있습니다.
이로 인해 개발자는 항상 매우 반응성이 좋은 페이지의 데이터까지 포함하여 백분위수와 성능 개선을 더 잘 측정할 수 있습니다. 이벤트 핸들러를 등록하지 않아도 됩니다.
1.4. 노출되는 이벤트
Event Timing API는 특정 이벤트에 대해서만 타이밍 정보를 노출합니다.
-
event의
isTrusted
속성이 false로 설정되어 있으면 false를 반환한다. -
event의
type
이 다음 중 하나라면:auxclick
,click
,contextmenu
,dblclick
,mousedown
,mouseenter
,mouseleave
,mouseout
,mouseover
,mouseup
,pointerover
,pointerenter
,pointerdown
,pointerup
,pointercancel
,pointerout
,pointerleave
,gotpointercapture
,lostpointercapture
,touchstart
,touchend
,touchcancel
,keydown
,keypress
,keyup
,beforeinput
,input
,compositionstart
,compositionupdate
,compositionend
,dragstart
,dragend
,dragenter
,dragleave
,dragover
,drop
, true를 반환한다. -
false를 반환한다.
참고: mousemove
,
pointermove
,
pointerrawupdate
,
touchmove
,
wheel
,
그리고 drag
는 "연속적인" 이벤트이기 때문에 제외됩니다.
현행 API는 이러한 이벤트를 개별 엔트리 기반의 의미 있는 성능 지표로 계산하고 집계하는 방법에 대한 충분한 가이드가 없습니다.
따라서 이러한 이벤트 타입은 노출되지 않습니다.
1.5. 이벤트가 측정되는 시점
이 섹션은 규범적인 내용이 아닙니다. § 3 처리 모델 섹션에서 노출되는 정보의 상위 수준 설명입니다.
이벤트 타이밍 정보는 특정 이벤트에 대해서만, 그리고 사용자 입력과 입력 처리 이후 페인트 작업 사이의 시간 차가 특정 지속 시간 임계값을 초과할 때만 노출됩니다.
Event Timing API는 duration
값을 노출합니다. 이는 실제 사용자 입력 발생 시점(Event
의
timeStamp
로
추정)
부터 입력 처리 후 Event
의
관련 글로벌 객체의 연결된 Document가 업데이트되는 시점까지의 시간입니다.
이 값은 8밀리초 단위로 제공됩니다.
기본적으로 Event Timing API는 duration
이
104 이상일 때 버퍼링 및 노출하지만, 개발자는 PerformanceObserver
를
설정하여 다른 임계값을 가진 향후 엔트리를 관찰할 수 있습니다.
이 설정은 버퍼링되는 엔트리에 영향을 주지 않으므로 buffered
플래그는 기본 임계값 이상인 과거 엔트리만 수신할 수 있도록 합니다.
Event
의
delay란 브라우저가 해당 이벤트의 핸들러를 실행하려는 시점과 Event
의
timeStamp
의
차이입니다.
앞선 시점은 PerformanceEventTiming
의
processingStart
로,
뒤의 시점은 PerformanceEventTiming
의
startTime
으로
노출됩니다.
따라서 Event
의
지연은
으로
계산할 수 있습니다.
processingStart
startTime
Event Timing API는 이벤트에 리스너가 등록되어 있든 없든 엔트리를 생성합니다.
특히 최초 클릭이나 최초 키 입력은 사용자가 실제로 페이지 기능을 사용하려는 시도가 아닐 수도 있습니다;
많은 사용자가 텍스트를 선택하거나 빈 영역을 클릭해 포커스를 조정하는 행동을 합니다.
이는 이벤트 리스너가 너무 늦게 등록된 페이지의 문제를 포착하고, 리스너가 없어도 의미 있는 입력(예: hover 효과)의 성능을 포착하기 위한 설계 선택입니다.
개발자는
값이 사실상 0인 엔트리를 무시함으로써 이러한 엔트리를 걸러낼 수 있습니다.
processingEnd
processingStart
processingEnd
는
이벤트 디스패치 알고리즘이 종료된 시점을 나타냅니다.
1.6. 사용 예시
const observer= new PerformanceObserver( function ( list, obs) { for ( let entryof list. getEntries()) { // Input Delay const inputDelay= entry. processingStart- entry. startTime; // Processing duration const processingDuration= entry. processingEnd- entry. processingStart; // Presentation Delay (approximate) const presentationDelay= Math. max( 0 , entry. startTime+ entry. duration- entry. processingEnd); // Obtain some information about the target of this event, such as the id. const targetId= entry. target? entry. target. id: 'unknown-target' ; console. log( entry. entryType, entry. name, entry. duration, { inputDelay, processingDuration, presentationDelay}); } }); observer. observe({ type: 'first-input' , buffered: true }); observer. observe({ type: 'event' , buffered: true , durationThreshold: 40 });
아래 예시는 interactionId
별로
각각의 이벤트 중 최대 지속 시간을 매핑하는 딕셔너리를 계산합니다.
이 딕셔너리는 나중에 집계되어 분석에 보고할 수 있습니다.
let maxDurations= {}; new PerformanceObserver( list=> { for ( let entryof list. getEntries()) { if ( entry. interactionId> 0 ) { let id= entry. interactionId; if ( ! maxDurations[ id]) { maxDurations[ id] = entry. duration; } else { maxDurations[ id] = Math. max( maxDurations[ id], entry. duration); } } } }). observe({ type: 'event' , buffered: true , durationThreshold: 16 });
아래는 이 API를 이용해 달성할 수 있는 샘플 사용 사례입니다:
-
웹사이트에서 최초 입력 지연 데이터를 수집하고 성능을 추적합니다.
-
버튼 클릭으로 테이블의 정렬 순서를 변경합니다. 클릭부터 재정렬된 콘텐츠 표시까지의 시간을 측정합니다.
-
사용자가 슬라이더를 드래그하여 볼륨을 조절합니다. 슬라이더 드래그 지연 시간을 측정합니다.
-
메뉴 항목에 마우스를 올리면 플라이아웃 메뉴가 나타납니다. 플라이아웃이 표시될 때까지의 지연을 측정합니다.
-
첫 번째 사용자 클릭의 지연 시간의 75번째 백분위수를 측정합니다(클릭이 최초 사용자 상호작용일 때).
2. 이벤트 타이밍
이벤트 타이밍은 다음과 같은 인터페이스를 추가합니다:
2.1.
PerformanceEventTiming
인터페이스
[Exposed =Window ]interface :
PerformanceEventTiming PerformanceEntry {readonly attribute DOMHighResTimeStamp processingStart ;readonly attribute DOMHighResTimeStamp processingEnd ;readonly attribute boolean cancelable ;readonly attribute Node ?;
target readonly attribute unsigned long long interactionId ; [Default ]object (); };
toJSON
PerformanceEventTiming
객체는 하나의 연결된 Event
에
대한 타이밍 정보를 보고합니다.
각 PerformanceEventTiming
객체는 다음과 같은 연결된 개념을 가지며, 모두 초기에
로 설정됩니다:
-
연결된
Node
를 포함하는 eventTarget.
target
속성의 getter는 다음 단계를 수행해야 합니다:
-
this의 eventTarget이 null로 주어졌을 때 페인트 타이밍에 대해 노출되지 않는 경우, null을 반환한다.
-
this의 eventTarget을 반환한다.
참고: Event Timing API를 구현하는 사용자 에이전트는 first
과 event
를 supportedEntryTypes
에 포함해야 하며,
Window
컨텍스트에 적용됩니다.
이는 개발자가 이벤트 타이밍 지원 여부를 감지할 수 있게 합니다.
이 섹션의 나머지는 규범적인 내용이 아닙니다.
PerformanceEventTiming
속성의 값은 § 3 처리 모델에서 설정됩니다.
이 섹션은 그 값이 어떻게 설정되는지 정보 제공 차원의 요약을 포함합니다.
PerformanceEventTiming
은 PerformanceEntry
인터페이스의 다음 속성을 확장합니다:
name
name
속성의 getter는 연결된 이벤트의type
을 제공합니다.entryType
entryType
속성의 getter는 "event"(긴 이벤트) 또는 "first- input"(최초 사용자 상호작용) 중 하나를 반환합니다.startTime
startTime
속성의 getter는 연결된 이벤트의timeStamp
를 반환합니다.duration
duration
속성의 getter는 렌더링 업데이트 단계가 연결된 이벤트의Document
에서 해당 이벤트가 디스패치된 후 완료되는 다음 시점과startTime
간의 차이를 8ms 단위로 반올림하여 반환합니다.
PerformanceEventTiming
은 다음 추가 속성을 가집니다:
processingStart
-
processingStart
속성의 getter는 이벤트 디스패치 알고리즘 시작 시점을 캡처한 타임스탬프를 반환합니다. 이 시점은 이벤트 핸들러가 실행되기 직전입니다. processingEnd
-
processingEnd
속성의 getter는 이벤트 디스패치 알고리즘 종료 시점을 캡처한 타임스탬프를 반환합니다. 이 시점은 이벤트 핸들러 실행이 완료된 직후입니다. 해당 핸들러가 없으면processingStart
와 동일합니다. cancelable
-
cancelable
속성의 getter는 연결된 이벤트의cancelable
속성 값을 반환합니다. target
-
target
속성의 getter는 연결된 이벤트의 마지막target
을 반환합니다. 이Node
가 분리되거나 섀도우 DOM에 속하지 않는 경우에만 해당합니다. interactionId
-
interactionId
속성의 getter는 연결된 이벤트를 트리거한 사용자Interaction
을 고유하게 식별하는 숫자를 반환합니다. 이 속성은 연결된 이벤트의type
속성 값이 다음 중 하나일 때만 0이 아닙니다:-
pointerdown
,pointerup
, 또는click
이며, 탭 또는 드래그 제스처에 속합니다.pointerdown
이 스크롤로 끝나는 경우는 제외됩니다.
-
2.2. EventCounts
인터페이스
[Exposed =Window ]interface {
EventCounts readonly maplike <DOMString ,unsigned long long >; };
EventCounts
객체는 키가 이벤트 type이고 값이 해당 type
의
이벤트가 디스패치된 횟수인 맵입니다.
type
이
PerformanceEventTiming
엔트리(섹션 § 1.4 노출되는 이벤트 참고)에서 지원되는 이벤트만 이 맵을 통해 집계됩니다.
2.3. Performance
인터페이스 확장
[Exposed =Window ]partial interface Performance { [SameObject ]readonly attribute EventCounts ;
eventCounts readonly attribute unsigned long long ; };
interactionCount
eventCounts
속성의 getter는 this의
관련 글로벌 객체의 eventCounts를 반환합니다.
interactionCount
속성의 getter는 this의
관련 글로벌 객체의 interactionCount를 반환합니다.
3. 처리 모델
3.1. DOM 명세 수정 사항
이 섹션은 [DOM]이 수정된 후 삭제될 예정입니다.
1단계 바로 뒤에 다음 단계를 추가합니다:
-
interactionId를 compute-interactionId 알고리즘을 event로 실행한 결과로 한다.
-
timingEntry를 initialize-event-timing 알고리즘을 event, 현재 고해상도 시각, interactionId로 실행한 결과로 한다.
해당 알고리즘의 반환 단계 바로 앞에 다음 단계를 추가합니다:
-
finalize-event-timing 알고리즘을 timingEntry, event, target, 현재 고해상도 시각을 인자로 실행한다.
참고: 사용자 에이전트가 이벤트 디스패치
알고리즘을 생략하는 경우에도 해당 Event
에
대한 엔트리를 포함할 수 있습니다.
이 경우 processingStart
값을 추정하고 processingEnd
를
그 값과 동일하게 설정합니다.
3.2. HTML 명세 수정 사항
이 섹션은 [HTML]이 수정된 후 삭제될 예정입니다.
각 Window
는 다음과 같은 개념을 가집니다:
-
entries-to-be-queued:
PerformanceEventTiming
객체를 저장하는 리스트로, 초기에는 비어 있습니다. -
has-dispatched-input-event: 초기값이 false인 불리언입니다.
-
user-interaction-value: 초기값이 100~10000 사이의 랜덤 정수인 정수입니다.
참고: user-interaction-value는 0이 아닌 임의의 값으로 설정되어, 개발자가 페이지 내 상호작용 수를 이 값에 의존하지 못하도록 합니다. 랜덤값으로 시작하면 실제 상호작용 횟수의 근거로 사용될 가능성이 줄어듭니다.
-
pending-key-downs: 정수를 키로
PerformanceEventTimings
를 값으로 하는 맵으로, 초기에는 비어 있습니다. -
pointer-interaction-value-map: 초기에는 비어 있는 정수 맵입니다.
-
pending-pointer-downs: 정수를 키로
PerformanceEventTimings
를 값으로 하는 맵으로, 초기에는 비어 있습니다. -
is-contextmenu-triggered: 초기값이 false인 불리언입니다.
-
eventCounts: type → numEvents 형태의 맵. 즉, type과 동일한
type
을 가진 이벤트가 numEvents번 디스패치된 것입니다.Performance
객체가 관련 글로벌 객체로Window
를 가지면, eventCounts는 § 1.4 노출되는 이벤트에서 지원하는 모든 이벤트 타입을 0으로 초기화한 맵으로 초기화되어야 합니다. -
interactionCount: compute-interactionId로 계산된 고유한
interactionId
에 대한 총 사용자 상호작용 수를 집계하는 정수입니다.
-
각 완전히 활성화된
Document
에 대해 docs에서 dispatch-pending-event-timing-entries 알고리즘을 실행한다.
3.3. 성능 타임라인 명세 수정 사항
이 섹션은 [PERFORMANCE-TIMELINE-2]가 수정된 후 삭제될 예정입니다.
PerformanceObserverInit
딕셔너리에 다음 멤버가 추가됩니다:
partial dictionary PerformanceObserverInit {DOMHighResTimeStamp ; };
durationThreshold
3.4. PerformanceEventTiming 추가 여부 결정
참고: 다음 알고리즘은 [PERFORMANCE-TIMELINE-2] 명세에서
PerformanceEventTiming
엔트리를 PerformanceObserver
버퍼 또는 성능 타임라인에 추가할지(상세 내용은 레지스트리 참고) 판단하는 데
사용됩니다.
PerformanceEventTiming
entry와 PerformanceObserverInit
options가 주어졌을 때,
PerformanceEventTiming 추가 여부를 결정하려면 entry 및
필요에 따라 options를 인자로 다음 단계를 실행한다:
-
entry의
entryType
속성 값이 "first
"이면 true를 반환한다.- input -
entry의
entryType
속성 값이 "event
"임을 단언한다. -
minDuration을 다음과 같이 계산한다:
-
options가 없거나 options의
durationThreshold
가 없으면 minDuration을 104로 한다. -
그 외에는 minDuration을 16과 options의
durationThreshold
값 중 큰 값으로 한다.
-
-
entry의
duration
값이 minDuration 이상이면 true를 반환한다. -
그렇지 않으면 false를 반환한다.
3.5. 상호작용 수 증가
Window
window 객체에 대해 상호작용 수 증가를 요청받으면 다음 단계를 수행한다:
-
window의 user interaction value 값을 사용자 에이전트가 선택한 작은 값만큼 증가시킨다.
-
interactionCount를 window의 interactionCount 값으로 한다.
-
interactionCount를 interactionCount + 1로 설정한다.
참고: user interaction value 값은 1이 아닌 사용자 에이전트가 선택한 작은 값만큼 증가시키며, 이는 개발자가 웹 애플리케이션의 실제 상호작용 횟수 카운터로 오용하지 않도록 하기 위함이다. 이 방식은 사용자 에이전트가 user interaction value를 미리 할당(예: pointerdown 시점)하고, 이후(pointercancel 등)에는 폐기할 수 있게 한다.
사용자 에이전트는 매번 작은 랜덤 정수로 증가시키거나, 상수를 사용할 수 있다.
모든 Window
에
대해 공유된 글로벌 user
interaction value를 사용해서는 안 되며, 이는 크로스 오리진 정보 누출을 일으킬 수 있기 때문이다.
3.6. interactionId 계산
-
event의
isTrusted
속성이 false이면 0을 반환한다. -
type을 event의
type
속성값으로 한다. -
type이
keyup
,compositionstart
,input
,pointercancel
,pointerup
,click
,contextmenu
중 하나가 아니라면 0을 반환한다.참고:
keydown
과pointerdown
은 finalize event timing 단계에서 pending으로 처리되며, 이후 interactionId 계산 알고리즘에서 처리된다 (keyup
,pointerup
등). -
window를 event의 관련 글로벌 객체로 한다.
-
pendingKeyDowns를 window의 pending key downs로 한다.
-
pointerMap을 window의 pointer interaction value map으로 한다.
-
pendingPointerDowns를 window의 pending pointer downs로 한다.
-
type이
keyup
인 경우:-
event의
isComposing
값이 true이면 0을 반환한다. -
code를 event의
keyCode
값으로 한다. -
pendingKeyDowns[code]가 없으면 0을 반환한다.
-
entry를 pendingKeyDowns[code]로 한다.
-
상호작용 수 증가를 window에 대해 실행한다.
-
interactionId를 window의 user interaction value 값으로 한다.
-
entry의
interactionId
를 interactionId로 설정한다. -
entry를 window의 entries to be queued에 추가한다.
-
Remove pendingKeyDowns[code]을 실행한다.
-
interactionId를 반환한다.
-
-
type이
compositionstart
인 경우:-
pendingKeyDowns의 값들에 대해 각 entry마다:
-
entry를 window의 entries to be queued에 추가한다.
-
-
Clear pendingKeyDowns를 실행한다.
-
0을 반환한다.
-
-
type이
input
인 경우:-
event가
InputEvent
인스턴스가 아니면 0을 반환한다. 참고: 이 검사는Event
중input
타입이지만 실제 텍스트 변경과 관련 없는 이벤트를 제외하기 위한 것이다. -
event의
isComposing
값이 false이면 0을 반환한다. -
상호작용 수 증가를 window에 대해 실행한다.
-
window의 user interaction value 값을 반환한다.
-
-
그 외 (type이
pointercancel
,pointerup
,click
,contextmenu
인 경우):-
pointerId를 event의
pointerId
값으로 한다. -
type이
click
인 경우:-
pointerMap[pointerId]가 없으면 0을 반환한다.
-
value를 pointerMap[pointerId]로 한다.
-
pointerMap[pointerId]를 제거한다.
-
value를 반환한다.
-
-
type이
pointerup
,pointercancel
,contextmenu
임을 단언한다. -
pendingPointerDowns[pointerId]가 없으면:
-
type이
contextmenu
인 경우, window의 user interaction value를 반환한다. -
type이
pointerup
이고 window의 is contextmenu triggered 플래그가 true이면:-
window의 is contextmenu triggered 플래그를 false로 설정한다.
-
window의 user interaction value를 반환한다.
-
-
그 외에는 0을 반환한다.
-
-
pointerDownEntry를 pendingPointerDowns[pointerId]로 한다.
-
pointerDownEntry가
PerformanceEventTiming
엔트리임을 단언한다. -
type이
pointerup
또는contextmenu
인 경우:-
상호작용 수 증가를 window에 대해 실행한다.
-
pointerMap[pointerId]를 window의 user interaction value 값으로 설정한다.
-
pointerDownEntry의
interactionId
를 pointerMap[pointerId] 값으로 설정한다.
-
-
pointerDownEntry를 window의 entries to be queued에 추가한다.
-
pendingPointerDowns[pointerId]를 제거한다.
-
type이
contextmenu
이면, window의 is contextmenu triggered를 true로 설정한다. -
type이
pointercancel
이면, 0을 반환한다. -
pointerMap[pointerId] 값을 반환한다.
-
참고: 이 알고리즘은 이벤트를 적절한 interactionId에 할당하려고 시도한다.
키보드 이벤트의 경우 keydown
은 새로운 interactionId를 트리거하고, keyup
은 반드시 이전 keydown
과
일치하는 ID를 가져야 한다.
포인터 이벤트의 경우 pointerdown
이후 pointercancel
또는 pointerup
이
발생해야 interactionId를 알 수 있다.
click
은 이전 pointerdown
의
interactionId와 매칭된다.
pointercancel
또는 pointerup
이
발생하면, 저장된 pointerdown
엔트리에 interactionId를 설정할 수 있다.
pointercancel
이면 pointerdown
에
interactionId를 할당하지 않는다.
pointerup
이면,
새로운 interactionId를 계산해 pointerdown
과
pointerup
에
설정하고, 이후 click
에도
적용한다.
3.7. 이벤트 타이밍 초기화
-
event가 이벤트 타이밍 대상이 아니라면 null을 반환한다.
-
timingEntry를 event의 관련 realm에서 새
PerformanceEventTiming
객체로 생성한다. -
timingEntry의
entryType
을 "event
"로 설정한다. -
timingEntry의
processingStart
를 processingStart로 설정한다. -
timingEntry의
cancelable
을 event의cancelable
값으로 설정한다. -
timingEntry의
interactionId
를 interactionId로 설정한다. -
timingEntry를 반환한다.
3.8. 이벤트 타이밍 종료
-
timingEntry가 null이면 반환한다.
-
relevantGlobal을 target의 관련 글로벌 객체로 한다.
-
timingEntry의
processingEnd
를 processingEnd로 설정한다. -
target이
Node
를 구현함을 단언한다.참고: 이 단언은 Event Timing API가 지원하는 이벤트 타입 때문에 항상 성립한다.
-
timingEntry의 연결된 eventTarget을 target으로 설정한다.
참고: 마지막 이벤트 타겟으로 설정된다. eventTarget은 재타겟팅이 발생하면 루트에 가장 가까운, 마지막 타겟이 사용된다.
-
event의
type
값이pointerdown
인 경우:-
pendingPointerDowns를 relevantGlobal의 pending pointer downs로 한다.
-
pointerId를 event의
pointerId
로 한다. -
pendingPointerDowns[pointerId]가 존재하면:
-
previousPointerDownEntry를 pendingPointerDowns[pointerId]로 한다.
-
previousPointerDownEntry를 relevantGlobal의 entries to be queued에 추가한다.
-
-
pendingPointerDowns[pointerId]를 timingEntry로 설정한다.
-
window의 is contextmenu triggered를 false로 설정한다.
-
-
그 외, event의
type
값이keydown
인 경우:-
event의
isComposing
값이
라면:true -
timingEntry를 relevantGlobal의 entries to be queued에 추가한다.
-
반환한다.
-
-
pendingKeyDowns를 relevantGlobal의 pending key downs로 한다.
-
code를 event의
keyCode
값으로 한다. -
pendingKeyDowns[code]가 존재하면:
-
previousKeyDownEntry를 pendingKeyDowns[code]로 한다.
-
code가 229가 아니면:
-
window의 user interaction value 값을 사용자 에이전트가 선택한 작은 값만큼 증가시킨다.
-
previousKeyDownEntry의
interactionId
를 window의 user interaction value로 설정한다.
참고: 229는 IME 키보드 이벤트로, 사용자 에이전트가 여러 개의 이벤트를 보낼 수 있으며 이는 키를 반복적으로 누르는 것과는 다르다.
-
-
previousKeyDownEntry를 window의 entries to be queued에 추가한다.
-
-
pendingKeyDowns[code]를 timingEntry로 설정한다.
-
-
그 외:
-
timingEntry를 relevantGlobal의 entries to be queued에 추가한다.
-
3.9. 대기 중인 이벤트 타이밍 엔트리 디스패치
Document
doc에 대해 요청받으면 다음 단계를 실행한다:
-
window를 doc의 관련 글로벌 객체로 한다.
-
renderingTimestamp를 현재 고해상도 시각으로 한다.
-
window의 entries to be queued의 각 timingEntry에 대해:
-
이벤트 타이밍 엔트리 duration 설정을 timingEntry, window, renderingTimestamp로 실행한다.
-
-
window의 entries to be queued를 비운다.
-
window의 pending pointer downs의 값들에 대해 각 pendingPointerDownEntry마다:
-
이벤트 타이밍 엔트리 duration 설정을 pendingPointerDownEntry, window, renderingTimestamp로 실행한다.
-
-
window의 pending key downs의 값들에 대해 각 pendingKeyDownEntry마다:
-
이벤트 타이밍 엔트리 duration 설정을 pendingKeyDownEntry, window, renderingTimestamp로 실행한다.
-
PerformanceEventTiming
timingEntry, Window
window, DOMHighResTimeStamp
renderingTimestamp로 요청받으면 다음 단계를 실행한다:
-
timingEntry의
duration
값이 0이 아니면 반환한다. -
start를 timingEntry의
startTime
값으로 한다. -
timingEntry의
duration
을DOMHighResTimeStamp
타입의renderingTimestamp
계산 결과(정밀도 8ms 이하)로 설정한다.- start -
name을 timingEntry의
name
값으로 한다. -
이벤트 카운트 업데이트를 위해 다음 단계를 실행한다:
-
eventCounts를 window의 eventCounts로 한다.
-
eventCounts가 name을 포함함을 단언한다.
-
Set eventCounts[name]를 eventCounts[name] + 1로 설정한다.
-
-
window의 has dispatched input event가 false이고 timingEntry의
interactionId
가 0이 아니면 다음 단계를 실행한다:-
firstInputEntry를 timingEntry의 복사본으로 한다.
-
firstInputEntry의
entryType
을 "first
"로 설정한다.- input -
queue firstInputEntry를 실행한다.
-
window의 has dispatched input event를 true로 설정한다.
-
4. 보안 및 개인정보 보호 고려사항
고해상도 타이머는 보안상 우려가 있기 때문에 웹 플랫폼에 더 많은 고해상도 타이머를 도입하고 싶지는 않습니다.
이벤트 핸들러 타임스탬프는 performance.now()
와
동일한 정밀도를 가집니다.
processingStart
와
processingEnd
는 이 API를 사용하지 않고도 계산할 수 있으므로,
해당 속성을 노출해도 새로운 공격면이 생기지 않습니다.
따라서 duration
만이 추가적인 고려가 필요합니다.
duration
은 8밀리초 단위로 반올림되어 제공됩니다.
따라서 이 타임스탬프로 고해상도 타이머를 생성할 수 없습니다.
하지만 이 값은 웹 개발자가 쉽게 알 수 없는, 이벤트 처리 후 픽셀이 그려지는 시간이라는 새로운 정보를 제공합니다.
해당 타임스탬프 노출에 대해, 특히 그 정밀도에 비추어 볼 때 보안이나 프라이버시 측면의 우려는 발견되지 않았습니다.
유용한 새로운 정보를 최소한만 노출하기 위해 8밀리초 정밀도를 선택했습니다.
이는 120Hz 디스플레이에서도 비교적 정확한 타이밍 측정을 가능하게 합니다.
duration
의 기본 컷오프 값으로 104ms를 선택한 것은 100ms보다 큰 첫 번째 8의 배수이기 때문입니다.
반올림된 duration이 104ms 이상이면, 반올림 전 duration은 100ms 이상이 됩니다.
이런 이벤트는 100ms 이내에 처리되지 않아 사용자 경험에 부정적인 영향을 줄 가능성이 높습니다.
durationThreshold
의 최소값으로 16ms를 선택한 이유는 일반적인 사용 사례에서
반응이 부드럽게 이루어짐을 보장하기 위함입니다. 120Hz 디스플레이에서는 프레임 하나 이상 건너뛰면 16ms 이상이 되고,
해당 사용자 입력에 대응하는 엔트리는 최소값 기준으로 API에 노출됩니다.