본문 바로가기
React-study/dil

[모던리액트 Deep Dive] 12장 모든 웹 개발자가 관심을 가져야 할 핵심 웹 지표

by 어느새벽 2024. 12. 20.

 

12.1 웹사이트와 성능

웹사이트의 성능은 어떻게 측정할 있을까?

구글은 핵심 지표(Core Web Vital)라고 하는, 웹사이트의 우수한 사용자 경험을 제공하는 요한 몇 가지 핵심적인 요소를 꼽고 이에 대한 지표를 제시하고 있다.

 

12.2 핵심 웹 지표료란?

구글에만든 지표로, 웹사이트에서 뛰어난 사용자 경험을 제공하는 필수적인 지표를 일컫는 용어다. 

  • 최대 콘텐츠풀 페인트(LCP: Largest Contentful Paint)
  • 최초 입력 지연(FID: First Input Delay)
  • 누적 레이아웃 이됨 CLS: Cumulative Layout Shift)

아래 두 가지는 핵심은 아니지만 특정 문제를 진단하는데 사용한다.

  • 최초 바이트까지의 시간 (TTFB : Time To First Byte)
  • 최초 콘텐츠풀 시간 (FCP : First Contentful Paint)

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

12.3.1 정의

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

 뷰포트 내부에서 '큰 이미지와 텍스트’는 다음과 같이 정의돼 있다.

  • <lmg>
  • <svg> 내부의 <image>
  • poster 속성을 사용하는 <video>
  • url()을 통해 불러온배경 이미지가 있는 요소
  • 텍스트와 같이 인라인 텍스트 요소를 포함하고 있는 블록 레벨 요소 이 블록 레벨 요소에는 <p>, <div> 등이 포함된다

사용자의 시점에 언제 노출되는 시점은 각 엘리먼트가 등장한 시점부터 텍스트 또는 이미지가 완전로딩되는 시점으로 보면 된다.

최대 콘텐츠풀 페인트에 영향을 미치는 부분은 오직 뷰포트 영역뿐이다.

 

12.3.2 의미

사용자에게 있어 로딩이란 일단뷰포트 영역에 보이는부분을 준으로 것이므로 뷰포트에 메인 콘텐츠가 화변에 완전히 전달되는 속도를 기준으로 한다면 사용자는 페이지가 로딩이 완료됐다고 체감하는 시간과 매우 비슷하게 측정할 있을 것이다. 따라서 객관적으로 판단하기 위한 지표로 만들어진 것이 바로 최대 콘탠츠풀 인트(LCP)다.

 

12.3.3 예제

 

  1. 최초에 헤더가 가장 먼저 노출됐다. 그러므로 최대 콘텐츠풀 페인트는 헤더다.
  2. 그다음 바둑판 메뉴가 노출됐다. 이 영역은 헤더보다 크기 때문에 최대 콘텐츠풀 페인트가 헤더에서 이 바둑판 메뉴로 바뀌었다.
  3. 시간에 지나고 콘텐츠가 로딩되면서 최대 콘텐츠풀 페인트는 가운데 사진 영역으로 바뀌었다.
  4. 3번에서 현재 최대 콘벤츠풀 페인트인 영역은 이미지 로딩이 뭘요한데, 아직 이미지 로딩이 끝나지 않았다.
  5. 최대 콘텐츠풀 떼인트 영역 내부의 이미지 로딩이 마침내 끝나면서 최대 콘텐츠풀 페인트 지표가 기록된다.

 

즉, 최대 콘벤츠풀 페인트는 페이지 로딩에 따라 변화하는 지표다. 사용자가 이용하는 디바이스의 크기에 라,최대 콘텐츠풀 페인트 지표의 값이 달라질 있다.

 

12.3.4 기준 점수

최대 콘텐츠풀 페인트에서 좋은 점수란 해당 지표가 2.5 내로 응답이 오는 것이다. 4 이내로 응답이 온다보통, 이상이 걸려면 나쁨으로 판단된다.

 

