

react-icons의 chunk 사이즈가 너무 큰 것이 문제였다. react-icons는 종류별로 아이콘을 제공하는데, 내가 실제로 사용하는 아이콘 외 그 상위 종류(카테고리)를 다 import 해와서 build하다보니 당연히 chunk 사이즈가 커질 수밖에 없는 것이었다. (아이콘을 하나만 사용해도 이 아래처럼 그 아이콘이 속해있는 모든 카테고리를 끌고왔었음)

이 문제를 해결하기 위해 @react-icons/all-files라는 별도의 라이브러리를 설치했다. @react-icons/all-files는 아이콘별로 자바스크립트 파일을 별도로 가지고 있기 때문에, 빌드 시 트리쉐이킹 방식으로 더 적은 chunk를 만들 수 있었다.
import { FaGithub } from 'react-icons/fa';
import { SiVelog } from 'react-icons/si';
import { MdEmail } from 'react-icons/md';
import { FaGithub } from '@react-icons/all-files/fa/FaGithub';
import { SiVelog } from 'react-icons/si';
import { MdEmail } from '@react-icons/all-files/md/MdEmail';
그러나 마지막 업데이트가 3년전이다 보니, react-icons에 최신 추가된 아이콘들은 @react-icons/all-files에 들어있지 않기도 했다.
반드시 해당 아이콘을 고수해야하는 상황이 아니라면, 차선으로 그냥 @react-icons/all-files에 들어있는 아이콘으로 대체하기로 결정했다.
react-icons외에도 mui의 chunk 파일도 실제 사용해야 하는 양보다 큰 것으로 나타났다. react-icons와 마찬가지로, 내가 정말 사용하는 것만 import 해오는 방식으로 개선하기로 했다.

import {
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Paper,
Button,
} from '@mui/material';
import Table from '@mui/material/Table';
import TableContainer from '@mui/material/TableContainer';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';![]

import되는 방식만 바꿨을 뿐인데 FCP도 2.3초, LCP 7.4초나 감량되는 효과를 볼 수 있었다!
React는 기본적으로 SPA 이기때문에 웹 사이트의 전체 페이지를 하나의 페이지에 담아 동적으로 변경하며 표현하는 것이다. 즉, 하나의 파일에 모든 로직이 담기게 되는데 이에 따라 당장 사용하지 않는 코드도 읽혀지면서 최초 로딩 시간이 길어지게 된다.
이를 해결하기 위해 Code Splitting을 적용해 해결하고 성능을 향상시켰다.
기존코드는 페이지를 바로 import 해서 라우터 안에 적용시켰다.
import React, { lazy, Suspense } from 'react';
const Login = lazy(() => import('../pages/Login'));
const Join = lazy(() => import('../pages/Join'));
const KakaoLogin = lazy(() => import('../components/KakaoLogin'));
const Photos = lazy(() => import('../pages/Photos'));
const PlaylistPage = lazy(() => import('../pages/PlaylistPage'));
const ChartPage = lazy(() => import('../pages/ChartPage'));
const NotFoundPage = lazy(() => import('../pages/404'));
function Router() {
return (
<BrowserRouter>
<Suspense fallback={<Loading />}>
<ScrollToTop />
<Routes>
<Route element={<Layout />}>
<Route path='/' element={<Home />} />
<Route path='/login' element={<Login />} />
<Route path='/join' element={<Join />} />
<Route path='/auth/kakao/callback' element={<KakaoLogin />} />
<Route path='/photos' element={<Photos />} />
<Route path='/playlist' element={<PlaylistPage />} />
<Route element={<ProtectedRouter />}>
<Route path='/chart' element={<ChartPage />} />{' '}
</Route>
<Route path='*' element={<NotFoundPage />} />
</Route>
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default Router;

이 단계는 배포까지 진행할 예정이라면 굳이 스스로 할 필요 없음
압축은 압축 알고리즘을 사용하여 데이터를 수정하는 프로세스이다. Gzip은 서버 및 클라이언트 상호 작용에 가장 널리 사용되는 압축 형식이고, Brotil은 Gzip보다 훨씬 더 나은 압축 결과를 낳는 최신 압축 알고리즘이라고 한다.
여러 가지 방법을 찾아보다가 압축해주는 라이브러리compression-webpack-plugin 를 발견해 아래와 같이 webpack.config.js를 작성했다.
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
deleteOriginalAssets: 'true',
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240, // 압축을 적용할 파일 크기의 최소값 (10KB)
minRatio: 0.8, // 압축률이 80% 이상일 경우에만 압축을 적용
}),
],
};
그러나 다 작성하고 나서 나중에 배포하고 나서 알게 된 사실인데, 대부분의 호스팅 플랫폼은 기본적으로 압축을 사용하여 assets을 인코딩하기 때문에 자체적으로 할 필요는 없다고 한다...🫠
Discography 컴포넌트에서 사용하는 이미지의 width, height를 명시하지 않았던 것이 문제였다. 가져오는 이미지의 용량이 현재 화면에 보여지는 용량보다 크기 때문에 과도하게 많은 용량을 가져온다는 의미로, 필요한만큼만의 사이즈로 줄이기로 했다.
//index.tsx
<StyledImgContainer>
<img src={divide} alt='divide album' />{' '}
</StyledImgContainer>
//index.tsx
<StyledImgContainer>
<S.AlbumImg src={divide} alt='divide album' />{' '}
</StyledImgContainer>
그리고 style.ts에서 크기를 명시해줬다!
const AlbumImg = styled.img`
width: 30rem;
height: 30rem;
`;
jpg/jpeg/png만을 사용했을 때는 프로젝트 내에서 사용하는 이미지 리소스 크기가 765.5 KiB로 측정되었다. lighthouse에서 좀 더 압축률이 높은 webp으로 변환할 경우 600 정도대로 낮출 수 있다고 추천해줘서, WebP으로 이미지 형식을 모두 변환하고 다시 빌드해보았다.

