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가 로드되지 않다가 첫 렌더 이후에 로드되기 시작하는 것이다.
갑자기 퍼포먼스가 20점 가깝게 올랐다. FCP가 확연히 줄어들었다. 하지만 LCP가 아직 높기 때문에, 이번에는 LCP를 잡아 보자.
상세 진단 결과에서 LCP를 보니, 썸네일을 로딩하는 시간이 오래 걸리는 듯 했다.
이미지를 느리게 로딩하는 방법을 찾다 보니 React.lazy
와 React.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
에는 컴포넌트가 로드되기 전에 보여 줄 내용을 넣어 주면 된다.
이렇게 이미지를 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 폰트가 보였다가 폰트가 로딩되면 내가 설정한 폰트가 보이게 된다.
아래 항목을 보면
Avoid enormous network payloads
항목이 있다.
확인해보니 Pretendard 폰트는 다이나믹 서브셋 폰트를 사용하고 있는데, Pretendard-Variable 폰트는 그냥 폰트를 사용하고 있는 것 같았다.
다이나믹 서브셋은 글에서 쓰이는 유니코드 영역의 문자가 사용될 때, 폰트 파일을 다운로드하기 때문에 다운로드할 폰트 파일을 최소화할 수 있다.
링크에 있는 가변 다이나믹 서브셋 폰트로 바꾸고 나니
퍼포먼스가 10점정도 올랐지만 아직 갈길이 멀다.
나머지 항목들을 잡으려고 여러가지로 검색/삽질해봤는데 잘 고쳐지지 않아서 우선 여기까지만 하고 라이브러리 버전 올리기 / 콘솔 에러 제거하기 등 다른 일들을 먼저 하고 와야겠다.