12.3.5 개선 방안

 

텍스트는 언제나 옳다

좋은 점수를 얻는 가장 확실한 방법은 뷰포트 최대 영역 최대 콘텐츠풀페인트 예상 영역에 이미지가 아닌 문자열을 넣는 것이다.

 

이미지는 어떻게 불러올 것인가?

 

 

 

  • <img>: 이미지는 브라우저의 프리로드 스캐너에 의해서 먼저 발견되어 빠르게 요청이 일어난다. 프리로드 스캐너란 HTML을 파싱하는 단계를 차단하지 않고 이미지와 같이 빠르게 미리 로딩하면 좋은 리소스를 먼저 찾아 로딩하는 브라우저의 기능이다. <img> 내부의 리소스는 이처럼 HTML 파싱이 미처 완료되지 않더라도 프리로드 스캐너가 병렬적으로 리소스를 다운로드하므로 최대 콘텐츠풀 페인트 요소를 불러오기에 적절한 방법이다. 이는 <picture>도 마찬가지다.
  • <svg> 내부의 <img>: 테스트 결과를 보면 한 가흥미로운 결과를 볼 수 있는데. <img>미처 로딩되지 시점,<svg>만 로딩된 시점에 이미 최대 콘텐츠풀 페인트가 완료된 것으로 간주한다는 것이다. 이는 크롬 102 버전 이하의 버그로, 이후 버전에서는 수정된 것으로 보인다. 수정된 버전에서는 <svg> 내부의 <img>가 로딩이 완료되기 전까지는 최대 콘텐츠풀 페인트가 완료되지 않않는다. 이 외에도 <img>와 다른 점이 하나 더 있다. 바로 모든 리소스를 다 불러온 이후에 이미지를 불러온다는 것이다. 이는 <img>와 다른다. 즉, <svg> 내부의 <img>는 프리로드 스캐너에 의해 발견되지 않아 병렬적으로 다운로드가 일어나지 않는다. 이는 결국 최대 콘텐츠풀 페인트 점수에도 악영향을 미치므로 이러한 방식은 삼가는 것이 좋다.
  • <video>poster: poster사용자가 video 요소를 재생하거나 탐색하기 전까지 노출되는 요소다. 역시 마찬가지로 프리로드 스캐너에 의해 조기에 발견되어 <img> 같은 성능을 나타낸다. 그리고 한 가지 알아둬야 사실은 향후에 poster가 없는 video경우 video실제로 로딩해 첫 번째 프레임을 해당 poster 리소스로 대체할 예정이라는 것이다. 그러므로 video 최대 콘텐츠풀 페인트에 영향을 받을 같다면 poster 반드시 넣어주는 것이 좋다.
  • background-image : url() : background-image 비롯해서 css있는 리소스는 항느리다. 이러한 리소스는 브라우저가 해당 리소스를 필요로 하는 DOM그렬 준비가 때까지 리소스 요청을 뒤로 미루기 때문이다. 콘텐츠풀 페인트에도 별로 좋은 영향을 미치지 않는다는 것이다. 그러므로 가능하다면 background-image최대 텐츠풀 페인트와 같이 중요한 리소스에는 사용하지 않는 것이 좋다.

