서버 타이밍으로 성능 측정하기

게시 됨: 2022-03-10
빠른 요약 ↬ Server Timing 헤더는 백엔드 서버 성능 타이밍을 브라우저의 개발자 도구에 전달하는 개별적이고 편리한 방법을 제공합니다. 애플리케이션에 타이밍 정보를 추가하면 백엔드 및 프론트엔드 성능을 모두 한 곳에서 모니터링할 수 있습니다.

모든 종류의 성능 최적화 작업을 수행할 때 가장 먼저 배우는 것 중 하나는 성능을 개선하기 전에 먼저 성능을 측정해야 한다는 것입니다. 어떤 것이 작동하는 속도를 측정할 수 없다면 변경 사항이 성능을 향상시키는지, 효과가 없는지 또는 상황을 악화시키는지 알 수 없습니다.

우리 중 많은 사람들이 어느 정도 성능 문제를 해결하는 데 익숙할 것입니다. 페이지의 JavaScript가 충분히 빨리 시작되지 않는 이유 또는 이미지가 좋지 않은 호텔 Wi-Fi에 표시되는 데 너무 오래 걸리는 이유를 파악하는 것처럼 간단할 수 있습니다. 이러한 종류의 질문에 대한 답은 종종 매우 친숙한 위치, 즉 브라우저의 개발자 도구에서 찾을 수 있습니다.

수년에 걸쳐 개발자 도구는 애플리케이션 프런트 엔드에서 이러한 종류의 성능 문제를 해결하는 데 도움이 되도록 개선되었습니다. 이제 브라우저에는 성능 감사 기능이 내장되어 있습니다. 이는 프런트 엔드 문제를 추적하는 데 도움이 될 수 있지만 이러한 감사는 브라우저에서 수정할 수 없는 또 다른 속도 저하 원인을 나타낼 수 있습니다. 그 문제는 느린 서버 응답 시간입니다.

"첫 번째 바이트까지의 시간"

서버에서 빌드 속도가 느린 페이지를 개선하기 위해 브라우저 최적화가 할 수 있는 일은 거의 없습니다. 그 비용은 브라우저가 파일을 요청하고 응답을 받는 사이에 발생합니다. 개발자 도구에서 네트워크 폭포수 차트를 연구하면 "대기(TTFB)" 범주에 이러한 지연이 표시됩니다. 이것은 브라우저가 요청을 하고 응답을 받기까지 기다리는 시간입니다.

점프 후 더! 아래에서 계속 읽기 ↓

성능 측면에서 이것은 첫 번째 바이트까지의 시간으로 알려져 있습니다. 즉, 서버가 브라우저에서 작업을 시작할 수 있는 무언가를 보내기 시작하기 전에 걸리는 시간입니다. 그 대기 시간에는 서버가 페이지를 빌드하는 데 필요한 모든 것이 포함됩니다. 일반적인 사이트의 경우 요청을 애플리케이션의 올바른 부분으로 라우팅하고, 요청을 인증하고, 데이터베이스 등과 같은 백엔드 시스템을 여러 번 호출하는 작업이 포함될 수 있습니다. 여기에는 템플릿 시스템을 통해 콘텐츠를 실행하고, 타사 서비스에 API를 호출하고, 이메일을 보내거나 이미지 크기를 조정하는 것과 같은 작업이 포함될 수 있습니다. 서버가 요청을 완료하기 위해 수행하는 모든 작업은 사용자가 브라우저에서 경험하는 해당 TTFB 대기로 압축됩니다.

단일 페이지 요청 검사를 보여주는 Chrome DevTools의 네트워크 패널
문서 요청을 검사하면 브라우저가 서버의 응답을 기다리는 데 소비한 시간이 표시됩니다.

그렇다면 어떻게 그 시간을 줄이고 사용자에게 페이지를 더 빨리 전달하기 시작할 수 있을까요? 글쎄요, 그것은 큰 질문이며 대답은 귀하의 응용 프로그램에 따라 다릅니다. 그것은 성능 최적화 자체의 작업입니다. 우리가 먼저 해야 할 일은 성능을 측정 하여 변경의 이점을 판단할 수 있도록 하는 것입니다.

서버 타이밍 헤더

