[React] 성능 최적화

Hyeonsik Bae·2022년 12월 20일
0

과제전형을 진행하며 '어떻게 성능을 최적화했는지'에 대한 질문을 받게 되었습니다.

이 글에선 자바스크립트의 함수형 프로그래밍을 기반으로 리액트 렌더링 최적화에 대해 정리해보고자 합니다.


리액트에서 렌더링 최적화를 하기 위해 아래 3가지에 대해 알아보겠습니다.

  1. useMemo
  2. useCallback
  3. React.memo

useMemo

useMemo는 함수의 반환값을 메모이제이션하는 hook입니다.

const memoizedValue = useMemo(callbackFunction, [dependency array]);

memoizedValue에는 callbackFunction의 반환값이 저장됩니다.

[dependency array] 내에 포함된 값이 변하지 않으면 memoizedValue는 기존에 메모이제이션한 값을 사용합니다.


아래는 예시코드입니다.

const [num, setNum] = useState(1);

const calculate = () => {
	let rtn = 0;
    for (let i = 0; i < 10000; i += 1) {
    	for (let j = 0; j < 10000; j += 1) {
        	rtn += j;
            rtn %= 500;
        }
    }
	return rtn;
};

const value = useMemo(() => calculate(), [num]);

위에서 value는 useMemo hook으로 인해 콜백함수의 반환값을 메모이제이션합니다.

여기서 콜백함수인 calculate()는 이중 반복문으로 인해 연산 속도가 빠르지 않습니다.

dependency array 에 있는 num이 변하지 않는다면 value는 기존에 메모이제이션된 값을 사용하기 때문에 효율성 면에서 유리할 수 있습니다.


useCallback

useCallback은 함수를 메모이제이션하는 hook입니다.

const memoizedCallback = useCallback(callbackFunction, [dapendency array]);

memoizedCallbackcallbackFunction을 저장합니다.

useCallback로 useMemo와 마찬가지로 dependency array 내에 포함된 값이 변하지 않으면 기존에 메모이제이션한 값을 사용합니다.

const [name, setName] = useState('hs');

const sayHello = useCallback(() => {
	console.log(`hi, ${name});
}, [name]);

위 예시에서 name이 변하지 않는다면 sayHello는 새로운 함수를 생성하지 않고 메모이제이션한 함수를 호출하게 됩니다.


React.memo

이 포스트를 작성하는 건 사실 React.memo 때문입니다.

React.memo는 컴포넌트를 컴포넌트로 반환하는 HOC입니다.

HOC에 대한 자세한 내용을 링크에 있습니다.


React.memo는 컴포넌트 자체를 메모이제이션합니다.

그러다 동일 props를 전달받아 같은 결과를 렌더링 한다면 기존에 메모이제이션 해 놓은 컴포넌트를 재사용합니다.

(React.memo로 감싸진 컴포넌트 내에서 useState, useContext등을 통해 state, context가 변하면 재렌더됩니다.)


주의해야 할 점은 React는 얕은 복사를 통해 값을 비교한다는 것입니다.

원시값이라면 값을 비교하고, 객체나 함수라면 참조할 주소를 비교하게 됩니다.

props의 비교방법을 변경하고 싶다면 React.memo의 두 번째 인자로 비교함수를 전달할 수 있습니다.


React.memo는 함수 생성 시, 혹은 반환 시에 래핑하여 사용합니다.

export const Component = React.memo(function Component(props) {
	return <div>test</div>;
});
const Component = () => {
	return <div>test</div>;
}

export default React.memo(Component);

아래 예시에서는 부모 컴포넌트에서 string, number 두 타입의 state를 가지고 있습니다.

각각의 state를 다른 자식 컴포넌트에 전달했고, state를 변경시켜도 해당되는 자식 컴포넌트만 재렌더되는 것을 확인할 수 있습니다.


정리

  1. useMemo, useCallback, React.memo는 불필요한 렌더링과 연산을 메모이제이션하여 성능 최적화를 하기 위해 사용합니다.

  2. 메모이제이션을 사용할 경우가 별로 없다면 오히려 쓰지 않는 것이 좋습니다.
    이는 오히려 캐싱, 호출, 비교 연산 등에 오히려 많은 자원이 낭비될 수 있습니다.

profile
현식 :)

0개의 댓글