프론트엔드 개발에서 성능은 항상 중요한 이슈입니다. 특히 대규모 애플리케이션에서는 컴포넌트가 많아지고 상태 관리가 복잡해짐에 따라 성능 저하가 발생할 수 있습니다. React에서는 이러한 문제를 해결하기 위한 여러 가지 메모이제이션 기법을 제공합니다. 이번 포스트에서는 React의 메모이제이션 기법을 비교하고, 각각의 활용 방안에 대해 살펴보겠습니다.
메모이제이션이란?
메모이제이션(Memoization)은 함수를 호출할 때 입력값과 결과값을 기억해 두었다가 같은 입력값이 들어오면 이전에 계산한 결과를 재사용하는 기법입니다. React에서는 성능 최적화를 위해 다음과 같은 메모이제이션 기법을 사용할 수 있습니다:
- React.memo
- useMemo
- useCallback
- React.memo: 주로 자식 컴포넌트에 사용하여 불필요한 리렌더링을 방지합니다. props가 자주 변경되지 않는 경우에 유용합니다.
- useMemo: 비용이 높은 계산을 메모이제이션하여 최적화. 주로 복잡한 계산 결과를 재사용할 때 사용합니다.
- useCallback: 함수가 자주 변경되지 않도록 메모이제이션. 자식 컴포넌트에 함수를 전달할 때 유용합니다.
이제 각 기법의 특징과 사용 예제를 살펴보겠습니다.
React.memo
React.memo는 고차 컴포넌트로, props가 변경되지 않는 한 컴포넌트를 리렌더링하지 않도록 최적화합니다. 주로 부모 컴포넌트가 리렌더링될 때, 자식 컴포넌트가 필요 없이 다시 렌더링되는 것을 방지하고 싶을 때 유용합니다.
import React from 'react';
const ChildComponent = React.memo(({ value }) => {
console.log('ChildComponent rendered');
return <div>{value}</div>;
});
const ParentComponent = () => {
const [count, setCount] = React.useState(0);
const value = 'Hello, World!';
return (
<div>
<ChildComponent value={value} />
<button onClick={() => setCount(count + 1)}>Increase Count</button>
<p>{count}</p>
</div>
);
};
위의 예제에서, ChildComponent는 부모 컴포넌트가 리렌더링되어도 value가 변경되지 않는 한 다시 렌더링되지 않습니다. 이를 통해 불필요한 렌더링을 방지할 수 있습니다.
useMemo
useMemo는 계산 비용이 높은 값을 메모이제이션하여 성능을 최적화하는 훅입니다. 주로 복잡한 계산 결과를 캐싱하여 재사용할 때 사용됩니다.
import React from 'react';
const ExpensiveComponent = ({ number }) => {
const computeExpensiveValue = (num) => {
console.log('Computing expensive value');
return num * 2; // 예를 들어 복잡한 계산이 있을 수 있음
};
const memoizedValue = React.useMemo(() => computeExpensiveValue(number), [number]);
return <div>Computed Value: {memoizedValue}</div>;
};
const ParentComponent = () => {
const [number, setNumber] = React.useState(1);
return (
<div>
<ExpensiveComponent number={number} />
<button onClick={() => setNumber(number + 1)}>Increase Number</button>
</div>
);
};
이 예제에서 computeExpensiveValue 함수는 number가 변경될 때만 계산됩니다. 그렇지 않으면 이전에 계산된 값을 재사용합니다. 이를 통해 성능을 크게 개선할 수 있습니다.
useCallback
useCallback은 함수를 메모이제이션하여 리렌더링 시 불필요한 함수 생성을 방지합니다. 주로 자식 컴포넌트에 함수를 props로 전달할 때 유용합니다.
import React from 'react';
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent rendered');
return <button onClick={onClick}>Click Me</button>;
});
const ParentComponent = () => {
const [count, setCount] = React.useState(0);
const handleClick = React.useCallback(() => {
alert('Button clicked!');
}, []);
return (
<div>
<ChildComponent onClick={handleClick} />
<button onClick={() => setCount(count + 1)}>Increase Count</button>
<p>{count}</p>
</div>
);
};
위의 코드에서는 handleClick 함수가 메모이제이션되어 자식 컴포넌트가 리렌더링될 때 불필요하게 새로운 함수를 생성하지 않도록 합니다.
기법 비교 및 활용 방안
성능 최적화를 위해서는 상황에 맞는 메모이제이션 기법을 적절히 활용해야 합니다. 예를 들어, 무거운 데이터 처리나 렌더링이 빈번한 컴포넌트에서 useMemo와 useCallback을 결합하여 사용할 수 있습니다.
마무리
React의 메모이제이션 기법은 성능 최적화에 매우 유용합니다. React.memo, useMemo, useCallback 각각의 특성과 사용 사례를 이해하고 적절히 활용하면, 불필요한 렌더링을 줄이고 사용자 경험을 향상시킬 수 있습니다. 앞으로의 개발 작업에서도 이러한 기법들을 적극적으로 활용해 보시기 바랍니다.