Web Vitals - Performance (1)
약 1년 전에 미디어 전시 프로젝트 "모던 그로테스크 타임즈(Modern Grotesque Times)"에 웹 개발자로 참여한 적이 있었다(🔗 Modern Grotesque Times 🔗 GitHub). 전시 기획자나 나에게나 뜻깊은 프로젝트였는데, 기획자는 매번 다른 기획자를 보조하기만 하다가 처음으로 단독으로 꾸린 전시였고 나로서는 세팅부터 개발, 배포까지 모두 한 첫 프로젝트였기때문이다. 기한에 맞춰 순조롭게 웹 사이트를 오픈했고 전시도 무사히 마쳤지만 뿌듯한만큼 아쉬움도 많이 남는다. 아직 서비스 중이라 생각날 때마다 들어가보곤 하는데, 매번 미처 못보던 부분들이 새롭게 보인다. 예를들면
첫 페이지 진입시에 이미지가 늦게 로드돼서 레이아웃 전체가 움직이는 현상과 같은 것이다. 기능에 문제가 있는 것은 아니라 당시에는 나와 기획자 모두 대수롭지 않게 넘어갔지만 (촌스러운 UI의 초창기 웹사이트 컨셉에 충실한 로딩속도라고 생각했을 수도 있다) 지금으로선 많이 아쉽다. 심지어 Figure1 과 같은 현상을 부르는 용어도 따로 있다고 한다. 해당 지표가 높게 나오면 웹 성능이 좋지 않은 사이트로 평가받는다고한다. 1년 전에는 내가 어떤 것들을 놓치고 있었는지 보다 체계적으로 알아보기 위해 크롬에서 제공하는 성능 측정 도구인 Lighthouse를 사용해 "모던 그로테스크 타임즈" 웹사이트의 성능을 측정해보았다.
각 항목별로 살펴보자.
First Contentful Paint (FCP)
First Contentful Paint(이하 FCP)란 유저가 화면에서 무엇 하나라도 볼 수 있기까지 걸린 시간을 의미한다. FCP가 늦으면 유저는 아무것도 없는 텅 빈 화면만 보게되므로 잘못된 URL에 진입했거나 에러가 났다고 판단해 웹사이트에서 이탈할 우려가 있으므로 FCP를 각별히 잘 관리해야한다. 텍스트나 이미지 등이 화면에 등장하면 FCP라고 감지될 수 있는데, 이 때 눈에 보이지 않는 흰 화면의 <canvas>
엘리먼트는 FCP 측정의 기준이 되지 않는다.
크롬 개발자 도구를 열어서 "모던 그로테스크 타임즈"의 FCP 시점을 찾아가보자. "모든 그로테스크 타임즈"에서 가장 먼저 그려지는 요소는 프로그레스 바이며 해당 요소의 등장할 즈음을 FCP 시점으로 측정하고 있음을 알 수 있다.
Speed Index (SI)
Speed Index(이하 SI)는 페이지가 로드되는 동안 내용물들이 얼마나 빠르게 화면에 디스플레이되는지를 측정하는 지표다. Lighthouse는 페이지가 로드되는 과정을 비디오로 촬영해서 각 프레임간에 시각적 요소들이 로딩된 비율이 차이가 나는지를 계산한다.
Recommended
크롬은 측정한 성능 값이 다른 웹사이트들의 SI 점수 분포도 중에서 어디에 위치하는지를 파악해 성능 값을 SI 점수로 환산한다. 크롬은 SI 지수가 3.4 미만인 웹사이트를 '빠름', 5.8 이상인 웹사이트를 '느림'으로 분류한다.
Optimization
페이지 로딩을 빠르게 하는 방법은 대부분 SI 점수를 개선시킨다.
Largest Contentful Paint (LCP)
FCP는 오직 초기 로딩 속도만을 측정하기때문에 웹사이트에 진입하고 몇초간 스피너가 돌고 있어서 실제 사용자가 웹사이트를 사용할 수 없더라도 FCP 지표만 놓고 보면 해당 사이트는 빠른 사이트에 해당한다. 이러한 괴리를 보완해주는 지표가 바로 Largest Contentful Paint(이하 LCP)다. 그야말로 화면 내 가장 큰 컨텐츠가 로딩되기까지의 속도로, 이는 사용자가 실제로 웹사이트를 사용할 수 있다고 판단하는 시점을 보다 잘 반영한다.
Largest Content
화면 내에서 가장 큰 컨텐츠가 어떤 것인지는 개발자 도구의 Performance 탭에 들어가 페이지를 리로드한 후 LCP시점을 클릭해서 알 수 있다. "모던 그로테스크 타임즈 "의 경우 메인에 노출되는 포스트의 썸네일이 화면 내 가장 큰 컨텐츠임을 확인할 수 있었다.
이미지 이외에도 비디오의 썸네일, 텍스트를 포함한 element 등이 화면 내 가장 큰 요소인 largest content가 될 수 있다.
주목할만한 것은 FP(First Paint), FCP(First Contentful Paint), DCL(DOMContentLoaded), L(Onload), LCP(Largest Contentful Paint)를 모두 세분화해서 볼 수 있다는 점과 FP, FCP, DCL, L 시점이 거의 연속적인 것에 반해 상대적으로 LCP 시점은 훨씬 뒤라는 것이다. 보통 개발자들이 내용물이 화면에 로드되었는지를 확인하기 위해 DOMContentLoaded
이벤트에 리스너를 붙이는데 반해 실제 사용자가 화면에 내용물이 충분히 로드되었다고 인지하는 시점에 차이가 존재하며 두 시점의 괴리를 줄이기 위해 LCP 지표를 사용할 수 있음을 알 수 있다.
Recommended
크롬은 LCP 까지의 시간이 2.5초 이하인 웹사이트를 '좋음', 4초가 넘어가는 웹사이트를 '나쁨'으로 분류한다.
Cause
다음과 같은 요소들이 있을 경우 LCP까지의 시간이 오래 걸릴 수 있다:
- 서버 응답이 느린 경우
- 렌더링 과정을 블로킹(blocking)하는 JavaScript나 CSS 코드가 있는 경우
- 클라이언트 사이드 렌더링
Optimization
LCP 지표를 개선하려면 다음과 같은 방안을 시도해볼 수 있다:
- 폰트, 이미지, CSS 등의 리소스들을 최적화한다
- 서버사이드 렌더링을 기법을 사용한다
- JavaScript 번들 파일의 사이즈를 줄인다 (tree-shaking을 통해 불필요한 디펜던시 제거, code splitting, minifyinc, etc.)
Time To Interactive (TTI)
어떤 웹사이트들은 화면은 빨리 뜨지만 하지만 실제로 클릭, 스크롤 등의 인터랙션을 하기까지는 좀 더 시간이 걸리는 경우가 있다. 일단 화면이 보이니 사용자는 웹사이트를 사용할 수 있다고 판단하지만 실제로는 그렇지 않은 것이다. Time To Interactive(이하 TTI)는 이러한 불일치를 잡아내기에 좋은 지표다.
TTI는 화면에 유의미한 컨텐츠들이 그려졌을뿐만 아니라 사용자와 안정적으로 인터랙션할 수 있음을 보장하는 지표다. 좀 더 구체적으로 표현하자면 TTI는 페이지가 최초로 로딩된 순간부터
- FCP의 측정 대상이 되는 유의미한 컨텐츠들이 그려지고
- 화면 내 눈에 보이는 대부분의 요소들에 이벤트 핸들러들이 등록되었으며
- 클릭, 스크롤과 같은 유저의 입력에 50ms 이내에 반응
하는 순간까지의 시간을 측정한다.
Recommended
크롬은 TTI가 3.8초 이하인 웹사이트를 '빠름', 7.3초가 넘어가는 웹사이트를 '느림'으로 분류한다.
Optimization
TTI 지표를 개선하려면 기본적으로 클릭 이벤트 등을 처리할 수 있는 메인 스레드가 하는 일을 줄여야한다. 그러기 위해서는
- 서버 요청의 개수와 송∙수신되는 데이터의 크기를 줄이고
- 자바스크립트 실행 시간을 줄이는
등의 방안을 고려해보아야한다.
Total Blocking Time (TBT)
웹 페이지가 최초로 로딩된 순간부터 페이지가 실제로 인터랙션이 가능해지는 순간, 즉 TTI가 10초인, 아주 느린 웹사이트 A, B가 있다고 가정해보자. A는 10초짜리 긴 작업 한개를 JavaScript로 실행하기때문에 TTI가 10초이고 B는 10초동안 JavaScript로 51ms짜리 작업 세 개를 수행하느라 TTI가 10초다. 두 사이트 모두 TTI가 10초로 동일하지만 실제 유저 경험은 사뭇 다를 것이다. A사이트에서는 10초동안 아무리 사이트 여기저기 클릭을 해봐도 아무런 반응이 없는 반면 B 사이트에서는 51ms짜리 작업과 작업 사이에 이따금씩 클릭이나 스크롤에 대한 반응이 있을 것이다. A 사이트의 경우 유저는 사이트가 완전히 먹통이라고 생각할 확률이 큰 반면, B 사이트의 경우 에러가 일시적인 것이라 여길 확률이 더 높다.
이처럼 TTI만으로는 알 수 없는 서로 다른 사용자 경험을 보다 잘 파악하기 위해 Total Blocking Time(이하 TBT)라는 지표를 사용할 수 있다. TBT는 FCP와 TTI사이에 메인 스레드에 블로킹이 일어난 시간의 총합을 측정한다. 어떤 긴 작업이 50ms를 넘어가면 해당 작업을 수행하는데 걸린 시간과 50ms의 차이를 구하고 이 차이값의 총 합이 TBT 값이 된다. 위의 예시를 보면 B 사이트의 경우 TBT는 (51ms - 50ms) * 3 = 3ms가 된다. 반면 A 사이트의 경우 10000 - 50ms = 9950m가 된다. TTI는 10초로 동일하지만 TBT는 무려 3000배 이상 차이난다.
Recommended
크롬은 TBT가 200ms 미만인 웹사이트를 '빠름', 600ms를 넘어가는 웹사이트를 '느림'으로 분류한다.
Optimization
TBT 지표를 개선하는 방법은 TTI 지표를 개선하는 방법과 동일하다.
- 서버 요청의 개수와 송∙수신되는 데이터의 크기를 줄이고
- 자바스크립트 실행 시간을 줄이는
등 메인 스레드가 하는 일을 줄일 수 있는 방안을 고려해보아야한다.
Cumulative Layout Shift (CLS)
글 첫머리의 gif(Figure 1)에서 뒤늦게 로드된 이미지때문에 레이아웃 전체를 다시그리는 현상을 웹 성능 지표에서는 Cumulative Layout Shift, 줄여서 CLS라고 부른다. 요소들이 쌓이면서(cumulate) 레이아웃이 변화한다 정도로 이해할 수 있겠다. CLS가 발생하면 사용자 경험의 질이 떨어진다. 예를들어
'더보기' 버튼이 있는 버튼을 클릭했지만 그 순간 '더보기' 버튼 위의 이미지가 로드되어 '더보기' 버튼이 저 아래로 밀려나는 현상이 발생할 수 있다. 만약 '더보기' 버튼 대신 잘못 클릭한 요소에 광고나 악성 링크가 걸려있다면 불편한 정도가 아니라 사용자에게 실질적인 피해를 끼칠 우려도 있다.
Layout Shift
렌더링된 프레임이 다음 프레임으로 넘어갈 때 element가 위치를 바꾸는 것을 layout shift라고 부른다. 그 중에서도 CLS가 측정하는 것은 급격한 layout shift로서, 그 정의는 프레임간 간격이 1초 미만이면서 레이아웃이 변화한 시간이 최대 5초인 경우를 말한다. 이때 수집하는 수치는 페이지의 전체 수명 동안에 일어난 layout shift 가운데 가장 변화가 큰 것을 기준으로한다.
Recommended
크롬은 CLS 지표가 0.1 이하인 웹사이트를 '좋음', 0.25 이상인 웹사이트를 '나쁨'으로 분류한다.
Optimization
CLS 지표를 개선하려면
- 이미지, 비디오를 포함한 모든 블록에 고정 너비와 높이 값을 부여해서 내용물이 로드되지 않은 경우에도 지정된 너비와 높이를 차지할 수 있도록한다.
- Skeleton과 같은 UI를 사용해서 리소스가 비동기적으로 로드되는 동안 리소스의 높이, 너비만큼을 차지하도록 해서 리소스가 로드되고 나서도 레이아웃이 변화하지 않도록 한다.
Coming up next
각각의 성능 지표들이 무엇을 하는지, 어떻게 개선해야하는지를 알아보았다. 다음번 포스팅에서는 실제 개선책을 적용해서 지표가 얼마나 개선되었는지를 알아보도록 하겠다.