#6 최적화 (1)

Lighthouse 점수를 올려보자


Table Of Contents


들어가기


remix로 작업하고 있는 블로그속도가 느리다는 이야기를 많이 들었다. Lighthouse로 성능 측정을 해보고, 점수를 올리기 위해 노력해보자.

메인 페이지 성능 측정


최적화 전

메인 페이지 성능 측정 결과는 55점... FCP가 12.3s, LCP가 13.7s로 엄청 느리다는 걸 알았다.

분석 결과

Diagnotics를 열어보니, Eliminate render-blocking resources 항목을 먼저 처리할 수 있을 것 같았다. font를 로딩하는 데 910ms, 거의 1초나 되는 시간이 걸리고 있다.

블로그에는 Pretendard github에서 추천하는 설정대로 아래처럼 font-family를 등록했다.

font-family: "Pretendard Variable", Pretendard, -apple-system,     BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI",     "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji",     "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;

따라서 이렇게 폰트를 로드하고 있었는데, 이 부분에서 시간이 오래 걸리는 것 같다.

<link rel="stylesheet" as="style" crossorigin href="....css" /> <link rel="stylesheet" as="style" crossorigin href="....css" />

stylesheet lazy load등으로 검색해보니 폰트를 lazy load할 수 있는 방법이 있는 것 같아서 해당 방법을 적용했다.

let isInitialRender = true; export default function App() { const isInitialRenderRef = useRef(true); const [, rerender] = useState(false); useEffect(() => { if (isInitialRenderRef.current) { isInitialRender = false; isInitialRenderRef.current = false; rerender(true); } }, []); ... <link rel="stylesheet" as="style" href="....css" media={isInitialRender ? "print" : "all"} /> <link rel="stylesheet" as="style" href="....css" media={isInitialRender ? "print" : "all"} />

isInitialRenderRef를 통해 최초 렌더인지 확인하고, 최초 렌더가 맞다면 inInitialRenderRef, isInitialRender를 false로 바꾼다. 해당 동작은 새로 렌더를 촉발하지 않기 때문에 renderer라는 set function을 따로 선언해서 렌더링을 촉발하는 역할로 사용한다. 두 번째 렌더에서는 isInitialRender가 false이기 때문에, 아래 media={isInitialRender ? "print" : "all"}media={"all"}로 평가된다. 즉, 첫 렌더시에는 css가 로드되지 않다가 첫 렌더 이후에 로드되기 시작하는 것이다.

font lazy loading 후

갑자기 퍼포먼스가 20점 가깝게 올랐다. FCP가 확연히 줄어들었다. 하지만 LCP가 아직 높기 때문에, 이번에는 LCP를 잡아 보자.

font lazy loading 후 진단

상세 진단 결과에서 LCP를 보니, 썸네일을 로딩하는 시간이 오래 걸리는 듯 했다. 이미지를 느리게 로딩하는 방법을 찾다 보니 React.lazyReact.Suspense를 이용하는 방법을 알게 되었다.

원래

<Thumbnail thumbnail={postData.thumbnail} />

처럼 이미지를 불러왔다면,

먼저

import { lazy, Suspense } from "react";

처럼 lazy, Suspense를 import해준다.

const Thumbnail = lazy(() => import("./Thumbnail"));

그리고 lazy를 이용해서 컴포넌트를 로딩해주고,

          <Suspense fallback={<></>}>             <Thumbnail thumbnail={postData.thumbnail} />           </Suspense>

Suspense로 원래 컴포넌트를 감싸준다. fallback에는 컴포넌트가 로드되기 전에 보여 줄 내용을 넣어 주면 된다.

image lazy loading 후

이렇게 이미지를 lazy loading하고 나니 퍼포먼스가 94점이 나왔다🥳 메인 페이지에는 아직 별 내용을 넣지 않아 더이상 최적화할 내용이 별로 없을 것 같으니 다른 페이지를 최적화해보자.

포스트 페이지 성능 측정


가장 최근 포스트 페이지에서 성능 측정을 해 보았다.

포스트 페이지 성능 측정

40점이 나왔는데, 메인 페이지와는 달리 TBT가 매우 느리게 나왔다.

진단 결과

4항목정도에서 빨간불이 들어왔다.

font-display: swap;

Ensure text remains visible during webfont load

우선 제일 위 항목부터 살펴보니 웹폰트 관련 문제였다. 위 페이지에서는 katex 문법 역시 파싱해서 보여주는데, 이 과정에서 관련 폰트를 다운받도록 되어 있었다. 그런데 font-display: swap; 옵션이 들어가 있지 않았기 때문에 폰트가 로딩되기 전에는 폰트가 있어야 할 자리가 빈 칸으로 보인다.

@font-face { font-family: "KaTeX_AMS"; src: url("/fonts/KaTeX_AMS-Regular.woff2") format("woff2"), url("/fonts/KaTeX_AMS-Regular.woff") format("woff"), url("/fonts/KaTeX_AMS-Regular.ttf") format("truetype"); font-weight: normal; font-style: normal; font-display: swap; }

이렇게 font-display를 추가해주면, 폰트가 로딩되기 전까지는 fallback 폰트가 보였다가 폰트가 로딩되면 내가 설정한 폰트가 보이게 된다.

서브셋 폰트 이용하기

font display 이후

아래 항목을 보면

Avoid enormous network payloads

항목이 있다. 확인해보니 Pretendard 폰트는 다이나믹 서브셋 폰트를 사용하고 있는데, Pretendard-Variable 폰트는 그냥 폰트를 사용하고 있는 것 같았다. 다이나믹 서브셋은 글에서 쓰이는 유니코드 영역의 문자가 사용될 때, 폰트 파일을 다운로드하기 때문에 다운로드할 폰트 파일을 최소화할 수 있다. 링크에 있는 가변 다이나믹 서브셋 폰트로 바꾸고 나니

폰트 최적화 이후

퍼포먼스가 10점정도 올랐지만 아직 갈길이 멀다.

나머지 항목들을 잡으려고 여러가지로 검색/삽질해봤는데 잘 고쳐지지 않아서 우선 여기까지만 하고 라이브러리 버전 올리기 / 콘솔 에러 제거하기 등 다른 일들을 먼저 하고 와야겠다.