모든 웹사이트에서 전체적으로 측정할 수 있는 사용자 중심 측정항목은 매우 유용합니다. 이러한 측정항목을 통해 다음을 수행할 수 있습니다.
- 실제 사용자가 웹을 전체적으로 어떻게 경험하는지 이해할 수 있습니다.
- 자신의 사이트를 경쟁업체의 사이트와 비교합니다.
- 커스텀 코드를 작성할 필요 없이 분석 도구에서 유용하고 실행 가능한 데이터를 추적할 수 있습니다.
유니버설 측정항목도 훌륭한 기준을 제공하지만, 대부분의 경우 특정 사이트의 전체 환경을 파악하려면 이러한 측정항목 외의 더 많은 측정항목도 측정해야 합니다.
맞춤 측정항목을 사용하면 사이트에만 적용되는 다음과 같은 사이트 경험의 측면을 측정할 수 있습니다.
- 단일 페이지 앱 (SPA)이 하나의 '페이지'에서 전환하는 데 걸리는 시간 공유할 수 있습니다
- 로그인한 사용자의 데이터베이스에서 가져온 데이터를 페이지에 표시하는 데 걸리는 시간입니다.
- 서버 측 렌더링 (SSR) 앱이 하이드레이션하는 데 걸리는 시간입니다.
- 재방문자가 로드한 리소스의 캐시 적중률입니다.
- 게임에서 클릭 또는 키보드 이벤트의 이벤트 지연 시간입니다.
커스텀 측정항목 측정을 위한 API
지금까지 웹 개발자는 성능을 측정할 수 있는 하위 수준의 API가 많지 않았기 때문에 사이트의 성능을 측정하기 위해 해킹에 의존해야 했습니다.
예를 들어 requestAnimationFrame
루프를 실행하고 각 프레임 간의 델타를 계산하여 장기 실행 JavaScript 작업으로 인해 기본 스레드가 차단되었는지 확인할 수 있습니다. 델타가 디스플레이의 프레임 속도보다 훨씬 긴 경우 긴 작업으로 보고할 수 있습니다. 그러나 이러한 해킹은 실제로 성능에 영향을 미치기 때문에 권장되지 않습니다 (예: 배터리 소모).
효과적인 실적 측정의 첫 번째 규칙은 실적 측정 기법이 실적 문제를 일으키지 않는지 확인하는 것입니다. 따라서 사이트에서 측정하는 모든 맞춤 측정항목의 경우 가능하면 다음 API 중 하나를 사용하는 것이 좋습니다.
Performance Observer API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Performance Observer API는 이 페이지에서 설명하는 다른 모든 Performance API에서 데이터를 수집하고 표시하는 메커니즘입니다. 좋은 데이터를 얻기 위해서는 이 사실을 이해하는 것이 매우 중요합니다.
PerformanceObserver
를 사용하여 성능 관련 이벤트를 수동으로 구독할 수 있습니다. 이렇게 하면 유휴 기간에 API 콜백이 실행되므로 일반적으로 페이지 성능을 방해하지 않습니다.
PerformanceObserver
를 만들려면 새 성능 항목이 전달될 때마다 실행할 콜백을 전달합니다. 그런 다음 observe()
메서드를 사용하여 수신 대기할 항목의 유형을 관찰자에 알립니다.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
다음 섹션에는 관찰에 사용할 수 있는 다양한 항목 유형이 모두 나열되어 있지만 최신 브라우저에서 정적 PerformanceObserver.supportedEntryTypes
속성을 통해 어떤 항목 유형을 사용할 수 있는지 검사할 수 있습니다.
이미 발생한 항목 관찰
기본적으로 PerformanceObserver
객체는 항목이 발생할 때만 관찰할 수 있습니다. 이는 우선순위가 더 높은 리소스를 차단하지 않도록 성능 분석 코드를 지연 로드하려는 경우 문제를 일으킬 수 있습니다.
과거 항목이 발생한 후에 이를 가져오려면 observe()
를 호출할 때 buffered
플래그를 true
로 설정합니다. 브라우저는 PerformanceObserver
콜백이 처음 호출될 때 성능 입력 버퍼에서 해당 유형의 최대 버퍼 크기까지 이전 항목을 포함합니다.
po.observe({
type: 'some-entry-type',
buffered: true,
});
피해야 할 기존 성능 API
Performance Observer API 이전에는 개발자가 performance
객체에 정의된 다음 세 가지 메서드를 사용하여 성능 항목에 액세스할 수 있었습니다.
이러한 API는 계속 지원되지만 새 항목을 내보낼 때 수신 대기할 수 없으므로 사용하지 않는 것이 좋습니다. 또한 다수의 새 API (예: largest-contentful-paint
)는 performance
객체를 통해 노출되지 않으며, PerformanceObserver
를 통해서만 노출됩니다.
특별히 Internet Explorer와의 호환성이 필요하지 않다면 코드에서 이러한 메서드를 피하고 앞으로는 PerformanceObserver
를 사용하는 것이 좋습니다.
사용자 시간 API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
User Timing API는 범용 API이며 Measurement API를 사용할 수 있습니다. 이 기능을 사용하면 나중에 이 표시 사이의 지속 시간을 측정합니다.
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
Date.now()
또는 performance.now()
와 같은 API는 유사한 기능을 제공하지만 User Timing API를 사용하면 성능 도구와 원활하게 통합된다는 이점이 있습니다. 예를 들어 Chrome DevTools는 성능 패널의 사용자 시간 측정을 시각화하며, 많은 분석 제공업체에서는 또한 모든 측정을 자동으로 추적하고 소요 시간 데이터를 분석 백엔드로 전송합니다.
사용자 시간 측정을 보고하려면 PerformanceObserver를 사용하고 measure
유형의 항목을 관찰하도록 등록하면 됩니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
긴 Tasks API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Long Tasks API는 브라우저의 기본 스레드가 프레임 속도 또는 입력 지연 시간에 영향을 미칠 만큼 충분히 오랫동안 차단되었는지 파악하는 데 유용합니다. API는 50밀리초 이상 실행된 모든 작업을 보고합니다.
<ph type="x-smartling-placeholder">비용이 많이 드는 코드를 실행하거나 대용량 스크립트를 로드 및 실행해야 할 때마다 해당 코드가 기본 스레드를 차단하는지 추적하는 것이 좋습니다. 실제로 상호작용까지의 시간(TTI) 및 총 차단 시간(TBT)과 같이, 많은 상위 수준 측정항목은 Long Tasks API 자체를 기반으로 합니다.
장기 작업이 발생하는 시점을 확인하려면 PerformanceObserver를 사용하고 longtask
유형의 항목을 관찰하도록 등록하면 됩니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
긴 애니메이션 프레임 API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Long Animation Frames API는 50밀리초가 넘는 긴 작업이 아닌 긴 프레임을 확인하는 Long Tasks API의 새로운 반복 버전입니다. 이를 통해 기여 분석 개선, 잠재적으로 문제를 일으킬 수 있는 지연 범위 등 Long Tasks API의 단점이 해결됩니다.
긴 프레임이 발생하는 시점을 확인하려면 PerformanceObserver를 사용하고 long-animation-frame
유형의 항목을 관찰하도록 등록하면 됩니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
Element Timing API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
콘텐츠가 포함된 최대 페인트 (LCP) 측정항목은 가장 큰 이미지 또는 텍스트 블록이 화면에 표시된 시점을 파악하는 데 유용하지만 경우에 따라 다른 요소의 렌더링 시간을 측정해야 할 수도 있습니다.
이러한 경우에는 Element Timing API를 사용하세요. LCP API는 실제로 Element Timing API를 기반으로 빌드되며 콘텐츠가 포함된 가장 큰 요소의 자동 보고를 추가합니다. 하지만 elementtiming
속성을 명시적으로 추가하고 element
항목 유형을 관찰하도록 PerformanceObserver를 등록하여 다른 요소에 관해 보고할 수도 있습니다.
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
이벤트 타이밍 API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
다음 페인트에 대한 상호작용 (INP) 측정항목은 페이지 수명 주기 동안 모든 클릭, 탭, 키보드 상호작용을 관찰하여 전반적인 페이지 응답성을 평가합니다. 페이지의 INP는 사용자가 상호작용을 시작한 시점부터 브라우저가 사용자 입력의 시각적 결과를 표시하는 다음 프레임을 그리는 시점까지 완료하는 데 가장 오래 걸린 상호작용인 경우가 가장 많습니다.
INP 측정항목은 Event Timing API를 통해 이용할 수 있습니다. 이 API는 다음을 비롯하여 이벤트 수명 주기 동안 발생하는 여러 타임스탬프를 노출합니다.
startTime
: 브라우저에서 이벤트를 수신하는 시간입니다.processingStart
: 브라우저에서 이벤트의 이벤트 핸들러 처리를 시작할 수 있는 시간입니다.processingEnd
: 브라우저가 이 이벤트의 이벤트 핸들러에서 시작된 모든 동기 코드의 실행을 완료하는 시간입니다.duration
: 브라우저가 이벤트를 수신하는 시점부터 이벤트 핸들러에서 시작된 모든 동기 코드의 실행을 완료한 후 다음 프레임을 그릴 수 있을 때까지의 시간 (보안상의 이유로 8밀리초로 반올림됨)입니다.
다음 예에서는 이러한 값을 사용하여 맞춤 측정을 만드는 방법을 보여줍니다.
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
Resource Timing API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Resource Timing API는 개발자에게 특정 페이지의 리소스가 로드된 방식에 관한 자세한 정보를 제공합니다. API의 이름에도 불구하고 API에서 제공하는 정보는 타이밍 데이터에 국한되지 않습니다 (많은 데이터 포함). 액세스할 수 있는 기타 데이터는 다음과 같습니다.
initiatorType
: 리소스를 가져온 방법(예:<script>
또는<link>
태그,fetch()
호출)입니다.nextHopProtocol
: 리소스를 가져오는 데 사용되는 프로토콜(예:h2
또는quic
)입니다.encodedBodySize
/decodedBodySize]: 인코딩 또는 디코딩된 형식의 리소스 크기 (각각)transferSize
: 네트워크를 통해 실제로 전송된 리소스의 크기입니다. 리소스가 캐시에 의해 처리될 때 이 값은encodedBodySize
보다 훨씬 작을 수 있으며 경우에 따라 0일 수 있습니다 (캐시 재검증이 필요하지 않은 경우).
리소스 시간 항목의 transferSize
속성을 사용하여 캐시 적중률 측정항목 또는 총 캐시된 리소스 크기 측정항목을 측정할 수 있습니다. 이는 리소스 캐싱 전략이 재방문자의 성능에 어떤 영향을 미치는지 이해하는 데 유용할 수 있습니다.
다음 예는 페이지에서 요청한 모든 리소스를 기록하고 각 리소스가 캐시에 의해 처리되었는지 여부를 나타냅니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
Navigation Timing API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Navigation Timing API는 Resource Timing API와 비슷하지만 탐색 요청만 보고합니다. navigation
항목 유형도 resource
항목 유형과 비슷하지만 탐색 요청에만 적용되는 일부 추가 정보가 포함되어 있습니다 (예: DOMContentLoaded
및 load
이벤트가 실행되는 경우).
많은 개발자가 서버 응답 시간 (TTFB (Time to First Byte))을 파악하기 위해 추적하는 측정항목 중 하나는 Navigation Timing API를 통해 확인할 수 있으며, 특히 항목의 responseStart
타임스탬프입니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
서비스 워커를 사용하는 또 다른 측정항목 개발자는 탐색 요청을 위한 서비스 워커 시작 시간입니다. 이 시간은 브라우저가 가져오기 이벤트 가로채기를 시작하기 전에 서비스 워커 스레드를 시작하는 데 걸리는 시간입니다.
특정 탐색 요청의 서비스 워커 시작 시간은 entry.responseStart
과 entry.workerStart
사이의 델타에서 확인할 수 있습니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Server Timing API
브라우저 지원
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Server Timing API를 사용하면 응답 헤더를 통해 요청별 타이밍 데이터를 서버에서 브라우저로 전달할 수 있습니다. 예를 들어 특정 요청에 대해 데이터베이스에서 데이터를 조회하는 데 걸린 시간을 표시할 수 있습니다. 이는 서버 속도 저하로 인한 성능 문제를 디버깅하는 데 유용할 수 있습니다.
서드 파티 분석 서비스 제공업체를 사용하는 개발자의 경우 Server Timing API는 서버 성능 데이터를 이러한 분석 도구가 측정할 수 있는 다른 비즈니스 측정항목과 연결할 수 있는 유일한 방법입니다.
응답에 서버 시간 데이터를 지정하려면 Server-Timing
응답 헤더를 사용하면 됩니다. 예를 들면 다음과 같습니다
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
그러면 페이지에서 Resource Timing 및 Navigation Timing API의 resource
또는 navigation
항목 모두에서 이 데이터를 읽을 수 있습니다.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});