React 렌더링에 대한 최적화시키기 위한 도구인 React.memo, useMemo, useCallback 에 대해 정리 해보았다.
최적화, 메모이제이션
업데이트, 변경과 같은 상황이 화면에서 이루어지면 컴포넌트를 다시 센터링하고 레이아웃 및 페인팅 과정을 다시 계산하는 상황이 발생하여 React의 성능을 점검할 때는 컴포넌트 자체의 리랜더링이 불필요하게 반복되고 있지 않은지, 그리고 내부 로직이 쓸데없이 다시 만들어지거나 복잡한 계산을 반복하고 있지는 않은지 고려해야 한다.
Memoization(메모이제이션)은 Caching의 일종으로 이미 계산해 본 연산 결과를 기억해두었다가 동일한 연산을 해야 할 때 다시 연산하지 않고 기억해두었던 데이터를 반환시키는 방법으로 쓸데없이 같은 계산을 반복하게 하지 않게 할 수 있다.
React.memo
React.memo는 Higher-Order Components(HOC - 고차 컴포넌트)이다.
(HOC란 컴포넌트를 인자로 받아서 새로운 컴포넌트를 return해주는 구조의 함수)
- React.memo는 오직 props가 변경됐는지 아닌지 만 체크한다
- props가 객체(object)일 경우 얕은 비교(shallow compare)만 한다. ( object의 경우 값을 'reference(참조)'하고 있는 지를 비교 ⇒ 비교를 원하면 React.memo의 두 번째 인자로 비교할 수 있는 함수를 넘겨주면 됨 )
얕은 비교 ?
값에 의한 비교가 아닌 주소에 의한 비교
자바스크립트에서 객체, 함수, 배열 같은 비 원시 타입의 자료형을 비교할 때 값에 의한 비교가 아닌 주소에 의한 비교를 한다
let a = { count: 1 };
let b = { count: 1 };
if (a === b) {
console.log("same");
} else {
console.log("not same");
}
console.log(a === b); // 결과: not same
⇒ 얕은 비교를 할 때 이전의 object값과 재 할당된 object값이 서로 다른 값으로 인식되어 props값이 변경이 안 됬어도 React.memo를 적용한 컴포넌트에 리 렌더링이 발생한다.
사용법
const MyComponent = React.memo((props) => {
return (/*컴포넌트 렌더링 코드*/)}
);
//or
function MyComponent (props) {
return (/*컴포넌트 렌더링 코드*/)}
}
export default React.memo(MyComponent);
useMemo
useMemo는 Memoization된 값을 return하는 hook이다. ( ****랜더링 과정 중 에 발동된다 / useEffect 는 랜더링이 끝나고 나서 발동됨 )
인자로 함수와 의존 값(dependencies)을 받아 의존 인자 중 하나라도 변경되면 값을 재 계산한다. (인가자 아무것도 없으면 렌더 시 마다 항상 값을 새로 계산하여 return함)
- 함수형 컴포넌트 내에서만 사용할 수 있다.
- 의존 인자인 a, b 값이 변할 때만 useMemo의 첫 번째 인자인 함수가 실행되어 재 계산되고, 그렇지 않은 경우에는 Memoization된 값을 리턴 한다.
- 모든 함수를 useMemo로 감싸게 되면 이 또한 리소스 낭비가 될 수 있으므로,퍼포먼스 최적화가 필요한 연상량이 많은 곳에 사용하는 것이 좋다.
사용법
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
//사용예시
const geAnalysis = () => {
const pickItem = data.filter((elem) => elem.pick = true).length;
const totalItem = data.length - pickItem;
return [pickItem, totalItem];
}
const [pickItem, totalItem] = useMemo(geAnalysis(), [data.length]);
return (
<div>
<p>pick 수 : {pickItem}개</p>
<p>nonePick 수 : {noneItem}개</p>
</div>
)
useCallback
useCallback은 리액트의 렌더링 성능을 위해서 제공되는 Hook이다.
컴포넌트가 렌더링 될 때마다 내부적으로 사용된 함수가 새롭게 생성되는 경우, 자식 컴포넌트에 Prop으로 새로 생성된 함수가 넘겨지게 되면 불필요한 리 렌더링이 일어날 수 있다. useCallback을 사용하면 함수가 처음 생성될 때 한 번만 생성되며, 나중에는 동일한 함수 인스턴스를 재 사용하게 된다.
사용 전 코드를 보면 ItemEdit 가 렌더링이 발생할 때마다 컴포넌트의 onSave 속성값으로 새로운 함수가 전달된다. ItemEdit 컴포넌트에서 React.memo를 사용해도 전달된 Prop이 항상 바뀌므로 불필요한 렌더링이 발생한다.
사용 후 코드에서 useCallback을 활용해 1번째 인자로 함수, 2번째 인자로 Dependencies를 전달해 전달된 의존성 인자가 바뀌지 않으면 이전에 생성한 함수가 재사용 된다.
자식컴포넌트에 함수를 props으로 줄때는 반드시 useCallback을 사용하여면 리렌더링을 방지 할 수있다.
//사용 전
import React, {useSatate} from 'react';
import {saveToServer} from './api';
import ItemEdit from './ItemEdit';
function ItemEdit(){
const [itemName, setItemName] = useState('');
const [itemPrice, setItemPrice] = useState(0);
return (
<div>
<p>{itemName}</p>
<p>{`price : ${itemPrice}`}</p>
<ItemEdit
onSave={() => saveToServer(itemName, itemPrice)}
setItemName={setItemName}
setItemPrice={setItemPrice}
/>
</div>
);
}
//사용 후
function ItemEdit(){
const [itemName, setItemName] = useState('');
const [itemPrice, setItemPrice] = useState(0);
const onSave = useCallback(() => saveToServer(itemName, itemPrice), [itemName, itemPrice]);
return (
<div>
<p>{itemName}</p>
<p>{`price : ${itemPrice}`}</p>
<ItemEdit onSave={onSave} setItemName={setItemName} setItemPrice={setItemPrice} />
</div>
);
}
React.memo, useMemo, useCallback
공통점
- 불필요한 렌더링 또는 연산을 제어하는 용도로 성능 최적화 한다.
차이점
- React.memo는 HOC이고, useMemo와 useCallback은 hook이다.
- React.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만, useMemo는 hook이기 때문에 함수형 컴포넌트 안에서만 사용 가능하다.
- useMemo는 함수의 연산량이 많을때 이전 결과값을 재사용하는 목적이고, useCallback은 함수가 재생성 되는것을 방지하기 위한 목적이다.
'React' 카테고리의 다른 글
달력 (히트맵) 만들기 (2) (0) | 2024.09.12 |
---|---|
달력 (히트맵) 만들기 (1) (0) | 2024.09.12 |
개별 토글 리스트 (0) | 2024.09.11 |
useform 추가 데이터 (0) | 2024.09.06 |
react-cookie (1) | 2024.09.04 |