문제점
성능을 측정해보기 위해 lighthouse를 사용하던 중 점수대가 심상치 않다는 것을 알게되었다.
무려 79점...이었다.
문제파악하기
우리 AlgoITNi는 Home과 Room 두가지 페이지로 구성된다.
대부분의 기능은 Room에 필요하고, Room에서만 사용한다.
그러나, 리액트는 SPA이기에 이런 모듈들을 Home을 렌더링할때도 가져오게되고, 따라서 Home의 초기렌더링 성능이 나빠지는 것이라 추측했다.
확인해보기
프로덕션에서는 빌드가 완료된 청크가 네트워크 응답으로 오기때문에 정확하게 분석하기 어렵다.
따라서 개발환경에서 Home에 접속했을때 개발자도구의 네트워크 탭을 통해 어떤 코드를 불러오는지 확인했다.
빨간색 부분이 Room에서 필요한 코드들이다.
즉, Home에 접근했을때는 필요없는 코드들인 것이다.
이 부분을 제거해주면, 빌드시 만들어진 청크 크기도 작아지고, 이에따라 자연스럽게 초기 렌더링도 개선될 것이다.
1.8MB의 크기를 불러오고, 화면에 그리는데에 644ms만큼 시간이 필요한 것을 확인할 수 있다.
어떻게 개선할까?
React.lazy와 Suspense를 사용해서 Room 컴포넌트를 빌드파일에 포함시키지않는 형태로 개선했다.
React.lazy를 사용하면 해당 컴포넌트를, 필요할때 동적으로 불러오는, 동적 임포트가 가능해진다.
https://ko.legacy.reactjs.org/docs/code-splitting.html
React.lazy를 사용해서 동적 임포트한 컴포넌트는 불러오기 전까지, 보이지 않는다는 문제가 있다.
이런 문제를 해결하기 위해 Suspense 컴포넌트로 감싸, 그 사이에 보여줄 fallback 컴포넌트를 설정한다.
이러면 컴포넌트를 동적으로 불러오는 동안, 로딩 컴포넌트를 보여줄 수 있게 된다.
const RoomPage = lazy(() => import('./pages/Room'));
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
},
{
path: '/:roomId',
element: (
<Suspense fallback={<RouterSpinner />}>
<RoomPage />
</Suspense>
),
},
]);
결과
아래처럼 Room과 관련한 코드 대부분이 제거되었다!
또한, 리소스 용량이 1.7MB로 줄어들었고, 로드하는 시간도 219ms로 감소했음을 확인할 수 있다!
비교
스플리팅 전 | 스플리팅 후 | |
용량 | 1.8MB | 1.7MB |
로드 시간 | 644ms | 219ms |
lightHouse | 79 | 86 |
응답시간이 644ms -> 219ms로, 약 65%빨라졌다는 것을 확인할 수 있었다.