이미지가 많은 웹 사이트 성능 개선하기

김채은·2024년 1월 25일
2
post-thumbnail

Lighthouse 10 Performance 점수 평가 요소

FCP(First Contentful Paint)

사용자가 페이지에 처음 도착한 시점부터 페이지 콘텐츠 일부가 화면에 렌더링된 시점까지의 시간

  • 점수에 10% 영향
  • '콘텐츠'는 텍스트, 이미지(배경 이미지 포함), <svg> 요소 또는 흰색이 아닌 <canvas> 요소를 의미
  • 우수한 사용자 환경을 제공하려면 사이트의 첫 콘텐츠 페인트가 1.7초 이하여야 한다.

LCP(Largest Contentful Paint)

사용자가 처음 페이지로 이동한 시점을 기준으로 표시 영역 내 표시되는 가장 큰 이미지 또는 텍스트 블록의 렌더링 시간

  • 점수에 10% 영향
  • <img> 요소
  • <svg> 요소 내의 <image> 요소
  • 포스터 이미지가 있는 <video> 요소 (포스터 이미지 로드 시간이 사용됨)
    url() 함수를 통해 로드된 배경 이미지가 있는 요소(CSS 그라데이션과 반대)
    텍스트 노드 또는 기타 인라인 수준 텍스트 요소 하위 요소를 포함하는 블록 수준 요소.
  • <video> 요소 자동재생을 위해 그린 첫 번째 프레임 (2023년 8월 기준)
  • 애니메이션 GIF와 같은 애니메이션 이미지 형식의 첫 번째 프레임 (2023년 8월 기준)

TBT(Total Blocking Time)

마우스 클릭, 화면 탭 또는 키보드 누름과 같은 사용자 입력에 응답하지 못하도록 차단된 총 시간

  • 점수에 25% 영향
  • 50ms를 초과하여 실행되는 모든 작업은 장기 작업
  • 50밀리초 후의 시간이 차단 부분입니다
  • 예를 들어 Lighthouse가 70ms 길이의 작업을 감지하면 차단 부분은 20ms

CLS(Cumulative Layout Shift)

예상치 못한 레이아웃 변경에 관한 레이아웃 변경 점수

  • 점수에 30% 영향

SI(Speed Index)

페이지 로드 중 콘텐츠가 시각적으로 표시 되는 속도

  • 점수에 25% 영향
  • 페이지가 얼마나 빠르게 뷰포트에 채워지는지

Text Me의 현황

Performance 점수가 55점으로 평가 되었다. 아래의 기준으로 보았을 때 '개선 필요' 중에서도 '나쁨'에 가까운 수치이다.

0~49 (빨간색): 나쁨
50~89 (주황색): 개선 필요
90~100 (녹색): 좋음

특히 LCP가 굉장히 안좋게 나왔는데, 아무래도 디자인과 감성이 중요한 사이트이다보니 고화질 이미지가 사용되어 자원의 로드가 느렸다.

이미지 최적화

LightHouse에서는 다음과 같이 사이트의 문제점과 해결책을 제시해준다.

next/image 사용

<img> 대신 next/image의 <Image> 사용하면 레이아웃 최적화와, 이미지 format을 자동으로 webp, avif로 변환하여 로드할 수 있다.

import Image from "next/image";

const CarImg = styled(Image)`
	// ...
`;

avif와 webp는 png나 jpg에 비해 높은 압축률을 가지기 때문에 웹 사이트에 적합하다. avif는 압축률이 더 높지만 지원하지 않는 브라우저가 꽤 있기 때문에, 최우선순위에 둔 뒤 지원하지 않는 브라우저인 경우 webp를 사용하도록 옵션을 설정할 수 있다.

const nextConfig = {
	// ...
  	images: {
    	formats: ["image/avif", "image/webp"],
  	}
};

module.exports = nextConfig;

이미지 preload

headlink 태그를 추가하여 이미지 로드 우선순위를 높게 설정할 수 있다.

	<Head>
        <link
          rel="preload"
          fetchpriority="high"
          as="image"
          href="/static/images/room-background.webp"
        />
	</Head>

Next.js에서 사용하려면 다음과 같이 custom module을 작성해주어야 한다.

  • /custom.d.ts
import { AriaAttributes, DOMAttributes } from "react";

declare module "react" {
  interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
    fetchpriority?: "high" | "low" | "auto";
  }
}
  • /tsconfig.json
{
  // ...
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "curstom.d.ts"]
}

번들 사이즈 최적화

번들 사이즈가 크면 페이지 로드에 오랜 시간이 소요된다. build 후 다음과 같이 번들 사이즈를 체크할 수 있고, webpack bundle analyzer를 활용하면 더욱 정확하게 어느 파일에서 번들 사이즈가 크게 측정되는지 알 수 있다.

코드 스플리팅

종합적으로 체크하여 초기 로드가 불필요한 컴포넌트들은 코드 스플리팅했다.
모달, 애니메이션 라이브러리, 이벤트에 따라 발생하는 컴포넌트들으로 보면 된다.

const AlertModal = dynamic(() => import("../../components/room/AlertModal"));
const SnowFall = dynamic(() => import("react-snowfall"));
const LetterViewContainer = dynamic(
  () => import("../../components/room/LetterViewContainer")
);
const SaveModal = dynamic(() => import("../../components/room/SaveModal"));

번들 크기가 빨간 색으로 뜨던 페이지들이 사라졌다.

폰트 최적화

사용하지 않는 폰트 정리

사용하지 않는 폰트와 폰트 사이즈들을 불러오고 있어서, 이것들을 정리했다.

Subset 폰트 적용

네트워크 탭을 확인해보았을 때, 폰트 자체의 사이즈도 800kB 이상으로 컸기 때문에 subset 폰트로 변환 작업을 진행했다.

그 결과 폰트 사이즈가 1/4로 줄고, 로드 시간이 33ms에서 5ms로 줄어들었다.

Origin 연결 최적화

항상 사용되는 origin에 대해서 미리 연결을 해주는 link 태그를 사용하라는 메시지이다. 서버 URL과 구글 애널리틱스 origin을 연결해주었다.

	<Head>
      <link rel="preconnect" href={process.env.NEXT_PUBLIC_BASE_URL} />
      <link rel="preconnect" href={"https://www.googletagmanager.com"} />
	</Head>

마무리

55점에서 76점으로, Performance 점수가 38% 개선되었다.

성능 최적화라고 하면 어렵게 느껴지고 복잡할 것 같아서 항상 미뤄왔었는데 생각보다 간단한 작업만으로 성능을 개선할 수 있었다. 다만 세부적/반복적 작업과 지속적인 테스트, 모니터링이 필요해서 재미있지는 않았다...
하지만 유저의 경험에 웹 사이트 성능이 큰 영향을 미친다는 것은 굉장히 자명한 사실이다. 개발자가 힘들고 지루하더라도 사이트 성능을 모니터링하고 개선하는 작업은 필수적이다!
76점에 만족하지 않고 조금 더 개선점을 찾아 나가려고 노력할 것이다.

profile
배워서 남주는 개발자 김채은입니다 ( •̀ .̫ •́ )✧

0개의 댓글