그밖에 조심해야 할 사항

  • 이미지 무손실 압축: 웹으로 서비스할 이미지는 가능한 한 무손실 형식으로 압축해 최소한의 용량으로 서비스하는 것이 좋다.
  • loading= lazy 주의 : loading=lazy는 리소스를 중요하지 않음으로 표시하고 필요할 때만 로드하는 전략으로,<img>, < iframe> 등에 적용할 수 있지만 문제는 최대 콘텐츠풀 콘텐츠의 이미지는 중요하지 않은 리소스로 분류해서는 안 된다는 것이다. 이는 그저 로딩 속도만 늦출 뿐 지표 접수에는 도움이 되지 않는다. 상대적으로 중요하지 않은 이미지에서는 사용해도 좋지만 최대 콘텐츠풀 콘텐츠의 이미지에는 사용하지 않는 것이 좋다.
  • fadein 같은 각종 애니메이션: 이미지그냥 뜨는 것보다 fadeln ease 10s 같이 처리한다면 최대 텐츠물 페인트도그만큼늦어진다.
  • 클라이언트에서 빌드하지 : 앞선 연구 사례에서 있는 최적의 시나리오는 무엇일까? 서버에서 빌드해온 HTML프리로드 스캐너가 바로 읽어서 최대 콘텐츠풀 페인트로 빠르게 가져가는 것이다. 그러나 만약 최대 콘텐츠풀 페인트에 대해 다음과 같은 useEffect 코드가 있으면 어떻게 될까?

  • 이 예제는 어떠한 API 엔드포인트에서 응답을 받아 최대 콘텐츠풀 페인트 영역의 노출을 제어하는 시나리오를 리액트 코드로 작성한 것이다. 이렇게 되면 결국 최대 콘텐츠풀 페인트는 HTML을 다운로드한 직후가 아닌 리액트 코드를 파싱하고 읽어서 API 요청을 보내고 응답을 받는 만큼 늦어지게 된다. 따라서 가능한 한 이 영역은 서버에서 미리 빌드된 채로 오는 것이좋다.
  • 최대 콘텐츠풀 리소스는 직접 호스팅 : 가능하다면 최대 콘텐츠풀 리소스는 같은 도메인에서 직접 호스팅하는 것이 좋다. 일반적인 경우 Cloudinary 같은 이미지 최적화 서비스를 사용해 하나의 이미지에 대해 크기도 줄이고, 포뱃도 변환하고, 압축해서 이미지를 관리하지만 다른 출처(origin)에서 이렇게 정제한 이미지를 가져오는 것은 최적화에 별로 좋은 영향을 미치지 않는다. 왜냐하면 이미 연결이 맺어진 현재 출처와는 다르게, 완전히 새로운 출처의 경우에는 네트워크 커넥션부터 다시 수행해야 하기 때문이다. 앞서 loading=lazy 의 경우와 마찬가지로 가능한 한 중요한 리소스는 직접 다루고 그 외에 덜 중요한 리소스에 대해서만 이미지 최적화 서비스를 사용하는 식으로 관리하는 것이 좋다.

12.4 최초 입력 지연(FID)

12.4.1 정의

사용자가 페이지와 처음 상호 작용할 때(예: 링크를 클릭하거나 버튼을 탭하거나 사용자 지정 JavaScript 기반 컨트롤을 사용할 때)부터 해당 상호 작용 대한 응답으로 브라우저가 실제로 이벤트 핸들러 처리를 시작하기까지의 시간을 측정한다. 

즉, 웹사이트반응성을 측정하는 지표가 바로 최초 입력 지연 (FID: First Input Delay)이다. 모든 입력에 대해 측정하는 것이 아니며, 최초의 입력 하나에 대해서만 응답 지연이 마나 걸리는지 판단한다.

 

12.4.2 의미

웹사이트 내부의 이벤트가 반응이 늦어지는 이유는 대부분 해당 입력을 처리해야 하는 브라우저의 메인 스레드가 바쁘기 때문이다. 메인 스레드가 바쁜 이유는 대규모 렌더링이 일어나고 있거나, 대규모 자바스크립트 파일을 분석하고 실행하는 다른 작업처리하는 리소스를 할애하고 있기 때문이다. 이런 경우자바스크립트 실행 환경‘싱글 스레드이기 때문에 자바스크립트가 이벤트 리스너와 같은 다작업을 실행할 없어 지연이 발생한다. 즉, 이벤트가 발생하는 시점에 최대한 메인 스레드가 다른 작업을 처리할 있도록 여유를 만들어 사용자에게 빠른 반응성을 보장할 수 있다.

 

