앞선 글에서 React 렌더링 최적화 방법인 Windowing, shouldComponentUpdate(), React.PureComponent, Lazy Loading에 대해 정리했다. 이어서 React 렌더링 최적화에 대해 공부하며 정리한다.
useMemo 는 React Hook중 하나로 간편하게 메모이제이션(memoization)을 수행할 수 있게 해주는 함수이다. 메모이제이션(memoization)은 중복 연산을 피할 수 있게 해주기 때문에 메모리를 조금 더 쓰더라도 어플리케이션의 성능을 최적화할 수 있다.
React에서는 상태 변경이 있을 때마다 리랜더링을 하게 되는데, 어떠한 함수(함수 a라고 정의)에서 상태를 변경하기까지 오래 걸리는 상황이 있을 수 있다 (연산이 오래 걸리거나, setTimeout 등). 하지만 해당 상황에서 리렌더링 후의 값이 기존의 값과 같게 되면 굳이 리렌더링 시에 a 함수의 오래 걸리는 상황을 반복할 이유가 없다. 이 기능을 위한 함수가 React Hook에서는 useMemo 이다.
useMemo는 리렌더링 후의 값과 기존 값이 동일한 경우 다시 값을 구하는 것이 아닌, 메모리 어딘가에 저장해두었던 기존 값을 불러와 사용하게 된다.
useMemo 함수는 2개의 인자를 받는데, 첫번째는 결과값을 생성해주는 함수이고, 두번째는 기존 결과값 재활용 여부의 기준이되는 입력값 배열이다.
const result = useMemo(() => calc(x, y), [x, y]);
useCallback 은 useMemo와 같이 메모이제이션(memoization)을 위한 Hook 함수이다. useMemo는 특정 '값'을 재사용 할 때 사용한다면, useCallback은 특정 '함수'를 재사용하고 싶을때 사용한다. 컴포넌트들은 리렌더링시에 선언된 함수들은 새로 만들어지게 된다. 따라서, 이 useCallback 함수는 메모이제이션을 통해 특정 함수가 의존하는 값이 변하지 않으면 재사용하여 성능 최적화에 도움을 준다.
useCallback 함수는 첫번째 인자로 넘어온 함수를, 두번째 인자로 넘어온 배열 내의 값이 변경될 때까지 저장해놓고 재사용할 수 있게 해준다.
const result = useCallback(함수, 배열);
예시
const sum = () => a + b;
▽▽
const sum = useCallback(() => a + b, [a, b]);
a 또는 b 값이 바뀌면 새로운 함수를 생성하여 add에 할당되고, a와 b의 값이 이전과 동일하면 다음 랜더링 때 이 함수를 재사용한다.
useCallback() 은 함수도 객체로 취급이 되기 때문에 비교 시에 메모리 주소에 의한 참조 비교가 일어나는 자바스크립트 특성 때문에 React 컴포넌트 함수 내에서 어떤 함수를 다른 함수의 인자로 넘기거나 자식 컴포넌트의 prop으로 넘길 때 예상치 못한 성능 문제로 이어질 수 있어 사용한다고 한다.
React.memo 는 위에 설명한 useMemo와 역할이 같다. 하지만 React.memo는 HOC이라 클래스형과 함수형 컴포넌트에서 모두 사용 가능하고, useMemo는 Hook 이기 때문에 함수형 컴포넌트에서만 사용이 가능한 차이가 있다. React.memo는 자식 컴포넌트에 props로 함수를 넘기게 될 때, useCallback과 같이 쓰는 경우가 많은데 부모 컴포넌트에서 정의한 useCallback은 자식 컴포넌트에서 사용할때 아무 효력도 없기 때문이다. useCallback 만으로는 자식 컴포넌트의 리렌더링을 막을 수 없어 useCallback에 대한 로직처리가 없다면 다시 부모의 렌더링에 영향을 받아 리렌더링이 된다.
자식 컴포넌트는 해당 이슈를 간단하게 해결할 수 있다.
export default React.memo(component)
함수형 컴포넌트 예시
function TmpFunc(props) {
return (
<div>
{props.data}
</div>
)
}
const MemoTest = React.memo(TmpFunc)