12. 모든 웹 개발자가 관심을 가져야 할 핵심 웹 지표

영근·2024년 4월 20일
0

12.2 핵심 웹 지표란?

  • 웹사이트에서 뛰어난 사용자 경험을 제공하는 데 필수적인 지표를 일컫는 용어
  • 지표
    • 최대 콘텐트풀 페인트(LCP : Largest Contentful Paint)
    • 최초 입력 지연(FID : First Input Delay)
    • 누적 레이아웃 이동(CLS : Cumulative Layout Shift)
    • 최초 바이트까지의 시간(TTB : Time To First Byte)
    • 최초 콘텐트풀 시간(FCP : First Contentful Paint)

12.3 최대 콘텐트풀 페인트(LCP)

정의

페이지가 처음으로 로드를 시작한 시점부터 뷰포트 내부에서 가장 큰 이미지 또는 텍스트를 렌더링 하는 데 걸리는 시간

  • 큰 이미지와 텍스트
    • <img>
    • <svg> 내부의 <image>
    • poster 속성을 사용하는 <video>
    • url()을 통해 불러온 배경 이미지가 있는 요소
    • 텍스트와 같이 인라인 텍스트 요소를 포함하고 있는 블록 레벨 요소(<p>, <div> 등이 포함된다)
  • 실제 크기가 크더라도 뷰포트 영역 밖에 넘치는 요소가 있으면 해당 영역의 크기는 고려되지 않는다.

의미

  • DOMContentLoaded(이벤트)
    • HTML 문서를 완전히 불러오고 파싱했을 때 발생하는 이벤트
    • 페이지의 document를 대상으로 일어나며, 한 번만 호출된다.
    • 스타일시트, 이미지, 하위 프레임의 로딩은 기다리지 않는다. -> 페이지가 로딩되었다고 판단할 수 없다.
  • 일단 뷰포트 영역에 보이는 부분이 완료되면 사용자는 페이지 로딩이 완료되었다고 여긴다. -> 따라서 LCP가 만들어졌다.
  • 2.5초 이내로 응답이 오면 좋음, 4초 이내면 보통, 그 이상은 나쁨

개선 방안

  • 최대 콘텐트풀 페인트 예상 영역에 이미지가 아닌 문자열을 넣는다.
  • 이미지
    • <img>
      • 브라우저의 프리로드 스캐너에 의해 먼저 발견되어 빠르게 요청이 일어난다.
      • 프리로드 스캐너 : HTML 파싱 단계를 차단하지 않고 이미지 등 빠르게 로딩하면 좋은 리소스를 먼저 찾아 로딩하는 기능
      • 따라서 HTML 파싱이 완료되지 않아도 병렬적으로 리소스를 다운로드 한다.
      • <picture>도 마찬가지로 작동한다.
    • <svg>
      • <svg>내부의 <img>가 로딩 완료되기 전까지는 최대 콘텐트풀 페인트가 완료되지 않는다.
      • 모든 리소스를 다 불러온 이후에 이미지를 불러온다.
      • LCP 점수에 악영향을 미치는 방법
    • <video>의 poster
      • poster : 사용자가 video요소를 재생하거나 탐색하기 전까지 노출되는 요소
      • 마찬가지로 프리로드 스캐너에 의해 발견되어 <img>와 같은 성능을 나타낸다.
      • poster가 없는 video는 video를 실제로 로딩해 첫 번째 프레임을 poster 리소스로 대체한다. -> 따라서 poster를 꼭 넣어주자
    • background-image
      • CSS에 있는 리소스는 항상 느리다. -> DOM을 그릴 준비가 될 때까지 리소스 요청을 뒤로 미루기 때문이다.
      • 따라서 사용하지 않는 것이 좋다.

그 밖에 조심해야 할 사항

  • 이미지 무손실 압축을 통해 최소한의 용량으로 서비스하자
  • loading=lazy 는 중요하지 않은 리소스로 표시하는 전략 -> 최대 콘텐트풀 콘텐츠의 이미지를 빠르게 로드해야 하므로 사용하지 말자
  • fadein 등 애니메이션 : 애니메이션과 함께 뜬다면 LCP도 그만큼 늦어진다.
  • 클라이언트에서 빌드하지 말 것
    • useEffect(() => {
        (async function loadData() {
          const result = await fetch("...");
          if (result.ok) {
            setShow(true);
          }
        })();
      }, []);
    • LCP는 HTML을 다운로드한 직후가 아닌 리액트 코드를 파싱하고 읽어 API 요청을 보내고, 응답 받는 만큼 늦어진다.
    • 따라서 이 영역은 서버에서 미리 빌드된 채로 오는 것이 좋다.
  • 최대 콘텐츠풀 리소스는 직접 호스팅
    • 같은 도메인에서 직접 호스팅하는 것이 좋다.
    • 다른 출처에서 정제한 이미지를 가져오면 네트워크 커넥션부터 다시 수행해야 하기 때문이다.