한가지 최초 입력 지연을 이해하기 위해 알아야 하는 것은 바로 ‘사용자의 입력 이다. 스크롤이나 줌 등은 사용자의 입력이 아닌 애니메이션으로 분류해 측정 대상에서 제외한다.

구글은 사용자 경험을 크게 4 가지로 분류해 정의하는데 이를 RAIL 이라고 한다. 다음과 내용을 보면,

  • Response: 사용자의 입력에 대한 반응 속도 50ms 미만으로 이벤트를 처리할 것
  • Animation: 애니메이션의 각 프레임을 10ms 이하로 생성할 것
  • Idle 유휴 시간을 극대화해 페이지가 50ms 이내에 사용자 입력에 응답 하도록 할 것
  • Load : 5초 이내에 콘텐츠를 전달하고 인터랙션을 준비할 것

가운데 최초 입력 지연은 R 해당하는 응답에 초점을 맞추고 있다.

정리하자면, 최초 입력 지연이란 화면이 최초에 그려지고 사용자가 웹페이지에서 클릭 상호작용을 수행했을 메인 스레드가 이벤트에 대한 반응을 할 수 있을 때까지 걸리는 시간을 의미한다. 그리고 시간은 메인스레드가 처리해야 하는 다른작업이 많을수록 느려진다.

 

12.4.3 예제

 

예제는 최초 입력 지연이 매우 심하게 발생하는 어떤 힘수에 대해 크롬에서 디버깅을 통해 살펴본 것이다.

클릭은 2000ms 경에 일어났지만 실제 클릭에 따른 이벤트는 2600ms 경에 시작됐다. 이렇게 오래 걸렸던 이유는 클릭 벤트가 발생했던 시점 이전을 살펴보면 클릭 이벤트가 있기 전부터 무언가 메인 레드가 다른 일을 하고 것을 볼 수 있다. 즉, 메인 스레드가 작업 중인 시점에 클릭 이벤트가 일어났고클릭 이벤트는 메인 스레드가 이전에 하던 작업을 다 마무리하고 나서야 비로소 실행될 있게 됐다.

여기서 추가로 염두에 둬야 것은 이벤트가 처리되는 것이 얼마나 지연되는지만 판단한다는 것이다. 이벤트 핸들러가 완료되는 걸리는 시간은 측정하지 않는다. 만약 이벤트 핸들러의 실행 시간을 측정하고 싶다면 Event Timing API사용하는 것이 좋다.

 

12.4.4 기준점수

최초 입력 지연의 좋은 점수를 얻기 위해서는 100ms 이내로 응답이 와하며, 300ms 이내인 경우 보통이후의 경우에는 나쁨으로처리된다.

 

12.4.5 개선 방안

최초 입력 지연에 가장 영향을 미치는 메인 스레드에 이벤트를 실행할 여유를 줘야 한다.

 

실행에 오래 걸리는 긴 작업을 분리 

  • 꼭 웹페이지에서 해야 하는 작업인지 고민해보기 -> 서버로 옮겨서 처리하는 방법도 있다.
  • 긴 작업을 여러 개로 분리하기 
  • 웹페이지 최초 로딩에 필요하지 않은 내용을 나중에 불러오는 방법으로 리액트의 Suspense와 lazy, Next.js의 dynamic이 있다.

자바스크립트 코드 최소화

현대의 번들링 도구들은 코드 번들링에 필요한 코드만 모아서 최종 프로덕션 자바스크립트 코드로 생성한다. 빌드 과정에서 사용하지 않는 코드로 간주되면 번들링에서 제거된다. 이외에도 필요 없는 코드의 존재를 확인하려면 다음과 같다.

 

 

 

 

 

 

  • 기록 버튼을 클릭하고 웹페이지를 새로고침한다.
  • 커버리지가 기록된다.
  • 기록을 멈추려면 다시 기록 버튼을 클릭한다.

 

 

 

 

 

 

 

 

 

