탐색 및 Resource Timing API를 사용하여 현장에서 로드 성능을 평가하는 방법을 알아보세요.
게시일: 2021년 10월 8일
로드 성능을 평가하기 위해 브라우저 개발자 도구의 네트워크 패널 (또는 Chrome의 Lighthouse)에서 연결 조절을 사용해 본 적이 있다면 이러한 도구가 성능 조정에 얼마나 편리한지 아실 것입니다. 일관되고 안정적인 기준 연결 속도로 성능 최적화의 영향을 빠르게 측정할 수 있습니다. 유일한 문제는 합성 테스트이므로 현장 데이터가 아닌 실험실 데이터가 생성된다는 점입니다.
합성 테스트는 본질적으로 나쁘지 않지만 실제 사용자의 웹사이트 로드 속도를 대표하지는 않습니다. 이를 위해서는 Navigation Timing 및 Resource Timing API에서 수집할 수 있는 필드 데이터가 필요합니다.
현장의 로드 성능을 평가하는 데 도움이 되는 API
Navigation Timing과 Resource Timing은 두 개의 서로 다른 항목을 측정하는 두 개의 유사한 API로 상당한 중복이 있습니다.
- Navigation Timing은 HTML 문서 (탐색 요청)의 요청 속도를 측정합니다.
- Resource Timing은 CSS, JavaScript, 이미지, 기타 리소스 유형과 같은 문서 종속 리소스의 요청 속도를 측정합니다.
이러한 API는 JavaScript로 브라우저에서 액세스할 수 있는 성능 입력 버퍼에 데이터를 노출합니다. 성능 버퍼를 쿼리하는 방법에는 여러 가지가 있지만 일반적인 방법은 performance.getEntriesByType
를 사용하는 것입니다.
// Get Navigation Timing entries:
performance.getEntriesByType('navigation');
// Get Resource Timing entries:
performance.getEntriesByType('resource');
performance.getEntriesByType
는 성능 항목 버퍼에서 검색할 항목 유형을 설명하는 문자열을 허용합니다. 'navigation'
및 'resource'
는 각각 Navigation Timing API 및 Resource Timing API의 타이밍을 가져옵니다.
이러한 API가 제공하는 정보의 양은 엄청날 수 있지만, 웹사이트를 방문하는 사용자로부터 이러한 시간을 수집할 수 있으므로 현장에서 로드 성능을 측정하는 데 중요한 역할을 합니다.
네트워크 요청의 수명 및 타이밍
탐색 및 리소스 타이밍을 수집하고 분석하는 것은 일종의 고고학과 같습니다. 사후에 네트워크 요청의 짧은 수명을 재구성하는 것이기 때문입니다. 개념을 시각화하는 것이 도움이 되는 경우도 있으며, 네트워크 요청이 우려되는 경우 브라우저의 개발자 도구가 도움이 될 수 있습니다.
네트워크 요청의 처리 과정에는 DNS 조회, 연결 설정, TLS 협상 및 기타 지연 시간 원인 같은 다양한 단계가 있습니다. 이러한 타이밍은 DOMHighResTimestamp
로 표시됩니다. 브라우저에 따라 타이밍의 단위를 마이크로초 단위로 줄일 수도 있고, 밀리초 단위로 반올림할 수도 있습니다. 이러한 단계를 자세히 살펴보고, Navigation Timing 및 Resource Timing의 관계와 어떤 관련이 있는지 알아보는 것이 좋습니다.
DNS 조회
사용자가 URL로 이동하면 도메인 이름 시스템(DNS)이 쿼리되어 도메인을 IP 주소로 변환합니다. 이 프로세스는 상당한 시간이 걸릴 수 있으며, 이 시간은 현장에서 측정하는 데 사용해야 할 수도 있습니다. Navigation Timing과 Resource Timing은 두 가지 DNS 관련 타이밍을 노출합니다.
domainLookupStart
는 DNS 조회가 시작되는 시점입니다.domainLookupEnd
는 DNS 조회가 종료되는 시점입니다.
총 DNS 조회 시간은 종료 측정항목에서 시작 측정항목을 빼서 계산할 수 있습니다.
// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;
<ph type="x-smartling-placeholder">
연결 협상
로드 성능에 영향을 미치는 또 다른 요인은 웹 서버에 연결할 때 발생하는 지연 시간인 연결 협상입니다. HTTPS가 관련된 경우 이 프로세스에 TLS 협상 시간도 포함됩니다. 연결 단계는 세 가지 타이밍으로 구성됩니다.
connectStart
는 브라우저가 웹 서버에 대한 연결을 열기 시작할 때입니다.secureConnectionStart
는 클라이언트가 TLS 협상을 시작할 때 표시됩니다.connectEnd
는 웹 서버에 연결된 경우입니다.
총 연결 시간을 측정하는 것은 총 DNS 조회 시간을 측정하는 것과 비슷합니다. 즉, 종료 시간에서 시작 시간을 빼면 됩니다. 하지만 HTTPS가 사용되지 않거나 연결이 지속되는 경우 0
일 수 있는 추가 secureConnectionStart
속성이 있습니다. TLS 협상 시간을 측정하려면 다음 사항에 유의해야 합니다.
// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with
// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
// Awesome! Calculate it!
tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}
DNS 조회 및 연결 협상이 끝나면 문서 및 종속 리소스를 가져오는 것과 관련된 타이밍이 작동하게 됩니다.
요청 및 응답
로드 성능은 다음 두 가지 유형의 요인에 영향을 받습니다.
- 외부 요인: 지연 시간 및 대역폭과 같은 요인입니다. 호스팅 업체와 CDN을 선택하는 것 외에는 사용자가 어디서나 웹에 액세스할 수 있기 때문에 대부분의 경우 Google에서 통제할 수 없습니다.
- 내재적 요인: 서버 및 클라이언트 측 아키텍처, 리소스 크기, 이러한 항목에 맞춰 최적화하는 능력 등은 Google에서 제어할 수 있습니다.
두 가지 요인 모두 로드 성능에 영향을 미칩니다. 이러한 요소와 관련된 타이밍은 리소스를 다운로드하는 데 걸리는 시간을 설명하므로 매우 중요합니다. Navigation Timing과 Resource Timing은 모두 다음 측정항목으로 로드 성능을 설명합니다.
fetchStart
는 브라우저가 리소스(리소스 타이밍) 또는 탐색 요청의 문서(탐색 타이밍) 가져오기를 시작하는 시점을 표시합니다. 실제 요청보다 선행하며 브라우저가 캐시 (예: HTTP 및Cache
인스턴스)를 확인하는 지점입니다.workerStart
는 서비스 워커의fetch
이벤트 핸들러 내에서 요청이 처리되기 시작하는 시점을 표시합니다. 현재 페이지를 제어하는 서비스 워커가 없으면0
가 됩니다.requestStart
는 브라우저가 요청하는 시점입니다.responseStart
은 응답의 첫 번째 바이트가 도착한 시간입니다.responseEnd
은 응답의 마지막 바이트가 도착한 시점입니다.
이러한 타이밍을 통해 서비스 워커 내 캐시 조회 및 다운로드 시간과 같은 로드 성능의 여러 측면을 측정할 수 있습니다.
// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;
// Service worker time plus response time
let workerTime = 0;
if (pageNav.workerStart > 0) {
workerTime = pageNav.responseEnd - pageNav.workerStart;
}
요청 및 응답 지연 시간의 다른 측면도 측정할 수 있습니다.
const [pageNav] = performance.getEntriesByType('navigation');
// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;
// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;
// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;
기타 측정값
탐색 시간 및 리소스 시간은 이전 예시에서 설명한 것 이상으로 유용합니다. 다음과 같은 관련 시기의 상황을 살펴볼 가치가 있습니다.
- 페이지 리디렉션: 리디렉션은 지연 시간 증가를 유발하는 간과된 원인이며, 특히 리디렉션 체인에 영향을 미칩니다. 지연 시간은 HTTP에서 HTTP로의 홉, 302/캐시되지 않은 301 리디렉션과 같은 다양한 방식으로 추가됩니다.
redirectStart
,redirectEnd
,redirectCount
타이밍은 리디렉션 지연 시간을 평가하는 데 도움이 됩니다. - 문서 언로드:
unload
이벤트 핸들러에서 코드를 실행하는 페이지에서는 브라우저가 다음 페이지로 이동하기 전에 해당 코드를 실행해야 합니다.unloadEventStart
및unloadEventEnd
는 문서 언로드를 측정합니다. - 문서 처리: 웹사이트에서 매우 큰 HTML 페이로드를 전송하는 경우가 아니라면 문서 처리 시간이 중요하지 않을 수 있습니다. 이 설명이 사용자의 상황에 해당하는 경우
domInteractive
,domContentLoadedEventStart
,domContentLoadedEventEnd
,domComplete
타이밍을 살펴보는 것이 좋습니다.
코드에서 타이밍을 가져오는 방법
지금까지 보여준 모든 예에서는 performance.getEntriesByType
를 사용하지만 성능 항목 버퍼를 쿼리하는 다른 방법(예: performance.getEntriesByName
및 performance.getEntries
)도 있습니다. 이러한 방법은 간단한 분석만 필요한 경우에 적합합니다. 그러나 다른 상황에서는 많은 수의 항목을 반복하거나 새로운 항목을 찾기 위해 성능 버퍼를 반복적으로 폴링하여 과도한 기본 스레드 작업을 발생시킬 수 있습니다.
성능 항목 버퍼에서 항목을 수집하는 데 권장되는 방법은 PerformanceObserver
를 사용하는 것입니다. PerformanceObserver
는 성능 항목을 수신 대기하고 버퍼에 추가될 때 이러한 항목을 제공합니다.
// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
// Get all resource entries collected so far:
const entries = observedEntries.getEntries();
// Iterate over entries:
for (let i = 0; i < entries.length; i++) {
// Do the work!
}
});
// Run the observer for Navigation Timing entries:
perfObserver.observe({
type: 'navigation',
buffered: true
});
// Run the observer for Resource Timing entries:
perfObserver.observe({
type: 'resource',
buffered: true
});
<ph type="x-smartling-placeholder">
이 타이밍 수집 방법은 성능 항목 버퍼에 직접 액세스하는 것에 비해 어색하게 느껴질 수 있지만, 기본 스레드를 사용자에게 표시되는 중요한 목적에 도움이 되지 않는 작업과 연결하는 것이 좋습니다.
집으로 전화하는 방법
필요한 모든 타이밍을 수집하면 추가 분석을 위해 엔드포인트로 전송할 수 있습니다. 두 가지 방법은 navigator.sendBeacon
를 사용하거나 keepalive
옵션이 설정된 fetch
을 사용하는 것입니다. 두 메서드 모두 비차단 방식으로 지정된 엔드포인트에 요청을 전송하며, 필요한 경우 요청이 현재 페이지 세션보다 오래 지속되도록 큐에 추가됩니다.
// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
// Caution: If you have lots of performance entries, don't
// do this. This is an example for illustrative purposes.
const data = JSON.stringify(performance.getEntries());
// Send the data!
navigator.sendBeacon('/analytics', data);
}
이 예시에서 JSON 문자열은 POST
페이로드로 도착하며, 이 페이로드는 필요에 따라 디코딩, 처리, 애플리케이션 백엔드에 저장할 수 있습니다.
결론
측정항목을 수집한 후 해당 필드 데이터를 분석하는 방법은 사용자의 몫입니다. 현장 데이터를 분석할 때 의미 있는 결론을 도출하려면 다음과 같은 몇 가지 일반적인 규칙을 따라야 합니다.
- 평균은 특정 사용자의 경험을 대표하지 않으며 외부값으로 인해 왜곡될 수 있으므로 평균을 사용하지 마세요.
- 백분위수를 사용하세요. 시간 기반 성능 측정항목 데이터 세트에서는 낮을수록 좋습니다. 즉, 낮은 백분위수에 우선순위를 두면 가장 빠른 환경에만 관심을 기울이게 됩니다.
- 값의 롱테일에 우선순위를 둡니다. 75번째 백분위수 이상의 실험 환경에 우선순위를 두는 경우 가장 느린 환경에 초점을 맞추게 됩니다.
이 가이드는 Navigation 또는 Resource Timing에 대한 완전한 리소스가 아닌 시작점입니다. 다음은 도움이 될 만한 추가 리소스입니다.
이러한 API와 API에서 제공하는 데이터를 통해 실제 사용자가 경험하는 로드 성능을 더 잘 파악할 수 있으므로 현장의 로드 성능 문제를 더 확신을 가지고 진단하고 해결할 수 있습니다.