React는 state가 변경되면 컴포넌트 리렌더링이 발생하고 리렌더링의 결과를 모아 Virtual DOM을 만든다. 생성된 Virtual DOM을 이전의 Virtual DOM과 비교하여 변경된 부분만을 실제 DOM에 적용시키는데 이 때 브라우저에 의해 수행된다. 그럼 여기서 컴포넌트를 반복적으로 렌더링 시키면 브라우저가 렌더링을 자주 수행하게 되고 성능이 저하되게 될 것이다. 그렇기 때문에 state가 이전 값과 다를 경우에만 렌더링이 발생되게 하는 렌더링 성능 최적화가 필요하다.
렌더링 성능 최적화 방법을 알기 전에 state가 어떤식으로 갱신되는 지를 알 필요가 있다. state 반영하는 방법은 아래와 같다.
const [count, setCount] = useState(0);
const add = () => setCount(prev => prev + 1);
Scheduled State Change
setState가 호출되고 상태 업데이트 함수를 호출하면 데이터로 상태가 업데이트 하게끔 예약하는 것을 말한다.
const [count, setCount] = useState(0);
const [isTrue, setIsTrue] = useState(false);
const [total, setTotal] = useState(0);
const add = () => {
// 동시에 업데이트를 한다.
setCount(prev => prev + 1);
setIsTrue(prev => !prev);
setTotal(prev => prev + count);
}
const App = (props) => {/* 렌더링 */};
export default React.memo(App); // 얕은 비교 수행
const App = (props) => {/* 렌더링 */};
function areEqual(prevProps, nextProps){
/* nextProps가 이전값과 같다면 true 반환 아니면 false */
}
export default React.memo(App, areEqual); // 얕은 비교 수행
🧐 모든 컴포넌트에 사용하지 않는걸까?
컴포넌트를 재평가하는 데 필요한 성능 비용과 PROPS를 비교하는 성능 비용을 맞바꾸는것과 다름이 없기 때문에 모든 컴포넌트에 사용하지 않고 필요한 부분에만 사용하는 것이 알맞다.
📍아래 예시를 살펴보자
1. setPage되는 버튼을 클릭
2. 의존성 배열에 따라 useMemo의 console이 출력
3. setCount되는 버튼을 클릭
4. page의 값이 변하지 않았기 때문에 console이 출력되지 않음.
이에 따라 의존성 배열에 해당되는 값이 변할때 함수를 실행한다.
const App = () => {
const [page, setPage] = useState(0);
const [count, setCount] = useState(0);
useMemo(() => {
console.log(page);
},[page])
return (
<div>
<button onClick={() => setPage(prePage => prePage + 1)}>add page</button>
<button onClick={() => setCount(preCount => preCount + 1)}>add count</button>
</div>
)
}
export default App
📍왔썹 프로젝트에서 사용한 코드로 살펴보자.
const 꿀조합_컬렉션_정렬해서_가져오기 = useCallback(async () => {
const 랭킹리스트_정보 = await getRankingList(key.current, 정렬_조건, 10);
if (랭킹리스트_정보) {
key.current = 랭킹리스트_정보.마지막_키;
setTimeout(() => {
랭킹리스트_수정(prev => [...prev, ...랭킹리스트_정보.랭킹리스트]);
}, 100);
}
}, [현재탭]);
최적화는 컴포넌트를 재평가하는 데 필요한 성능 비용을 props를 비교하는 성능 비용을 맞바꾼 것과 다름없다고 한다라는 이 말이 가장 기억에 남는다. 이 말로 또다시 개발하면서 가장 적절한 곳에 적절한 방법을 적용해야하는 것이 최고의 개발 방법이라는 것을 깨닫게 되었다.
참고자료
https://leehwarang.github.io/2020/05/02/useMemo&useCallback.html
https://ui.toast.com/weekly-pick/ko_20190731
https://yceffort.kr/2022/04/best-practice-useCallback-useMemo