사용되지 않은 것으로 표시된 코드를 다 삭제해야하는 것은 아니지만 특정 이벤트에 따라 실행되는 코드, 예기치 못한 상황에서 실행될 코드 등 다양한 것들이 존재할 수 있다. 이런 코드들은 우선순위를 낮춰 위에 말한 지연 로딩 기법이나 필요한 순간 불러오는 등 바꾸면 된다.

 

 

 

 

 

또 한 가지 살펴볼 만한 것은 폴리필(polyfill) 이다. 폴리필이란 브라우저에서 지원하지 않는 기능을 사용하기 위해 웹페이지에서 직접 구현하고 집어넣는 코드를 의미한다. 한 메서드에 들어가는 폴리필의 크기는 제법 크다. 때문에 아래와 같이 확인해야 한다.

  1. 폴리필이 필요한 환경인가? 만약 인터넷 익스플로러 11과 같은 구형 브라우저 환경을 지원하지 않기로 결심했다면 대부분의 폴리필을 집어 넣을 필요가 없다.
  2. 꼭 필요한 폴리필인가? 애플리케이션의 여러 군데에서 자주 사용되는 코드인지 반드시 확인해 봐야 한다. 직접 저수준 자바스크립트 코드를 작성해 구현하는 편이 코드 크기를 줄이는 데 도움이 될 수 있다.

만약 바벨 같은 도구를 사용하고 있다면 @babel/preset-en 사용해 애플리케이션 코드에서 사용하고 있는 내용만 폴리필에 담을 있다. Next.js 의 SWC사용하고 있다면 이미 SWC 내부에 구현돼 있기 때문에 별도로 처리하지 않아도 것으로 보인다.

 

타사 자바스크립트 코드 실행의 지연

 

타사 스크립트는 대부분 웹페이지 로드에 중요한 자원이 아니므로 <script> async defer이용지연 불러오기를 하는 것이 좋다. 각각의 차이는 다음과 같다.

  • defer: script 에 defer 속성이 있다면 먼저 해당 스크립트를 다른 리소스와 함께 병렬로 다운로드한다. 다운로드하는 중에도 HTML 파싱 등의 메인 스레드 작업은 멈추지 않는다. 다운로드가 완료됐다 하더라도 이 스크립트의 실행은 페이지가 완전히 로딩된 이후에 맨 마지막에 실행된다.
  • async: script 에 async 속성이 있다면 마찬가지로 해당 스크립트를 다른 리소스와 함께 병렬로 다운로드 한다. async 리소스의 다운로드가 완료되어 버리면 다른 리소스의 다운로드가 완료되는 것을 기다리지 않고 바로 실행한다. 따라서 async 리소스의 실행 순서는 다운로드가 완료된 순서대로 실행된다
  • 둘 다 없는 경우 script 를 만나는 순간 다운로드가 우선되며 . 다운로드가 완료되면 코드 실행이 우선된다. 다른 작업은 다운로드와 실행이 끝날 때까지 미뤄진다.

스크립트는 async, defer 갈수록 실행은 뒤로 미뤄지지만 성능은 좋아진다. 

 

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

12.5.1 정의

페이지의 생명주기 동안 발생하는 모든 예기치 않은 이동에 대한 지표를 계산히는 것이 바로 누적 레이아이동( CLS: Cumulative Layout Shift) 이라고 한다. 다른 지표와 마찬가지로 지표가 낮을수록사용자가 겪는 예상치 못한 레이이웃 이동이 적을수록 좋은 웹사이트다.

 

12.5.2 의미

최초 렌더링 이후에 실행되는 useEffect 많을수그리고 useEffect 가 렌더링에 영향을 미칠수록 누적 레이아웃 이동에 좋지 못한 점수를 받을 가능성이 커진다.

