사용자가 페이지에 처음 도착한 시점부터 페이지 콘텐츠 일부가 화면에 렌더링된 시점까지의 시간
<svg>
요소 또는 흰색이 아닌 <canvas>
요소를 의미사용자가 처음 페이지로 이동한 시점을 기준으로 표시 영역 내 표시되는 가장 큰 이미지 또는 텍스트 블록의 렌더링 시간
<img>
요소<svg>
요소 내의 <image>
요소<video>
요소 (포스터 이미지 로드 시간이 사용됨)<video>
요소 자동재생을 위해 그린 첫 번째 프레임 (2023년 8월 기준)마우스 클릭, 화면 탭 또는 키보드 누름과 같은 사용자 입력에 응답하지 못하도록 차단된 총 시간
예상치 못한 레이아웃 변경에 관한 레이아웃 변경 점수
페이지 로드 중 콘텐츠가 시각적으로 표시 되는 속도
Performance 점수가 55점으로 평가 되었다. 아래의 기준으로 보았을 때 '개선 필요' 중에서도 '나쁨'에 가까운 수치이다.
0~49 (빨간색): 나쁨
50~89 (주황색): 개선 필요
90~100 (녹색): 좋음
특히 LCP가 굉장히 안좋게 나왔는데, 아무래도 디자인과 감성이 중요한 사이트이다보니 고화질 이미지가 사용되어 자원의 로드가 느렸다.
LightHouse에서는 다음과 같이 사이트의 문제점과 해결책을 제시해준다.
<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;
head
에 link
태그를 추가하여 이미지 로드 우선순위를 높게 설정할 수 있다.
<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"));
번들 크기가 빨간 색으로 뜨던 페이지들이 사라졌다.
사용하지 않는 폰트와 폰트 사이즈들을 불러오고 있어서, 이것들을 정리했다.
네트워크 탭을 확인해보았을 때, 폰트 자체의 사이즈도 800kB 이상으로 컸기 때문에 subset 폰트로 변환 작업을 진행했다.
그 결과 폰트 사이즈가 1/4로 줄고, 로드 시간이 33ms에서 5ms로 줄어들었다.
항상 사용되는 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점에 만족하지 않고 조금 더 개선점을 찾아 나가려고 노력할 것이다.