서버 타이밍의 역할은 실제로 서버의 활동 시간을 측정하는 데 도움이 되지 않습니다. 백엔드 플랫폼에서 사용할 수 있는 도구 세트를 사용하여 직접 타이밍을 수행해야 합니다. 오히려, 서버 타이밍의 목적은 이러한 측정이 브라우저와 통신할 수 있는 방법을 지정하는 것입니다.

이 작업이 수행되는 방법은 매우 간단하고 사용자에게 투명하며 페이지 무게에 최소한의 영향을 미칩니다. 정보는 간단한 HTTP 응답 헤더 집합으로 전송됩니다.

 Server-Timing: db;dur=123, tmpl;dur=56

이 예는 dbtmpl 이라는 두 개의 서로 다른 타이밍 포인트와 통신합니다. 이것들은 사양의 일부가 아닙니다. 이것은 우리가 선택한 이름입니다. 이 경우에는 일부 데이터베이스 및 템플릿 타이밍을 각각 나타냅니다.

dur 속성은 작업을 완료하는 데 걸린 시간(밀리초)을 나타냅니다. 개발자 도구의 네트워크 섹션에서 요청을 보면 차트에 타이밍이 추가되었음을 알 수 있습니다.

새 서버 타이밍 섹션을 표시하는 Chrome DevTools의 페이지 요청 타이밍 패널.
Server-Timing HTTP 헤더로 설정된 타이밍을 보여주는 새로운 서버 타이밍 섹션이 나타납니다.

Server-Timing 헤더는 쉼표로 구분된 여러 메트릭을 사용할 수 있습니다.

 Server-Timing: metric, metric, metric

각 측정항목은 세 가지 가능한 속성을 지정할 수 있습니다.

  1. 메트릭의 짧은 이름 (예: 이 예에서는 db )
  2. 밀리초 단위의 지속 시간 ( dur=123 으로 표시)
  3. 설명 ( desc="My Description" 으로 표현)

각 속성은 구분 기호로 세미콜론으로 구분됩니다. 다음과 같이 예제에 설명을 추가할 수 있습니다.

 Server-Timing: db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing"
서버 타이밍 측정항목에 사용되는 설명을 보여주는 Chrome DevTools의 페이지 요청의 타이밍 패널.
이름은 제공되는 경우 설명으로 대체됩니다.

필요한 유일한 속성은 name 입니다. durdesc 는 모두 선택 사항이며 필요한 경우 선택적으로 사용할 수 있습니다. 예를 들어 한 서버나 데이터 센터에서 발생하고 다른 서버나 데이터 센터에서 발생하지 않는 타이밍 문제를 디버그해야 하는 경우 연결된 타이밍 없이 해당 정보를 응답에 추가하는 것이 유용할 수 있습니다.

 Server-Timing: datacenter;desc="East coast data center", db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing”

그러면 타이밍과 함께 표시됩니다.

시간이 설정되지 않은 서버 타이밍을 표시하는 Chrome DevTools 페이지 요청의 타이밍 패널.
타이밍이 없어도 "동해안 데이터 센터" 값이 표시됩니다.

한 가지 알아차릴 수 있는 것은 타이밍 막대가 폭포수 패턴으로 표시되지 않는다는 것입니다. 이는 단순히 서버 타이밍이 타이밍 시퀀스 를 전달하지 않고 원시 메트릭 자체를 전달하려고 하기 때문입니다.

서버 타이밍 구현

자신의 애플리케이션 내에서 정확한 구현은 특정 상황에 따라 다르지만 원칙은 동일합니다. 단계는 항상 다음과 같습니다.

  1. 일부 작업 시간
  2. 타이밍 결과를 함께 수집
  3. HTTP 헤더 출력

의사 코드에서 응답 생성은 다음과 같을 수 있습니다.

 startTimer('db') getInfoFromDatabase() stopTimer('db') startTimer('geo') geolocatePostalAddressWithAPI('10 Downing Street, London, UK') endTimer('geo') outputHeader('Server-Timing', getTimerOutput())