누적 레이아웃 이동은 사용자의 가시적인 콘텐츠에 영향미쳐야 하기 때문에 뷰포트 내부의 요대해서측정하며 밖의 요소에 대해서는 측정하지 않는다.

요소가 추가됐다 하더라도 다른 요소의 시작 위치에 영향미치지 않았다연 레이아웃 이동으로 간주되지 않는다.

사용자 액션으로 인해 발생한 레이아웃 이동은 점수에 포함되지 않는다.

  • 영항분율: 레이아웃 이동이 발생한 요소의 전체 높이와 뷰포트 높이의 비율을 의미한다. 예를 들어 레이아웃 이동이 발생한 요소의 높이가 10 이고 예기치 않은 레이아웃 이동으로 인해 10 만큼 내려갔다고 가정해 보자. 이 경우 뷰포트의 높이 100 이라고 가정하면 레이아웃 이동으로 인해 총 10 +10 만큼 뷰포트에 영향을 미쳤으므로 이 경우 영항분율은 0.2 점이 된다((10+10)/100 = 0.2)
  • 거리분율: 레이아웃 이동이 발생한 요소가 뷰포트 대비 얼마나 이동했는지를 의미한다. 예를 들어, 예기치 않은 레이아웃 이동으로 인해 10 만큼 내려갔고, 전체 뷰포트가 100 이라먼 0.1점이 된다(10/100).

12.5.3 생략

 

12.5.4 기준점수

누적 레이아웃 이동의 경우 0.1 이하인 경우 좋음, 0.25 이하인 경우 보통이며 그 외에는 개선이 필요한 나쁜 점수로 보고된다.

 

12.5.5 개선 방안

삽입이 예상되는 요소를 위한 추가적인 공간 확보

useEffect의 내부에서 요소에 영향을 미치는 작업을 최소화하기. 사용이 불가피하다면 useLayoutEffect 훅을 사용해볼 여지가 있으나 오히려 다른 작업에 약영향을 미칠 수 있어 신중하게 선택할 것.

스켈레톤 UI의 사례처럼, 미리 무언가가 동적으로 것으로 예상되는 공간을 미리 확보해 두는 것도 좋은 방법이다.

가장 좋은 방법은 서버 사이드 렌더링이다. 동적인 요소의 유무를 사전에 판단하여 클라이언트에 HTML을 미리 제공할 수 있기 때문이다. 하지만 타사 스크립트에 의존해 처리하는 경우 SSR이 불가능할 수 도 있다.

 

폰트 로딩 최적화

폰트로 인해 발생할 수 있는 문제는 크게 두 가지다.

  • FOUT(flash of unstyled text): HTML 문서에서 지정한 폰트가 보이지 않고 대체 기본 폰트로 보01고 있다가 뒤늦게 폰트가 적용되는 현상
  • FOIT(flash of invisible text): HTML 문서에서 지정한 폰트가 보이지 않고, 기본 폰트도 없어서 텍스트가 없는 채로 있다가 뒤늦게 폰트가 로딩되면서 페이지에 렌더링되는 현상