12.4 최초 입력 지연(FID)

정의

사용자가 페이지와 처음 상호 작용할 때부터 해당 상호 작용에 대한 응답으로 브라우저가 실제로 이벤트 핸들러 처리를 시작하기까지의 시간

의미

  • 브라우저 내부 이벤트 반응이 늦어지는 이유 : 브라우저의 메인 스레드가 바쁘기 때문(JS는 싱글 스레드이기 때문이다)
  • 즉 이벤트가 발생하는 시점에 최대한 메인 스레드가 다른 작업을 처리할 수 있도록 여유를 만들어야 한다.
  • 사용자 경험(RAIL)
    • Response : 사용자의 입력에 대한 반응 속도. 50ms 미만으로 이벤트를 처리할 것
    • Animation : 애니메이션의 각 프레임을 10ms 이하로 생성할 것
    • Idle : 유휴 시간을 극대화해 페이지가 50ms 이내에 사용자 입력에 응답하도록 할 것
    • Load : 5초 이내에 콘텐츠를 전달하고 인터랙션을 준비할 것
  • FID는 R에 초점을 맞추고 있다.
  • 최초 이벤트 발생 ~ 해당 이벤트의 핸들러가 실행되는 순간까지 사이의 기간만 측정한다.

기준 점수

100ms 이내로 응답이 와야 좋음, 300ms 이내인 경우 보통, 그 이후의 경우 나쁨

개선 방안

  • 실행에 오래 걸리는 긴 작업을 분리
    • 꼭 웹페이지에서 해야 하는 작업인가 : 그렇지 않다면 서버로 옮겨서 처리하자
    • 긴 작업을 여러 개로 분리하기 : 팝업이나 레이어 등을 나중에 불러오는 방법 등
  • JS 코드 최소화
    • 당장 급하지 않은 코드는 지연 로딩 기법 등을 사용하는 것이 좋다.
    • 폴리필 확인
      • 폴리필이 필요한 환경인가? : 인터넷 익스플로러를 지원하지 않는다면 필요하지 않다.
      • 꼭 필요한 폴리필인가? : 자주 사용되지 않는다면 직접 구현하는 편이 도움이 된다.
      • 바벨을 사용한다면 @babel/preset-env를 사용해 애플리케이션 내부에서 사용하고 있는 내용만 폴리필에 담을 수 있다.
  • 타사 JS 코드 실행의 지연
    • GA 등 제3자가 만든 JS 코드는 <script>의 async나 defer를 이용한다.
      • defer
        • 해당 스크립트를 다른 리소스와 함께 병렬로 다운로드한다.
        • 다운로드 중 HTML 파싱 등 메인 스레드 작업은 멈추지 않는다.
        • 다운로드가 완료되었어도 이 스크립트의 실행은 페이지가 완전히 로딩된 이후에 맨 마지막에 실행된다.
      • async
        • 해당 스크립트를 다른 리소스와 함께 병렬로 다운로드한다.
        • async 리소스가 다운로드 완료되면 다른 리소스 다운로드를 기다리지 않고 바로 실행한다.
        • 따라서 다운로드가 완료된 순서대로 실행된다.
      • 둘 다 없는 경우 : script를 만나는 순간 다운로드가 우선되며, 다운로드가 완료되면 코드 실행이 우선된다. 다른 작업은 다운로드/실행이 끝날 때까지 미뤄진다.
    • 가능하다면 async, defer로 지연하는 것이 좋다.

12.5 누적 레이아웃 이동(CLS)

정의

페이지의 생명주기 동안 발생하는 모든 예기치 않은 이동에 대한 지표를 계산하는 것

의미

  • 최초 렌더링 이후에 실행되는 useEffect가 많을수록, useEffect가 렌더링에 영향을 미칠수록 좋지 못한 점수를 받을 가능성이 크다.
  • 뷰포트 내부의 요소에 대해서만 측정한다.
  • 최초 렌더링이 시작된 위치에서 이동이 발생하면 점수가 기록된다.
  • 요소가 추가되었어도 다른 요소의 시작 위치에 영향을 미치지 않았다면 레이아웃 이동으로 간주되지 않는다.
  • 포함되는 내용
    • 영향분율 : 레이아웃 이동이 발생한 요소의 전체 높이와 뷰포트 높이의 비율
    • 거리분율 : 레이아웃 이동이 발생한 요소가 뷰포트 대비 얼마나 이동했는지
  • 미리 노출이 예상되는 부분을 HTML로 자리 잡아 두는 것이 누적 레이아웃 지표에 큰 도움이 된다

기준 점수

0.1 이하인 경우 좋음, 0.25 이하인 경우 보통, 그 외에는 나쁨

