메모이제이션
, 개념만 보았을 때는 굉장히 효율적이고 사용하기만 하면 최적화가 이루어질 것 같은 느낌이 들기도 한다. 하지만 명확한 목적없이 무작정 메모이제이션을 사용하는 것은 오히려 비효율적이다.
그런데 메모이제이션을 하기 전 새로운 값을 만드는 것과 어딘가에 이전의 값을 저장해두고 메모이제이션 함수를 호출하고 의존성을 비교해서 가져올지 말지 여부를 판단하는 것 중 어떤 것이 비용이 더 적게 들지 생각해봐야 한다.
만약 새로운 값을 만드는 과정이 복잡하고 무거운 연산이라면 메모이제이션을 사용하는 것이 더 효율적일 수 있다. 하지만 새로운 값을 만드는 과정이 복잡하지 않다면 메모이제이션을 사용하는 것은 오히려 비용이 더 많이 들수도 있다. 컴퓨터 자원의 측면뿐만 아니라 메모이제이션을 쓰면서 코드의 복잡도가 올라간다는 개발적인 측면의 비용도 무시할 수 없기 때문이다.
이처럼 메모이제이션은 무조건 사용하는것이 좋은게 아니라, 필요성을 분석하고 필요하다고 판단되는 순간에만 사용해야 한다. 리액트에서 메모이제이션이 필요하다고 판단할 수 있는 요인은 아래 두가지다.
1. 새로운 값을 만드는 연산이 복잡하다.
2. 함수 컴포넌트의 이전 호출과, 다음 호출 간 사용하는 값의 동일성을 보장하고 싶다.
1번의 경우에는 만약 10000개의 요소를 가진 배열이 있다고 생각하면 이 배열을 매번 생성하는 것 보다는 메모해서 활용하는 것이 효율적일 것 이다. 2번의 경우에는 함수 컴포넌트의 호출 간 값들의 동일성을 보장하기 위해서다. 동일성을 보장해야 하는 이유는 React.memo
와 연동해서 사용하기 위해서다.
앞서 memo의 잘못된 활용 예시에서 props로 전달되는 hello 함수(객체)의 동일성이 보장되지 않아 실제 함수(객체)의 값은 똑같아도 shallow compare를 통해서 다른 함수(객체)라고 판단되어서 매번 리렌더링이 실행되는 상황을 확인했다. 이런 상황에서 전달되는 객체의 동일성을 보장하기 위해서 메모이제이션을 활용할 수 있다.
메모이제이션 된 객체는 새롭게 만들어진 것이 아니라 이전의 객체를 그대로 활용하는 것이기에 shallow compare에서 동일함을 보장받을 수 있다.
import React, { useCallback, useState } from "react";
import MemoizedChild from "../src/Child";
export default function App() {
const [text, setText] = useState("");
const [_, setState] = useState(1);
const reRender = () => setState((prev) => prev + 1);
// 기존 함수
// const hello = () => {
// alert("Hello, World");
// };
// 기존 함수를 useCallback의 첫 번째 인자로 전달해주고 있다.
const memoizedHello = useCallback(() => {
alert("Hello, World");
}, []);
return (
<div className="App">
<h1>Memoization Test</h1>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button
style={{ display: "block", margin: "20px auto" }}
onClick={reRender}
>
re render
</button>
<MemoizedChild name="memo O" value={text} hello={memoizedHello} />
</div>
);
}