이번에 개인 프로젝트로 NFT 거래소를 만들고 있는데, 최근에는 성능 최적화에 관심이 많이 생겨서 여러 가지 성능 최적화를 시도해 보았습니다.
성능 최적화는 주로 Next.js의 기능들을 활용하여 이루었는데, 정말 잘만들어진 프레임워크라는걸 다시 한번 느낄수 있었습니다.
1. Next 의 Image 를 활용한 최적화
next.config.js를 통해 이미지 최적화 옵션을 설정할 수 있습니다. 예를 들어, 이미지 파일 형식을 avif로 변경할 수 있습니다.
기본적으로 Next.js는 이미지를 webp 형식으로 변경해주는데, avif로 변경하면 인코딩에는 약간의 시간이 더 걸리지만, webp에 비해 압축률이 더 좋습니다.
따라서 처음 요청할 때는 avif가 약간 더 느리지만, 이후에는 캐싱된 상태이기 때문에 webp보다 빠르게 렌더링됩니다.
또한, placeholder와 blurDataUrl을 사용하여 이미지를 받아오기 전에 스켈레톤 이미지를 보여주어 UX 경험을 상당히 향상시킬 수 있습니다.
images: {
formats: ["image/avif", "image/webp"],
minimumCacheTTL: 31536000, //이미지의 캐싱시간을 정할수있다. 31536000 은 캐싱최대시간
remotePatterns: [
{
protocol: "https",
hostname: "**",
},
],
},
또한 placeholder 와 blurDataUrl 을 사용하여 이미지를 받안오기전 스켈레톤 이미지를 보여주어 UX 경험을 상당히 향상시킬수 있습니다.
2. dynamic import 활용하여 번들크기 줄이기
Dynamic import는 현재 사용되지 않는 컴포넌트들을 초기 로드되는 번들에서 제외시킨 후, 실제 사용될 때 다시 로드하여 사용하는 것을 말합니다. 예를 들면 모달(Modal)이 해당될 수 있을 것 같습니다.
https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#examples
실제 프로젝트에서도 Dynamic import를 사용하였는데, 위와 같이 Dynamic을 사용하면 초기 번들에 포함되지 않으며, 유저가 해당 모달을 클릭할 때부터 로드되어 화면에 렌더링될 것입니다. 다만 로드될 때 약간의 딜레이가 있기 때문에 Dynamic을 사용할 때는 팀원들과 적절한 소통을 나눈 후 사용해야 할 것 같습니다.
3. metadata API 를 활용하여 SEO 검색엔진 최적화
Next.js 13 버전으로 올라오면서 app directory로 page routing이 가능해졌습니다. 또한, app directory에서는 Metadata API를 활용하여 SEO를 개선 할 수 있습니다.
/* app/layout.tsx */
import "./globals.css";
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
display: "swap",
});
export const metadata = {
openGraph: {
image: "/images/azuki2.avif",
},
title: "QWERO - NFT Market Place",
description: "오직 나만의 NFT 를 소유하세요!",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ko" className={inter.className}>
<body>
<main>{children}</main>
</body>
</html>
);
}
`app/layout.tsx` 파일은 app 폴더 안에 있는 layout 컴포넌트입니다. layout 컴포넌트는 기본적으로 Server Component이며, app directory를 사용한다면 필수적으로 존재해야 하는 컴포넌트입니다.
Metadata API는 서버 컴포넌트에서 사용할 수 있는데, 이전 12 버전에서는 Next.js의 Head 태그를 import하여 page 폴더의 `index.tsx` 안에서 JSX 코드 부분에 직접 넣어서 사용하였습니다. 하지만 13 버전에서는 그럴 필요 없이 Metadata 객체를 export 해주기만 하면 되고, import를 해줄 필요도 없습니다.
3 - 1. metadata 동적 생성하기
import { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: ParamsType;
}): Promise<Metadata> {
const { tokenId, collectionContract } = params;
const info = await getNFTInfoDetail(collectionContract, tokenId); //데이터 페칭
return {
title: `${info.name} - ${info.collection_name} | QWERO`,
description: info.collection_name,
openGraph: {
images: { url: info.image, alt: info.name },
title: info.name,
description: `Get Your NFT Right Now! - ${info.collection_name}`,
url: `/collection/${collectionContract}/${tokenId}`,
},
};
}
동적으로 원하는 데이터를 metadata로 바로 적용시킬수 있는 메서드 입니다. 이 메서드 또한 서버컴포넌트에서만 작동하게됩니다.
저 같은 경우 동적 metadata를 원하는 page.tsx 에서 직접 사용하였습니다.
ps. 만약 동적메타데이터와 데이터페칭을하여 화면에 랜더링 하는 코드가 하나의 page.tsx 파일에 있으면 요청을 두번 하게 되는거 아니야? 라고 생각하실수도 있습니다.
Next 는 fecth를 사용한다면 자동적으로 모든요청은 캐시되고 중복은 제거됩니다. 즉 두번쨰 요청은 첫번째의 요청 결과를 재사용하게 되는셈이니 요청도 한번 하는셈입니다.
https://nextjs.org/docs/app/building-your-application/data-fetching/caching#fetch
4. webpack-bundle-analyzer 를 활용한 번들크기 파악후 tree-shaking
`webpack-bundle-analyzer`를 활용하면 초기 로드되는 번들의 크기를 파악할 수 있습니다. 또한, 시각적으로 어떤 파일이 가장 크기를 많이 차지하는지 확인할 수 있습니다.
저는 프로젝트에서 사용되는 아이콘들을 `react-icons` 패키지를 사용했는데, 해당 패키지는 2MB가 넘는 고용량 패키지였고 이를 제거한 후 직접 아이콘들을 정적 폴더에 저장하여 사용하여 Tree Shaking을 했습니다.
5.레이아웃 시프트 줄이기
레이아웃 시프트(Layout Shift)란 보통 데이터가 로딩되기 전에 화면이 비어있다가 데이터가 로딩된 후 화면이 채워지면서 화면이 위아래로 움직이는 현상을 말합니다. 이는 Lighthouse에서도 점수로 측정하고 있으며, 사용자 경험에 아주 많은 영향을 끼치게 됩니다.
저는 레이아웃 시프트를 줄이기 위해 데이터가 로드되기 전에 스켈레톤 컴포넌트를 사용하여 레이아웃 시프트를 최소화하였습니다. 스켈레톤 컴포넌트를 사용해도 되고 로딩 스피너를 사용해도 됩니다. 하지만 UX 경험상 미리 어떤 구조로 데이터가 로딩될지 사용자에게 보여주는 것이 UX에 더 좋다고 판단하여 스켈레톤을 사용하였습니다.
(예시 화면은 Network 상태를 slow 3g 로 적용하고 실험하였습니다)
'FrontEnd' 카테고리의 다른 글
FrontEnd - React-query 를 이용한 무한스크롤 구현하기 - (2) (0) | 2023.07.06 |
---|---|
FrontEnd - React-query 를 이용한 무한스크롤 구현하기 - (1) (0) | 2023.07.06 |
Frontent - SSR(ServerSideRendering) vs CSR(ClientSideRendering) (0) | 2023.06.29 |
AWS Lambda 란 무엇인가? (0) | 2023.06.29 |
번들링과 웹팩 (0) | 2023.03.14 |