사용자 기기의 기본 폰트 외에 다른 폰트를 사용한다면 다음과 같은 점을 유념해야 한다.

  • <link> 의 preload 사용: <link> 요소의 rel=preload 는 페이지에서 즉시 필요로 하는 리소스를 명시하는 기능이다. preload 로 지정된 요소는 웹페이지의 생명주기에서 초기에 불러와야 하는 중요한 리소스로 간주되므로 브라우저는 리소스를 더 빠르게 사용할 수 있도록 준비해 준다. 따라서 rel=preload 로 스타일이나 폰트를 지정하면 페이지의 렌더링을 가로막거나 레이아웃을 방해할 가능성이 줄어든다.
  • font-family: optional: 폰트를 불러올 수 있는 방법은 크게 다섯 가지로 나뉜다.
  • auto( 기본값): 브라우저가폰트를 불러오는 방법을 결정한다.
  • block: 폰트가 로딩되기 전까지 렌더링을 중단한다. (최대 3초) 웹 폰트의 로딩이 완료되면 비로소 폰트를 적용한다.
  • swap: 앞서 언급한 FOUT 방식이다. 우선 폴백 폰트로 글자를 렌더링한 다음, 웹 폰트의 로딩이 완료되면 웹 폰트를 적용한다.
  • fallback: 이 옵션을 사용하면 100ms 간 텍스트가 보이지 않고 그 이후에 폴백 폰트로 렌더링한다. 그리고 3초 안으로 폰트가 로딩된다면 해당 웹 폰트로 전환하고 그렇지 않다면 폴백 폰트를 계속 사용한다.
  • optional: fallback과 매우 유사하다 100ms 간 텍스트가 보이지 않고 폴백 폰트로 렌더링한다는 점은 동일하다. 그러나 0.1 초 이내로 폰트가 다운로드돼 있거나 캐시돼 있지 않다면 폴백 폰트를 사용한다. 또 한 가지 더 재밌는 사실은 브라우저가 네트워크 상태를 파악해 일정 기간 폰트를 다운로드하지 못한다면 연결을 취소한다는 것이다. 즉.0.1 초 내로 폰트가 다운로드되지 않으면 해당 폰트를 보여주지 않고 이후에 해당 폰트 리소스를 사용할 때를 고려해 폰트를 다운로드하긴 하나 이마저도 네트워크 상황에 따라 취소될 수 있다는 점이다.

적절한 이미지 크기 설정

width, height 지정한다. 

예제처럼 width: 100%; height:auto;와 함께 width, height를 원하는 비율로 지정하면 브라우저가 이미지를 로딩하기 전 적절한 비율을 계산해 이미지가 표시되는 만큼 면적을 할당해 둔다. 이는 aspect-ratio 속성 덕분인데 브라우저의 유저 에이전트 스타일(브라우저가 기본으로 제공하는 스타일)에 포함돼 있으며, 이미지의 비율을 자동으로 맞춰준다.

 

 

 

사용자 뷰포트에 맞춰야 하는 경우 srcset 속성을 사용하는 것이 좋다. 가로세로 비율이 똑같은, 그러나 크기가 다른 여러 개의 이미지를 미리 준비해 둔 다음 브라우저가 상황에 맞게 이미지를 사용할 수 있도록 준비하면 된다.

 

 

 

12.5.6 핵심 웹 지표는 아니지만 성능 확인에 중요한 지표들

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

브라우저가 웹페이지의 번째 바이트를 수신하는걸리는 시간을 의미한다. 즉, 페이지를 요청했을 요청이 완전히 완료되는 걸리는 시간을 측정하는 것이 아니라 최초의 응답이 오는 바이트까지가 얼마나 걸리는지를 측정하는 지표다. 지표는 600ms 이상 걸릴 경우 개선이 필요한 것으로 간주된다.

 

서버 사이드 더링은 최초 페이지를 만들기 위해 서버에서 어느 정도 작업을 수행해야 하기 때문에 특히 주의 깊게 봐야 할 지표다.

 

  • 서버 사이드 렌더링을 수행하고 있다면
  • 로직을 최적화해 페이지를 최대한 빨리 준비시켜야 한다. 여기서 말하는 로직이란 Next. js 를 예로 든다면 getServerSideProps 함수의 실행과 그 결과에 따른 정적 페이지 렌더링이 될 것이다. 페이지를 만드는 데 필요한 작업을 최소화하고 페이지를 그리는 데 중요한 내용만 서버 사이드 렌더링에서 준비하는 등의 최적화가 필요하다.
  • 서버 사이드 렌더링 시에 API 호출이 필요하다면 이 API 호출 또한 최적화할 필요가 있다. 호출 횟수와 가져오는 정보의 크기를 최소화해서 최대한 API 응답의 속도를 빠르게 하고 크기를 가능한 한 줄여야 한다.
  • 웹페이지의 주된 방문객의 국적을 파악해 최대한 해당 국적과 가깝게 서버를 위치시키는 것이 좋다. 응듭해야 할 서버가 사용자와 가까울수록 응답 속도가 빨라지기 때문이다.
  • 리액트 서버 사이드 렌더링이라면 renderToNodeStream, renderToStaticNodeStream과 같은 스트리밍 API를 사용하는 것이 좋다. 4.2절 ’서버 사이드 렌더링을 위한 리액트 API 살펴보기’에서 살펴본 것처럼 renderToString()은 완전히 HTML 을 그리고 나서야 다운로드가 완료되지만 스트리밍을 사용할 경우 완성된 영역부터 조각조각 받을 수 있어 최초 바이트까지의 시간을 단축할수 있다.

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

