React에서 성능 최적화를 위한 useMemo, useCallback, React.memo 이해하기
React에서 앱을 만들다 보면 성능이 중요한 문제가 될 때가 많습니다. 특히 많은 컴포넌트가 렌더링되거나 상태가 자주 변하는 경우, 불필요한 리렌더링을 방지하는 것이 성능을 향상시키는 데 큰 도움이 될 수 있습니다. 이를 위해 React는 useMemo, useCallback, React.memo라는 기능을 제공합니다. 이 세 가지는 모두 렌더링 성능을 최적화하는 데 사용되며, 각기 다른 상황에서 유용하게 사용됩니다.
useMemo
useMemo는 값을 메모이제이션(기억해두기)하는 훅입니다. 주로 계산 비용이 많이 드는 값을 다시 계산하지 않도록 할 때 사용합니다. 컴포넌트가 렌더링될 때마다 모든 코드가 실행되면 성능에 부담이 될 수 있으므로, 필요할 때만 값을 재계산하도록 도와줍니다.
사용 예시:
import React, { useMemo, useState } from 'react';
const ExpensiveComponent = ({ num }) => {
const calculateFactorial = (n) => {
console.log('Calculating factorial...');
return n === 0 ? 1 : n * calculateFactorial(n - 1);
};
// useMemo를 사용하여 불필요한 재계산을 방지
const factorial = useMemo(() => calculateFactorial(num), [num]);
return <div>Factorial of {num} is {factorial}</div>;
};
export default ExpensiveComponent;
위 예시에서 useMemo는 num이 변경될 때만 calculateFactorial 함수를 호출하고, 그렇지 않으면 이전에 계산한 값을 그대로 사용합니다. 이렇게 하면 num이 자주 바뀌지 않는다면 불필요한 계산을 피할 수 있습니다.
useCallback
useCallback은 함수 자체를 메모이제이션하는 훅입니다. 함수는 컴포넌트가 렌더링될 때마다 새로 생성되기 때문에, 불필요한 함수 재생성을 막기 위해 사용합니다. 주로 자식 컴포넌트에 함수를 전달할 때 유용합니다.
사용 예시:
import React, { useState, useCallback } from 'react';
const Button = React.memo(({ onClick, label }) => {
console.log('Button rendered');
return <button onClick={onClick}>{label}</button>;
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
// useCallback을 사용하여 불필요한 함수 재생성을 방지
const increment = useCallback(() => setCount(count + 1), [count]);
return (
<div>
<h1>{count}</h1>
<Button onClick={increment} label="Increment" />
</div>
);
};
export default ParentComponent;
useCallback을 사용하면 increment 함수는 count가 변경될 때만 새로 생성되고, 그렇지 않으면 이전 함수를 재사용합니다. Button 컴포넌트는 increment 함수가 바뀌지 않으면 다시 렌더링되지 않으므로 성능을 최적화할 수 있습니다.
React.memo
React.memo는 컴포넌트를 메모이제이션하는 고차 컴포넌트입니다. 자식 컴포넌트가 props가 변경되지 않으면 리렌더링되지 않도록 하여 성능을 최적화합니다. 주로 함수형 컴포넌트에서 사용되며, shouldComponentUpdate를 직접 구현하지 않고도 성능을 최적화할 수 있습니다.
사용 예시:
import React, { useState } from 'react';
// React.memo를 사용하여 리렌더링 최적화
const ChildComponent = React.memo(({ name }) => {
console.log('ChildComponent rendered');
return <div>Hello, {name}!</div>;
});
const ParentComponent = () => {
const [name, setName] = useState('Alice');
const [count, setCount] = useState(0);
return (
<div>
<ChildComponent name={name} />
<button onClick={() => setCount(count + 1)}>Increase Count</button>
</div>
);
};
export default ParentComponent;
위 예시에서 ChildComponent는 name이 바뀌지 않으면 렌더링되지 않습니다. count가 변경되더라도 ChildComponent는 name이 변경되지 않았기 때문에 재렌더링되지 않습니다. React.memo는 이렇게 컴포넌트를 최적화하는 데 도움을 줍니다.
정리
- useMemo: 값의 계산을 메모이제이션하여, 값이 변경될 때만 재계산합니다. 주로 비싼 계산을 최적화할 때 사용합니다.
- useCallback: 함수를 메모이제이션하여, 함수가 의존성 배열의 값이 변경될 때만 새로 생성합니다. 주로 자식 컴포넌트에 함수를 전달할 때 사용합니다.
- React.memo: 컴포넌트를 메모이제이션하여, props가 변경되지 않으면 컴포넌트가 리렌더링되지 않도록 최적화합니다.
이 세 가지를 적절히 사용하면 React 앱에서 성능을 향상시킬 수 있습니다. 각각의 상황에 맞게 사용하여, 필요 없는 렌더링을 줄이고 앱을 더 빠르게 만들 수 있습니다!