개선 방안

  • 삽입이 예상되는 요소를 위한 추가적인 공간 확보
    • useEffect 내부에서 요소에 영향을 미치는 작업을 최소화한다.
    • 스켈레톤 UI처럼, 미리 무언가가 뜰 것으로 예상되는 공간을 미리 확보해둔다.
    • 서버 사이드 렌더링 : 동적인 요소의 유무를 사전에 판단해 클라이언트에 HTML을 미리 제공해준다.
  • 폰트 로딩 최적화
    • 폰트로 인해 발생할 수 있는 문제
      • FOUT(flash of unstyled text) : 기본 폰트로 보이고 있다가 뒤늦게 폰트가 적용되는 현상
      • FOIT(flash of invisible text) : 지정한 폰트가 보이지 않고, 기본 폰트도 없어서 텍스트가 보이지 않다가 뒤늦게 폰트가 로딩되면서 렌더링되는 현상
    • 유념해야 할 점
      • <link>의 preload 사용
        • rel=preload는 즉시 필요로 하는 리소스를 명시하는 기능 -> 렌더링을 가로막거나 레이아웃을 방해할 가능성이 줄어든다.
      • font-family: optional : 폰트를 불러오는 5가지 방법
        • auto(기본값) : 브라우저가 폰트를 불러오는 방법 결정
        • block : 폰트가 로딩되기 전까지 로딩 중단(최대 3초), 로딩 완료되면 폰트 적용
        • swap : FOUT 방식. 폴백 폰트로 렌더링한 뒤, 로딩이 완료되면 적용
        • fallback : 100ms간 텍스트가 보이지 않고, 그 이후에 폴백 폰트로 렌더링. 3초 안에 폰트가 로딩되면 해당 폰트로 전환
        • optional : 100ms간 텍스트가 보이지 않고, 그 이후에 폴백 폰트로 렌더링. 0.1초 이내로 폰트가 다운로드되어 있거나 캐시되어 있지 않다면 폴백 폰트를 사용한다. 일정 기간 폰트를 다운로드하지 못하면 연결을 취소한다.
      • 즉, 폰트를 우선순위 높게 설정하고, 로딩에 실패했다면 기본 폰트를 노출하는 방법
  • 적절한 이미지 크기 설정
    • img {
        width: 100%;
        height: auto;
      }
      • 누적 레이아웃 이동이 커진다.
      • 높이를 높게 잡아뒀다가, 이미지가 완전히 로딩된 이후에 높이를 계산해 자리잡기 때문이다.
    • 따라서 width, height 지정하거나 aspect-ratio로 비율을 계산한다.
    • 사용자 뷰포트 너비에 맞춰 다른 이미지를 제공하고 싶으면 srcset 속성을 사용한다.

이외의 지표들

최초 바이트까지의 시간(TTFB, Time To First Byte)

  • 브라우저가 웹페이지의 첫 번째 바이트를 수신하는 데 걸리는 시간
  • 600ms 넘으면 개선이 필요하다.
  • SSR 애플리케이션에서 중요하게 봐야하는 지표
    • 첫 번째 HTML을 만들기 위해 해야 하는 작업이 많거나 느릴 수록 시간이 길어지게 된다.
  • 개선하기 위해서 고려할 점
    • SSR
      • 로직을 최적화해 최대한 빨리 페이지를 준비시켜야 한다.
      • API 호출이 필요하다면 호출 횟수와 가져오는 정보의 크기를 최소화해서 최대한 응답 속도를 빠르게 해야 한다.
    • 주된 방문객의 국적을 파악해 최대한 해당 국적과 가깝게 서버를 위치시킨다.
    • 리액트 SSR : renderToNodeStream, renderToStaticNodeStream 등과 같은 스트리밍 API를 사용하는 것이 좋다.

최초 콘텐트풀 페인트(FCP, First Contentful Paint)

  • 페이지가 로드되기 시작한 시점부터 페이지 콘텐츠의 일부가 화면에 렌더링될 때까지의 시간을 측정한다.
  • 1.8초 이내면 좋음, 3.0초 이내면 보통, 그 이후는 개선 필요
  • 고려해야 할 점
    • 최초 바이트까지의 시간을 개선한다.
    • 렌더링을 가로막는 JS, CSS 같은 리소스를 최소화하고, 방해하는 리소스를 비동기적으로 로드한다.
    • Above the Fold에 대한 최적화
      • Above the fold : 스크롤을 굳이 하지 않아도 보이는 영역
      • 이 영역은 빠르게 보여줘야 하는 영역 -> lazy loading이나 스크립트에 의존해 요소가 렌더링 되는 것은 피해야 한다.
    • 페이지 리다이렉트 최소화
    • DOM 크기 최소화
profile
Undefined JS developer

0개의 댓글