대부분의 내용은 https://prateeksurana.me/blog/when-should-you-memoize-in-react/ 를 참고하였습니다.
메모이제이션(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다.
(출처 위키피디아)
리액트의 함수 컴포넌트는 re-render 될때마다 함수 전체가 재실행 된다.
즉, 함수 내부에 선언한 값(함수/변수)들도 매번 초기화/재정의가 이루어진다.
하지만, 변하지않는 값들이 매번 다시 계산되어 정의되는건 비효율적으로 느껴진다.
이러한 문제점을 해결하기 위해선 변수/함수를 메모이제이션 해야한다.
React는 메모이제이션을 위한 몇가지 API를 제공한다.
처음 useCallback
, useMemo
를 접했을땐, 무조건 사용해야겠다고 생각했다.
"모든 값들이 업데이트가 필요한 시점에만 값을 재계산, 할당하는게 성능상에도 당연히 더 좋지않을까?" 하는 판단에서였다. 그래서 남발했다.
그러나 간과한 부분이 있었다. 바로 메모이제이션을 위한 비용 이었다.
사용하다보면 마치 값을 할당하는 코드가 skip되는 것처럼 느껴질 수 있다.
그러나 실제로는 이전 데이터의 참조값을 기억할 수 있도록 해주는 메모이제이션 로직이 동작하는것이기 때문에, 이를 위한 별도의 비용이 필요하다.
또한 메모이제이션은 코드를 복잡하게 만들고, 가독성을 떨어뜨리는 부작용도 있다.
useCallback은 함수 메모이제이션을 위한 React hook이다.
React hook 이란?
Hook은 함수 컴포넌트에서React state
와생명주기 기능(lifecycle features)
을 “연동(hook into)“할 수 있게 해주는 함수
아래 예제코드를 보면, handleChange
가 메모이제이션 되고있기 때문에, re-render가 되더라도 재정의되지않고 효율적으로 동작할 것이라고 생각 수 있다.
const MyForm = () => {
const [firstName, setFirstName] = React.useState('');
const handleSubmit = event => {
/**
* Omitted for brevity
*/
};
const handleChange = React.useCallback(event => {
setFirstName(event.target.value);
}, []);
return (
<form onSubmit={handleSubmit}>
<input type="text" name="firstName" onChange={handleChange} />
<button type="submit" />
</form>
);
};
그러나 이는 잘못된 생각이다. 위 코드에서 MyForm
컴포넌트가 re-render 되었을때 handleChange
의 동작을 생각해보자.
useCallback
함수를 재호출한다.useCallback
내부로직에서는 dependency 배열을 매번 체크하고,handleChange
변수에 return 된 함수 reference 값을 할당한다.그러나 useCallback
사용하지 않는다면
1. 함수를 정의하고,
2. handleChange
변수에 할당한다.
이처럼 오히려 useCallback
을 사용하지 않는 편이 과정이 간단해보인다.
useMemo
는 변수 메모이제이션을 위한 React hook이다.
동작은 useCallback
과 비슷하며, 메모이제이션의 대상이 함수가 아닌 변수라는 차이점이 있다.
앞서 설명한 useCallback
과 비슷한 맥락에서 비효율적이라고 볼 수 있다.
React.memo
는 컴포넌트는 hook은 아니지만, useCallback, useMemo와 비슷한 방식으로 동작한다.
메모이제이션의 대상이 컴포넌트라고 생각하면된다.
컴포넌트에 전달되는 props가 변경되었을때만 re-render(재정의)를 하도록 한다.
먼저 useCallback과 useMemo의 목적을 이해하자.
useCallback
은 함수 참조값의 유지를 위해서 사용한다.
useMemo
는 값의 참조값 유지와 별개로, 복잡한 값의 계산을 피하기위해서도 사용된다.
리액트는 부모컴포넌트가 re-render되면 자식 컴포넌트또한 re-render 된다.
따라서 최상단에서부터 변하는 컴포넌트와 변하지 않는 컴포넌트를 잘 분리하는게 중요하다.
메모이제이션과 관련된 React API의 무분별한 사용은 지양하자. 오히려 성능저하를 가져올 수 있다.
props에 대해서 얕은복사만 진행하기 때문에, 부모가 re-render된다면 props의 값이 변경되지않더라도 자식컴포넌트는 모두 re-render 된다.
https://ko.reactjs.org/docs/hooks-state.html
https://ko.reactjs.org/docs/hooks-overview.html
https://prateeksurana.me/blog/when-should-you-memoize-in-react/