React를 사용하다보면 렌더링문제에 직면하게 된다.
예를들어 특정 부모컴포넌트가 리렌더링이 될 때 자식컴포넌트는 리렌더링이 되지 않게 한다던지, 컴포넌트 자신 내부에서 복잡한 로직이 있는 함수만 리렌더링시 실행 제외를 시켜준다던지 해야 될 필요가 있다. 그래야 렌더링 최적화를 이룰 수 있어 기능면에서 향상되기 때문이다.
1. memo
부모 컴포넌트가 리렌더링될 때 자식 컴포넌트의 prop이 변하지 않으면, 리렌더링을 방지하는 컴포넌트. 성능 최적화를 위해 사용.
import React, { memo } from 'react'; const ChildComponent = memo(() => { console.log("ChildComponent rendered"); return <div>I'm the child component</div>; }); const ParentComponent = () => { const [count, setCount] = React.useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>Increase Count</button> <p>Parent count: {count}</p> <ChildComponent /> </div> ); }; export default ParentComponent;
이 코드는
memo
를 사용해 ChildComponent
가 prop의 변경 없이 부모 컴포넌트가 리렌더링될 때 불필요한 렌더링이 발생하지 않도록 한다. 따라서 ChildComponent rendered
메시지는 더 이상 콘솔에 출력되지 않는다.2. useMemo
성능이 중요한 복잡한 계산의 결과를 메모이제이션하여 리렌더링 시 재실행을 방지하는 Hook.
import React, { useMemo } from 'react'; const slowFunction = (num) => { console.log("Running slow function"); for (let i = 0; i < 1000000000; i++) {} // 오래 걸리는 작업 return num * 2; }; const MyComponent = () => { const [count, setCount] = React.useState(0); const [input, setInput] = React.useState(''); const result = useMemo(() => slowFunction(count), [count]); return ( <div> <input value={input} onChange={(e) => setInput(e.target.value)} /> <button onClick={() => setCount(count + 1)}>Increase Count</button> <p>Result: {result}</p> </div> ); }; export default MyComponent;
useMemo
를 사용하여, count
가 변경될 때만 slowFunction
이 실행되고, input
이 변경될 때는 더 이상 불필요한 계산이 발생하지 않는다.3. 결론
그러면 전부 memo, useMemo를 사용하면 좋은것이 아닌가?라는 생각을 할 수도 있다. 하지만 딱히 그렇지만도 않다. 그 이유는 다음과 같다
1) 메모이제이션의 비용

memo
와 useMemo
는 각각 메모이제이션을 통해 렌더링을 최적화하지만, 메모이제이션 자체도 비용이 든다. 상태를 저장하고 이전 값을 비교하는 과정이 발생하기 때문에, 아주 단순한 컴포넌트나 연산에는 오히려 성능에 부정적인 영향을 줄 수 있다.예를 들어, 단순한 UI 업데이트에서
memo
나 useMemo
를 사용하는 것은 불필요한 비교 비용을 추가할 뿐만 아니라, 코드도 복잡하게 만든다.2) 복잡하지 않은 연산은 굳이 메모이제이션할 필요가 없다.

useMemo
는 복잡한 연산이나 많은 리소스를 사용하는 계산에만 적용하는 것이 적절하다. 간단한 산술 연산이나 작은 배열 필터링, 맵핑 등의 경우에는 메모이제이션으로 얻는 성능 이득이 거의 없다. 오히려 useMemo
로 인해 코드가 불필요하게 복잡해질 수 있다.3) 컴포넌트 상태나 prop의 변화가 빈번하면 오히려 역효과

만약 컴포넌트의 prop이나 상태가 자주 변경되는 경우,
memo
나 useMemo
의 효과는 거의 없어지거나, 메모이제이션을 위한 비교 작업만 추가되므로 오히려 성능이 저하될 수 있다.따라서 이런 최적화 방법론을 적재적소에 사용할 수 있도록 하여 프로그램을 제작하는 역량이 프론트엔드 개발자가 가져야하는 부분인 것 같다.