이러한 라인을 따라 무언가를 구현하는 기본 사항은 모든 언어에서 간단해야 합니다. 매우 간단한 PHP 구현은 타이밍 작업을 위해 microtime() 함수를 사용할 수 있으며 다음과 같이 보일 수 있습니다.

 class Timers { private $timers = []; public function startTimer($name, $description = null) { $this->timers[$name] = [ 'start' => microtime(true), 'desc' => $description, ]; } public function endTimer($name) { $this->timers[$name]['end'] = microtime(true); } public function getTimers() { $metrics = []; if (count($this->timers)) { foreach($this->timers as $name => $timer) { $timeTaken = ($timer['end'] - $timer['start']) * 1000; $output = sprintf('%s;dur=%f', $name, $timeTaken); if ($timer['desc'] != null) { $output .= sprintf(';desc="%s"', addslashes($timer['desc'])); } $metrics[] = $output; } } return implode($metrics, ', '); } }

테스트 스크립트는 다음과 같이 사용합니다. 여기에서는 usleep() 함수를 사용하여 스크립트 실행을 인위적으로 지연시켜 완료하는 데 시간이 걸리는 프로세스를 시뮬레이션합니다.

 $Timers = new Timers(); $Timers->startTimer('db'); usleep('200000'); $Timers->endTimer('db'); $Timers->startTimer('tpl', 'Templating'); usleep('300000'); $Timers->endTimer('tpl'); $Timers->startTimer('geo', 'Geocoding'); usleep('400000'); $Timers->endTimer('geo'); header('Server-Timing: '.$Timers->getTimers());

이 코드를 실행하면 다음과 같은 헤더가 생성됩니다.

 Server-Timing: db;dur=201.098919, tpl;dur=301.271915;desc="Templating", geo;dur=404.520988;desc="Geocoding"
Chrome DevTools에서 페이지 요청의 Timings 패널이 올바르게 표시된 테스트 값을 보여줍니다.
예제에 설정된 서버 타이밍은 테스트 스크립트에 구성된 지연과 함께 타이밍 패널에 표시됩니다.

기존 구현

Server Timing이 얼마나 편리한지 고려하면, 내가 찾을 수 있는 구현은 비교적 적습니다. 서버 타이밍 NPM 패키지는 노드 프로젝트에서 서버 타이밍을 사용하는 편리한 방법을 제공합니다.

미들웨어 기반 PHP 프레임워크를 사용하는 경우 tuupola/server-timing-middleware도 편리한 옵션을 제공합니다. 나는 몇 달 동안 Notist의 프로덕션에서 그것을 사용해 왔으며, 만약 당신이 야생에서 예제를 보고 싶다면 몇 가지 기본 타이밍을 항상 활성화된 상태로 둡니다.

브라우저 지원을 위해 내가 본 것 중 가장 좋은 것은 Chrome DevTools이며 이 기사의 스크린샷에 사용한 것입니다.

고려 사항

서버 타이밍 자체는 유선을 통해 다시 전송되는 HTTP 응답에 최소한의 오버헤드를 추가합니다. 헤더는 매우 작으며 일반적으로 내부 사용자만 대상으로 하는 것에 대해 걱정하지 않고 보내는 것이 안전합니다. 그렇더라도 불필요한 오버헤드를 추가하지 않도록 이름과 설명을 짧게 유지하는 것이 좋습니다.

더 우려되는 것은 페이지 또는 애플리케이션의 시간을 정하기 위해 서버에서 수행할 수 있는 추가 작업입니다. 추가 타이밍 및 로깅을 추가하는 것 자체가 성능에 영향을 줄 수 있으므로 필요할 때 이를 켜고 끄는 방법을 구현하는 것이 좋습니다.

서버 타이밍 헤더를 사용하는 것은 애플리케이션의 프론트 엔드와 백엔드 모두의 모든 타이밍 정보를 한 위치에서 액세스할 수 있도록 하는 좋은 방법입니다. 애플리케이션이 너무 복잡하지 않다면 구현하기 쉽고 매우 짧은 시간 내에 가동 및 실행할 수 있습니다.

서버 타이밍에 대한 자세한 내용을 보려면 다음을 시도해 보십시오.

  • W3C 서버 타이밍 사양
  • Server Timing의 MDN 페이지에는 브라우저 지원에 대한 예제와 최신 세부 정보가 있습니다.
  • 서버 타이밍 사용에 대한 BBC iPlayer 팀의 흥미로운 글.