Web Vitals
고친 사람 github-actions[bot]
Web Vitals 는 웹 페이지가 사용자에게 얼마나 쾌적한지를 숫자 몇 개로 잽니다. 가장 큰 사진이 화면에 뜨기까지의 시간이 그런 숫자의 하나입니다. 숫자마다 「좋음」과 「나쁨」을 가르는 기준선이 붙어 있습니다. 구글이 고르고 이름을 붙인 지표 묶음입니다. 그중 로딩·반응·흔들림을 재는 핵심 셋은 따로 코어 웹 바이탈이라고 부릅니다.
쉽고 빠른 이해
Web Vitals 는 웹 페이지의 쾌적함을 숫자 몇 개와 합격선으로 매기는 성적표입니다. 쇼핑몰 상품 페이지라면 상품 사진이 뜨기까지의 시간, 버튼을 누른 뒤 화면이 바뀌기까지의 시간, 글자가 밀려난 정도를 봅니다.
웹 페이지의 속도를 재는 숫자는 많습니다. 무엇을 봐야 하는지, 몇이면 괜찮은지가 사람마다 달랐습니다. 몇 개만 골라 기준선까지 정해 두면 누구나 같은 잣대로 말할 수 있습니다.
어떻게 도나:
- 방문자의 브라우저가 숫자를 잽니다
- 여러 방문의 기록을 모아 작은 순으로 줄 세웁니다
- 방문 넷 중 셋이 「좋음」 선 안에 들면 합격입니다
대가도 있습니다. 고른 숫자에 안 잡히는 불편은 보이지 않습니다. 진짜 방문 기록이 쌓여야 판정이 나옵니다. 다른 서버가 부르는 백엔드 서비스에는 쓸 수 없습니다.
상세
이 절은 Web Vitals 가 왜 생겼는지와 지표 묶음의 모양, 기준선을 봅니다. 이어서 숫자를 재는 두 방법과 백엔드가 숫자를 모으는 코드를 봅니다.
건강검진의 활력 징후
병원에 가면 간호사가 먼저 맥박과 혈압과 체온을 잽니다. 몸에서 잴 수 있는 것은 많습니다. 그래도 이 몇 개를 먼저 잽니다. 숫자마다 정상 범위가 정해져 있어서 환자도 결과지를 보고 괜찮은지 압니다.
영어로 이런 숫자를 vital signs, 줄여서 vitals 라고 부릅니다. Web Vitals 는 웹 페이지의 활력 징후라는 뜻입니다. 웹 페이지에서 잴 수 있는 숫자 가운데 사용자가 느끼는 쾌적함에 가까운 몇 개를 골랐습니다. 숫자마다 기준선도 붙였습니다.
왜 따로 묶었나
웹 페이지의 속도를 재는 지표는 오래전부터 많았습니다. 지표는 상태를 숫자 하나로 줄여 보여 주는 값입니다. 페이지의 파일을 모두 받은 시각, 첫 글자가 화면에 뜬 시각, 스크립트가 다 돈 시각이 모두 지표가 됩니다.
문제는 고르는 일이었습니다. 도구마다 내세우는 숫자가 달랐습니다. 몇이면 괜찮은지도 제각각이었습니다. 사이트를 운영하는 사람이 자기 페이지가 괜찮은지 알려면 웹 성능을 깊이 파고들어야 했습니다.
Web Vitals 는 이 고르는 일을 대신 해 줍니다. 구글이 사용자 경험의 품질을 가늠할 신호 몇 개를 골라 하나의 지침으로 묶었습니다. 성능 전문가가 아니어도 이 숫자만 보면 페이지 상태를 알 수 있게 하려는 것입니다.
백엔드 개발자에게 익숙한 숫자는 응답 시간입니다. 서버가 요청을 받고 답을 보내기까지 걸린 시간입니다. 그런데 사용자의 기다림은 답이 도착한 뒤에도 이어집니다. 브라우저가 그림과 스크립트를 더 받고 화면에 칠해야 내용이 보입니다.
Web Vitals 는 이 뒷부분까지 사용자의 브라우저에서 잽니다. 서버 로그에서는 응답이 짧게 찍힙니다. 그래도 사용자는 오래 기다릴 수 있습니다. 그 차이를 숫자로 드러내는 것이 이 묶음의 쓸모입니다.
두 겹으로 된 묶음
Web Vitals 는 두 겹입니다. 안쪽 겹은 모든 웹 페이지에 적용하는 핵심 지표 셋입니다. 이 셋을 코어 웹 바이탈(Core Web Vitals)이라고 부릅니다. 바깥 겹은 핵심 지표를 거드는 보조 지표들입니다.
핵심 지표의 이름에는 「페인트」가 자주 나옵니다. 페인트는 브라우저가 화면에 픽셀을 칠하는 일입니다. 사용자는 칠해진 것만 볼 수 있어서 지표들이 칠한 순간을 기준으로 삼습니다.
LCP(Largest Contentful Paint)는 로딩을 잽니다. 이름은 내용이 담긴 것 가운데 가장 큰 것이 칠해지는 때라는 뜻입니다. 페이지를 열기 시작한 때부터 첫 화면에서 가장 큰 그림이나 글 덩어리가 칠해질 때까지의 시간입니다. 뉴스 기사라면 대표 사진이 그 덩어리가 되기 쉽습니다.
INP(Interaction to Next Paint, 다음 페인트까지의 상호작용)는 반응을 잽니다. 사용자가 클릭하거나 키를 친 뒤 화면이 다음으로 칠해지기까지의 시간입니다. 페이지에 머무는 동안 일어난 반응 가운데 가장 오래 걸린 것 근처의 값이 그 페이지의 값이 됩니다.
CLS(Cumulative Layout Shift, 누적 레이아웃 이동)는 흔들림을 잽니다. 이미 보이던 글이나 버튼이 예고 없이 밀려나면 그 정도를 점수로 매겨 더합니다. 시간이 아니라 점수라서 단위가 없습니다.
보조 지표는 핵심 지표가 기준선을 넘을 때 원인을 찾는 데 씁니다. 그중 하나는 메인 스레드를 봅니다.
메인 스레드는 브라우저가 자바스크립트 실행과 화면 그리기를 함께 맡기는 스레드 하나입니다. 오래 도는 스크립트가 이 스레드를 붙잡고 있으면 클릭이 와도 처리할 차례가 늦게 옵니다. 이렇게 스레드를 오래 붙잡는 일을 긴 작업(long task)이라고 부릅니다. 보조 지표 셋은 아래 표와 같습니다.
| 지표 | 풀어 쓴 이름 | 재는 것 |
|---|---|---|
| TTFB | Time to First Byte, 첫 바이트까지의 시간 | 요청을 보낸 뒤 응답의 첫 바이트가 오기까지 |
| FCP | First Contentful Paint | 글이든 그림이든 내용이 처음 칠해지기까지 |
| TBT | Total Blocking Time, 총 차단 시간 | 메인 스레드가 긴 작업에 묶여 입력을 못 받은 시간의 합 |
TTFB 와 FCP 는 로딩의 앞부분을 쪼개 봅니다. LCP 가 늦을 때 서버 응답이 늦은 건지 그리기가 늦은 건지 가르는 데 씁니다. TBT 는 INP 가 길어질 낌새를 미리 잡습니다.
아래 그림은 두 겹과 그 사이의 관계를 한데 놓은 것입니다. 화살표는 보조 지표가 어느 핵심 지표를 거드는지를 가리킵니다. CLS 를 거드는 보조 지표는 이 표에 없어서 CLS 로 들어오는 화살표가 없습니다.
flowchart TD
subgraph 보조["바깥 겹 · 보조 지표"]
TTFB["TTFB"]
FCP["FCP"]
TBT["TBT"]
end
subgraph 핵심["안쪽 겹 · 코어 웹 바이탈"]
LCP["LCP · 로딩"]
INP["INP · 반응"]
CLS["CLS · 흔들림"]
end
TTFB --> LCP
FCP --> LCP
TBT --> INP
지표 목록이 바뀌는 방식
Web Vitals 의 지표는 고정된 목록이 아닙니다. 새 지표는 시험 단계(experimental)와 예고 단계(pending)를 거칩니다. 안정 단계(stable)에 올라야 핵심 지표로 쓰입니다. 예고 단계는 바뀔 지표를 미리 알려 두는 기간입니다.
INP 가 이 길을 밟았습니다. 그 전에는 FID(First Input Delay, 첫 입력 지연)가 반응을 쟀습니다. FID 는 첫 입력 하나만 봤습니다. 그마저 브라우저가 처리를 시작하기까지만 셌습니다. 2024년에 INP 가 FID 를 대신했습니다.
세 칸으로 가르는 기준선
핵심 지표 셋에는 저마다 「좋음」과 「나쁨」을 가르는 선이 있습니다. 두 선 사이는 「개선 필요」입니다. 영어 이름은 good · needs improvement · poor 입니다. 뒤의 코드에도 이 이름이 나옵니다.
| 지표 | 좋음 | 개선 필요 | 나쁨 |
|---|---|---|---|
| LCP | 2.5초 이하 | 4초 이하 | 4초 초과 |
| INP | 200밀리초 이하 | 500밀리초 이하 | 500밀리초 초과 |
| CLS | 0.1 이하 | 0.25 이하 | 0.25 초과 |
표에서 LCP 와 INP 는 시간입니다. CLS 만 점수입니다. 셋 다 작을수록 사용자가 덜 답답합니다.
방문 한 번으로는 판정하지 않습니다. 같은 페이지도 방문자의 기기와 네트워크에 따라 숫자가 크게 달라지기 때문입니다. 그래서 많은 방문의 기록을 모아 백분위수로 봅니다.
백분위수는 값을 작은 순으로 줄 세웠을 때 몇 퍼센트 지점에 오는 값인지를 말합니다. Web Vitals 는 75번째 백분위수를 씁니다. 방문 100번의 LCP 를 짧은 순으로 세웠을 때 75번째 값이 2.5초 이하면 그 페이지의 LCP 는 「좋음」입니다.
평균을 쓰지 않는 까닭이 있습니다. 방문 열 번 가운데 일곱 번은 LCP 가 1초, 세 번은 4.5초였다고 해 봅시다. 평균은 2.05초라서 「좋음」에 듭니다. 그런데 열 명 가운데 세 명은 「나쁨」 칸의 시간을 기다렸습니다.
75번째 백분위수는 오래 걸린 쪽 4분의 1이 시작되는 경계입니다. 이 값이 선 안이면 방문 넷 중 셋은 선 안에 들었습니다. 위 예에서 75번째 백분위수는 4.5초라서 「나쁨」으로 판정됩니다.
휴대폰과 데스크톱은 따로 셉니다. 둘은 처리 능력과 네트워크가 달라서 섞으면 어느 쪽 사정도 안 보입니다. 세 지표가 모두 「좋음」이어야 그 페이지가 코어 웹 바이탈을 통과한 것으로 칩니다.
현장에서 재기와 실험실에서 재기
숫자를 얻는 방법은 둘입니다. 진짜 방문자의 브라우저에서 모으면 현장 데이터입니다. 시험용 환경에서 페이지를 열어 재면 실험실 데이터입니다. 판정의 근거는 현장 데이터입니다.
현장 데이터는 두 곳에서 모입니다. 한 곳은 크롬 브라우저입니다. 크롬이 모은 방문 기록은 Chrome UX Report(Chrome User Experience Report)라는 공개 통계로 나옵니다. 방문이 많은 페이지라면 따로 손대지 않아도 숫자를 볼 수 있습니다.
다른 한 곳은 내 서버입니다. 내 페이지에 측정 스크립트를 심어 방문자의 숫자를 직접 모읍니다. 이 방식을 실사용자 모니터링(Real User Monitoring, RUM)이라고 부릅니다.
실험실 데이터는 원인을 찾을 때 씁니다. Lighthouse 같은 도구가 정해진 기기와 네트워크를 흉내 내 페이지를 엽니다. 조건이 늘 같아서 고치기 전과 후를 견주기 쉽습니다.
실험실에는 누르는 사람이 없습니다. 클릭이 없으니 INP 를 잴 수 없습니다. 그래서 실험실에서는 INP 대신 TBT 를 봅니다.
백엔드가 숫자를 받는 길
실사용자 모니터링을 직접 하면 백엔드가 숫자를 받는 쪽이 됩니다. 이 소절은 브라우저가 숫자를 재서 보내는 코드부터 봅니다. 이어서 백엔드가 그 숫자로 무엇을 하는지 봅니다.
브라우저 쪽은 web-vitals 라는 자바스크립트 라이브러리로 짜는 경우가 많습니다. 구글이 내놓은 라이브러리입니다. 이름이 표제어와 같습니다. 소문자에 하이픈이 들어간 쪽이 라이브러리입니다.
콜백은 나중에 불러 달라고 넘겨 두는 함수입니다. 이 라이브러리는 지표마다 콜백을 받는 함수를 하나씩 줍니다. onLCP 에 콜백을 넘기면 LCP 값이 정해질 때 그 콜백을 부릅니다.
보내는 데는 sendBeacon 을 씁니다. 브라우저가 작은 데이터를 서버로 보내는 함수입니다. 페이지를 닫는 중에도 전송을 마쳐 준다는 점이 쓸모입니다. CLS 와 INP 는 머무는 동안 값이 바뀌어서 페이지를 떠날 무렵에 보고되는 일이 많기 때문입니다.
아래 코드는 세 지표를 재서 /vitals 주소로 보냅니다. 주석은 LCP 를 보고할 때 들어가는 값의 예입니다.
import { onLCP, onINP, onCLS } from 'web-vitals';
function send(metric) {
const body = JSON.stringify({
name: metric.name, // 'LCP'
value: metric.value, // 1830
rating: metric.rating, // 'good'
page: location.pathname, // '/item/42'
});
navigator.sendBeacon('/vitals', body);
}
onLCP(send);
onINP(send);
onCLS(send);
방금 본 코드에서 metric 하나에 지표 이름과 값과 판정이 함께 들어 있습니다. 페이지 주소는 location.pathname 으로 따로 붙였습니다.
LCP 와 INP 의 값은 밀리초입니다. CLS 의 값은 단위 없는 점수입니다. rating 은 방문 한 번의 값이 앞 표의 세 칸 가운데 어디에 드는지 알려 줍니다.
백엔드는 이 요청을 받아 쌓습니다. 기기 종류는 요청 헤더의 사용자 에이전트로 가를 수 있습니다. 본문의 페이지 주소와 기기 종류가 있으면 판정에 필요한 것이 다 모입니다.
판정은 쌓인 값으로 합니다. 페이지와 기기와 지표마다 75번째 백분위수를 구해 기준선 표와 견줍니다. 방문 한 번의 rating 을 세는 것이 아니라 값이 퍼진 모양을 보는 것입니다.
쓰는 곳과 못 쓰는 곳
Web Vitals 는 사람이 브라우저로 여는 페이지를 위한 숫자입니다. 검색으로 들어오는 방문이 많은 공개 페이지일수록 챙길 까닭이 큽니다. 구글 검색이 순위를 정하는 여러 신호 가운데 하나로 코어 웹 바이탈을 보기 때문입니다.
브라우저가 없는 곳에는 쓸 수 없습니다. 앱이나 다른 서버가 부르는 백엔드 서비스는 응답 시간과 오류율 같은 서버 쪽 지표로 잽니다.
방문이 적은 페이지는 공개 통계에 잡히지 않습니다. 사내 관리 도구가 그렇습니다. 이런 페이지는 앞 소절처럼 직접 모아야 숫자가 나옵니다.
숫자가 모두 좋아도 쓰기 불편한 페이지는 있습니다. Web Vitals 는 로딩과 반응과 흔들림을 봅니다. 찾는 정보가 없거나 절차가 복잡한 불편은 이 숫자에 안 잡힙니다.
관련 항목
Web Vitals 의 핵심을 이루거나 이루었던 지표
코어 웹 바이탈 · LCP · INP · CLS · FID
핵심 지표 곁에서 원인을 짚는 보조 지표
TTFB · FCP · TBT · Speed Index · Time to Interactive
Web Vitals 를 재는 도구
Lighthouse · Chrome UX Report · PageSpeed Insights · Search Console · 개발자 도구 · web-vitals
Web Vitals 를 모으고 판정하는 수단
실사용자 모니터링 · 합성 모니터링 · 백분위수 · sendBeacon · PerformanceObserver · 텔레메트리
이 숫자를 움직이는 브라우저 처리 단계
렌더링 · 렌더 파이프라인 · 페인트 · 리페인트 · 레이아웃 이동 · 메인 스레드
브라우저가 없는 서비스에서 대신 보는 지표
응답 시간 · 오류율 · 꼬리 지연 · 처리량 · SLO
Web Vitals 가 속하는 상위 분야
다른 이름: 웹 바이탈 · 웹 바이탈스