프로젝트 진행 중 적용해 본 최적화에 대해 기술해보려합니다.
우선 렌더링(rendering)이란 무엇인지부터 확인하고 가야할 것 같습니다.
렌더링이란 컴포넌트가 현재 상태값을 바탕으로 UI를 구성하는 것을 말합니다. 예를 들어
<Link href={url}>
<a className={isSelected ? `${className} Selected` : className}>{text}</a>
</Link>
위 코드에서 컴포넌트는 상태 href=url
, className="000"
text=test
로 화면상에 UI를 꾸미는 것입니다.
그렇다면 리액트에서 렌더링은 언제 일어날까요 ?
Props가 변경되었을 때
State가 변경되었을 때
useEffect
부모 컴포넌트가 렌더링되었을 때
위와 같은 상황에서 렌더링이 발생하는 것입니다.
리렌더링이 무조건적으로 나쁜 것은 아닙니다. 상태값이 바뀌어야 할 때 적정하게 리렌더링이 이루어져야만 컴포넌트는 변할 것입니다. 하지만 필요 이상의 리렌더링이 발생한다면 웹 애플리케이션의 성능은 저하될 것입니다.
그럼 어떻게 리렌더링을 잡았는 지 적어보도록 하겠습니다.
위 영상을 보면 링크(메뉴바 클릭)가 변할 때 마다 모든 메뉴바가 다시 렌더링되는 것을 확인하실 수 있습니다.
즉, 트렌드에서 공간 페이지로 이동 시 모든 메뉴들이 새롭게 구성되는 것입니다.
이는 불필요한 리렌더링입니다.
저는 링크 이동이 발생할 때마다 클릭 한 링크와 이전의 링크의 색상 변화(className 변화)만 바뀌기에 이 두 컴포넌트만 리렌더링이 이루어져야하고 나머지는 그대로여야합니다. 이를 위해 사용하는 것이 React.memo입니다.
React.memo
컴포넌트가 동일한 props로 동일한 결과를 렌더링해낸다면, React.memo를 호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다
import Link from "next/link";
import React, { ReactNode } from "react";
interface ICustomLinkProps {
url: string;
isSelected: boolean;
className?: string;
text: ReactNode | string;
}
const CustomLink = ({ url, text, className, isSelected }: ICustomLinkProps) => {
console.log("custom render", url, className);
return (
<Link href={url}>
<a className={isSelected ? `${className} Selected` : className}>{text}</a>
</Link>
);
};
export default React.memo(CustomLink);
마지막 줄 React.memo()
를 통해 props가 변경되지 않으면 기존의 컴포넌트를 렌더링합니다.
그럼 처음에만 모든 컴포넌트가 렌더링되고 이후에는 변해야할 컴포넌트만 렌더링이 되는 것을 확인하실 수 있습니다.
하지만 영상 마지막 부분에 이상한 점이 있습니다. 트렌드와 검색 버튼은 두번씩 렌더링이 되고 있습니다. 이는 모바일 컴포넌트를 따로 두고 있기 때문입니다.
데스크탑 환경에서는 mobile 메뉴창이 렌더링되면 안됩니다. 이럴 땐 dynamic import를 사용하여 막습니다.
const DynamicMobileNavigation = dynamic(() => import("./MobileNavigation"));
const DynamicNavigation = dynamic(() => import("./Navigation"));
const [isMobile, setIsMobile] = React.useState(false);
const handlerResize = () => {
setIsMobile(() => {
return window.innerWidth <= sizes.mobile;
});
};
React.useEffect(() => {
handlerResize();
window.addEventListener("resize", handlerResize);
return () => {
window.removeEventListener("resize", handlerResize);
};
}, []);
이런식으로 window size를 통해 해당컴포넌트를 특정 사이즈에만 가지고 오는 방법이다.
이렇게 화면이 작아질 때 컴포넌트를 서버에서 가지고 오는 것을 확인할 수 있다.
물론 이 방법들이 정답은 아닙니다. 극단적인 예를 위해서 사용하는 방법들일 뿐입니다. 각자의 웹 페이지 상황에 맞춰 적절한 곳에 최적화를 진행해보면될 듯합니다. 다음에는 폰트를 최적화를 프로젝트에 적용하도록 해보겠습니다.