코어 웹 바이탈이란, 사용자 경험에 영향을 끼치는 다양한 요소 중 구글이 강조하는 3가지 매트릭스를 말한다. 3가지 매트릭스는 각각 LCP(Largest Contentful Pain), FID(First Input Delay), CLS(Cumulative Layout Shift)로 구성되어 있으며 사용자 경험의 기본이자 핵심요소라고 볼 수 있다.
LCP란 초기 로드 시점에서 viewport의 가장 큰 영역을 차지하는 이미지나 텍스트 요소가 표시되는 시점을 의미한다. 모든 요소를 측정하는 것은 아니고 다음의 요소들만 측정한다.
img 태그
svg 내 image 요소
poster 속성을 사용하는 video 요소
background url 속성을 사용해 이미지 로드하는 요소
텍스트 노드를 포함한 블록 레벨 요소
만약 요소가 viewport를 벗어나거나 잘리는 영역은 요소 크기에 포함하지 않는다. 그리고 이미지를 원본 크기보다 작게 설정한 경우 작은 크기가 사용되고, 원본 크기보다 크게 설정한 경우 원본 크기를 보고 계산한다. LCP 측정 도구로는 구글의 Lighthouse, Chrome devtools, Pagespeed insight 등이 있으며, 2.5초 이하를 권장하고 4초 이상이면 성능이 좋지 않다고 본다.
사용자가 웹페이지 접속해 로딩상태에 있을 때 버튼이나 링크를 클릭하는 등의 액션 요청을 보낸다면, 브라우저에서 이를 일정 시간 보류한다. 즉, 브라우저에 접속해 버튼이나 링크를 이동하는 등의 액션까지 걸리는 시간을 측정한 지표를 FID 라고 한다. 이 때, FID에서 중요하게 보는 지표는 액션 처리 시간이 아니라 요청 지연 시간이다. 요청 지연 시간은 테스트 환경에서는 확인할 수 없고 오직 사용자 환경에서 사용자가 체감하는 지표라 시뮬레이션하기가 어렵다. 대신 구글에서는 FID 대신, 여러 테스트 환경에서 TBT(Total Blocking Time)를 측정하는 것을 권장한다. TBT란 페이지 로드 중 input 작업이 불가능한 시간을 모두 합한 것으로 낮을 수록 성능이 좋은 것으로 평가한다. 100ms 이하를 권장하고, 300ms 이상이면 성능이 좋지 않다고 본다.
CLS에는 비주얼 안정성과 관련된 지표이다. 예를 들어, 사이트에서 어떤 컨텐츠를 클릭하는데 순간적으로 레이아웃이 변경 되어 광고를 클릭하게된다면 이는 부정적인 사용자 경험에 해당할 것이다. 이렇게 순간적으로 발생하는 레이아웃 이동 정도를 모두 합산한 지표가 CLS다. 0.1 이하를 권장하고, 0.1~0.25라면 개선 필요, 0.25 이상이면 성능이 좋지 않다고 본다.
코어 웹 바이탈 요소 외에도 구글에서는 다음의 4가지 요소를 사용자 경험에 큰 영향을 끼치는 요소로 보고있다. 첫 번째로는 페이지가 얼마나 모바일 친화적인가를 평가하는 고려한 모바일 친화성, 두 번째로 거짓 정보로 사용자를 유입하거나 악성 코드를 포함하고 있는지를 평가하는 세이프 브라우징, 세 번째로는 보안 프로토콜 제공 여부, 마지막으로는 전면 광고 등 서비스 이용 방해 요소가 있다.
LCP를 개선하기 위해서는, 서버 응답 시간 및 리소스 로딩 시간을 줄이고 JS 및 CSS 파일이 렌더링 차단하는 경우를 최소화하는게 중요하다.
CSS는 HTML 파싱을 중단시키는 렌더링 차단 리소스다. 때문에 common.css이나 reset.css 등 쓰지 않는 파일이나 코드들을 정리하거나 공통되는 코드들을 재사용하는 것이 좋다.
웹팩 등 번듈러를 사용해 페이지에 사용되는 자원을 묶어 리소스 요청을 줄일 수 있다.
preload
웹 사이트를 방문했을 때 페이지 사용될 자원들을 서버에 요청하면, 서버는 요청 순서와 상관없이 준비되는 순서대로 응답하게 된다. 이 때 rel=”preload”
를 적용하여 우선 순위를 부여하고, 특정 자원을 빠르게 로딩할 수 있다. 주의할 점은 preload를 적용하면 사용 여부와 관계 없이 무조건 다운로드 받기 때문에, 페이지에서 반드시 사용하는 자원에 preload를 사용해야한다는 것이다.
<head>
<link rel="preload" href="style.css" as="style">
//cors 속성도 추가 가능
<link rel="preload" href="webfont.woff2" as="font" type="font/woff2" crossorigin>
</head>
//href : 로딩할 url
//as : 파일 형식(audio, document, image, font 등)
preconnect
preconnect는 페이지에서 참조하는 외부 도메인 리소스를 미리 연결하도록 브라우저에게 지시하는 것이다. 보통 연결을 구축할 때 DNS Lookup, TCP Handshake, TLS 등 서버와의 여러 차례 왕복이 발생한다. 이 과정을 미리 처리하여 사용자로 하여금 어플리케이션이 빠르게 느껴지도록 할 수 있다. 다만, preconnect는 리소스를 많이 먹는 작업이기 때문에 과도한 사용은 자제해야한다.
<head>
<link rel="preconnect" href="https://www.example.com">
</head>
prefetch
prefetch는 당장 필요하진않지만 향후 사용자와 상호작용 과정에서 필요할 수 있을 리소스들을 미리 받아와 캐시에 저장한다. 예를 들어 사용자가 1페이지에 접속했을 때, 2페이지를 클릭할 것을 예상하여 미리 로드하는 경우에 prefetch를 적용할 수 있다.
<head>
<link rel="prefetch" href="listpage_2.html">
</head>
이미지 레이지로딩
자원을 모두 로드하면 그만큼 초기 렌더링 속도가 느려질 수 밖에 없다. 따라서 viewport 밖에 있는 이미지는 로딩하지 않고, 스크롤이 접근했을 때 자원을 요청하는 방식을 레이지로딩이라고 한다. img loading 속성을 사용하면 지연 로딩 혹은 병렬 로딩 할 수 있다.
<img src="item.jpg" loading="lazy" alt>
스프라이트 이미지 사용하기
아이콘, 광고 이미지, 로고 등 한 페이지에서 사용되는 이미지들을 묶어 1개의 이미지로 만드는 방식을 말한다. 파일 개수만큼 리소스 요청을 줄일 수 있다.
CLS를 개선하기 위해서는 크기가 정해지지 않는 이미지 자원들, 리플로우를 유발하는 동적 컨텐츠, FOIT나 FOUT를 발생시키는 웹폰트를 최소화하는 것이 중요하다.
이미지나 비디오 요소에 width, height 값 명시하기
스켈레톤 UI
데이터가 로드되기 전에 미리 윤곽을 보여주는 스켈테론 UI를 사용하여, 데이터가 나올 자리를 미리 표시한다.
애니메이션의 경우 width,height를 직접 조정하는 것보다 transform을 사용하는 것이 성능 및 UX 측면에서 유리하다.
FOIT, FOUT 줄이기
먼저 FOIT(Flash of invisible text)란 브라우저가 폰트를 다운로드 받기 전까지 텍스트를 렌더링하지않아 텍스트가 보이지 않는 현상을 의미한다. 그리고 FOUT(Flash of unstyled text)란 브라우저가 폰트를 다운로드 받기 전에 대체 폰트로 보여주다가 다운로드가 끝나면 reflow하는 현상을 의미한다.
일단 각 브라우저마다 동작방식이 다른데 엣지는 FOUT이고 크롬, 파이어폭스,오페라, 사파리 FOIT 3S를 사용하는데 여기서 3S란 폰트 다운로드를 3초동안 기다렸다가 대체 폰트를 보여주는 방식을 말한다.
font-display 옵션을 사용해서 브라우저가 폰트를 보여주는 방식을 설정할 수 있다.
@font-face {
font-family: 'Roboto';
font-display: fallback;
src: url(#{$font-folder}/Roboto-Light.woff2) format('woff2');
}
preload를 사용하여 폰트 파일 미리 로드하기
<head>
<link rel="preload" href="webfont.woff2" as="font" type="font/woff2" crossorigin>
</head>
FID 수치를 개선하기 위해서는 렌더링 차단 리소스인 CSS 및 JS 코드를 최적화 하는 것이 중요하다.
// 코드 경량화 적용 전
body {
}
h1 {
color:tomato;
}
// 코드 경량화 적용 후
h1{color:tomato}
불필요한 공백 및 줄바꿈, 코드 제거
코드 스플리팅
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() => import('@components/hello'))
function DynamicPage() {
return (
<div>
<Header />
<DynamicComponent />
</div>
)
}
export default DynamicPage
빌드 타임에서 webpack에 의해 모든 파일들이 하나로 묶이고, 로드하는데 오랜 시간을 소요하게 된다. 이를 개선하여 필요한 파일이나 코드만 로드하고 필요하지 않은 파일들은 나중에 로드하여 로딩시간을 개선하는 최적화 기법이 바로 코드 스플리팅이다. React 16부터는 React.Lazy와 Suspense가, Next.js에서는 next/dynamic가 동적 Import를 지원하고 있다. 특정 라우트에만 매칭되는 루트 컴포넌트들에 next/dynamic을 적용할 수 있다.
자바스크립트 실행 우선 순위 설정
네트워크와 메인스레드에 영향을 주는 third-party 라이브러리(외부 생산자가 프로그래밍 지원용으로 만든 라이브러리)는 자바스크립트 defer 옵션을 사용하여 렌더링 차단을 최소화한다. 그리고 third-party 라이브러리가 메인 자바스크립트 파일보다 우선순위가 높아 페이지 상호작용을 저하시키지 않도록 설정한다.
Core Web Vitals은 웹페이지가 얼마나 향상된 사용자 경험을 제공하는지 보여주는 품질 지표이다. Lighthouse, PageSpeed Insights, Chrome Devtool 등 다양한 모니터링 도구를 통해Core Web Vitals 수치를 측정할 수 있다. 이 중 Lighthouse를 중심으로 어떤 항목을 기준으로 Core Web Vitals 모니터링하는지 살펴보자.
Generate report
버튼을 누른다.화면에 콘텐츠가 얼마나 빨리 표시되고, 사용자가 얼마나 빨리 인식할 수 있는 지 평가한다. 속도 저해요소가 무엇인지 평가한다.
img 태그에 alt 속성이 있는지, html태그에 lang 속성이 있는지, 배경색과 전경색의 대비가 충분한지 등을 확인한다.
웹 표준 사례를 따르고 있는지 확인한다. 어플리케이션을 가동할 때 console에 오류가 출력되는지, 불필요한 api 호출이 있진 않은지, HTTPS를 통해 페이지에 접근할 수 있는 지 체크한다.
페이지가 검색 엔진에 최적화되어있는지 확인한다. 각 사용자가 디바이스로 접속했을 때 콘텐츠를 읽는 데 불편함이 없는 글꼴 크기를 사용하는지, robots.txt는 유효한지 등을 체크한다.
Progressive Web App이란, 웹과 네이티브 앱의 이점을 모두 가지도록 개발된 웹앱을 의미한다. Lighthouse에서는 모바일 네트워크에서 페이지 로드가 빠른지, 웹이 HTTP를 HTTPS로 리다이렉션하는지 여부 등을 확인한다.
사용자가 페이지를 로드했을 때 페이지 컨텐츠의 일부가 처음 화면에 렌더링되는 시간
뷰포트 내 콘텐츠가 빠르게 채워지는 속도.
뷰포트 중 가장 큰 영역을 차지하는 이미지나 텍스트 요소가 처음 로딩되는 시점
페이지가 사용자와 상호작용하는 상태가 될 때까지 걸리는 시간
TBT는 메인스레드가 50ms 이상 차단되었을 때, FCP(First Contentful Paint)에서 TTI(Time to Interactive)까지 걸리는 총 시간.
시각적인 안정성을 정량화한 지표(사용자가 예상치 못한 레이아웃을 경험하는 빈도)