페이지가 로드되기 시작한 시점부터 페이지 콘텐츠일부가 화면에 렌더링될 때까지의 시간을 측정한다. 조금 쉽게 설명하자면 웹사이트에 접속한 순간부페이지에 뭐라도 뜨기 시작한 시점까지의 시간을 의미한다. 여기서 의미히는 뭐라도 뜨기 시작한’에 해당하는 요소는 텍스트, 이미지, svg 등을 의미한다.

 1.8 이내에 이뤄진다면 좋음, 3.0이내는 보이후는 개선이 필요한 것으로 보고된다.

  • 최초 바이트까지의 시간(TTFB) 을 개선: 일단 뭐라도 다운로드가 시작돼야 렌더링을 할 수 있다. 렌더링을 최대한 빠르게 하기 위해 최초 바이트까지의 시간을 단축해야 한다.
  • 렌더링을 가로막는 리소스 최소화: 자바스크립트나 css 같은 렌더링을 가로막는 리소스를 최소화하고, 렌더링을 방해하는 리소스를 비동기적으로 로드하도록 해야 한다. 렌더링이 최대한 빨리 이뤄져야 시용자에게 뭐라도 보여줄 수 있다.
  • Above the Fold에 대한 최적화:'Above the fold ’란 신문에서 비롯된 용어로 신문이 독자에게 제공됐을 때 가장 먼저 보이는 영역을 일컫는 말이다. 일반적으로 신문은 1 페이지가 반으로 접혀서 보여지는 형태인데, 이 반 으로 접혀져서 보이는 영역을 Above the fold 라 한다. 이와 비슷하게 웹에서는 최초에 스크롤을 굳이 하지 않아도 보이는 영역을 Above the fold 라고 한다. 이 영역은 최대한 사용지에게 빠르게 무언기를 보여져야 하는 영역이므로 게으른 로딩을 하거나 스크립트 (앞서 언급한 리액트의 useEffect 와 같이)에 의존해 요소가 렌더링되는 것을 피해야 한다. 게으른 로딩이 나 스크립트에 의존하는 것은 모두 최초콘텐츠풀 페인트에 도움이 되지 않는다.
  • 페이지 리다이렉트 최소화: 만약 특정 페이지에서 다른 페이지로 리다이렉트를 해야 한다면 그만큼 사용자에게 무언가를 보여줄 수 있는 시간이 지연되기 마련이다. 리다이렉트는 없거나 최소한으로 유지해야 한다.
  • DOM 크기 최소화: HTML의 크기가 크다면, 즉 DOM 이 복잡하고 크다면 그만큼 렌더링되는 데 시간이 오래 걸린다. 구글의 기준에 따르면 전체 DOM 노드는 1500 개 미만으로 깊이는 32 단계 정도까지만, 그리고 부모 노드는 자식 노드를 60 개 정도만 가지고 있어야 한다. DOM 이 필요 이상으로 많고 복잡하다면 이를 줄일 수 있는 방법을 고민해 봐야 한다.

 

 

 

 

반응형