React는 웹 애플리케이션을 효과적으로 구축하는 것을 목적으로 하는 UI 라이브러리입니다.
여기서 성능 최적화는 중요한 요소인데, 이를 위해 React에서 제공하는 주요 기능 중 하나가 Hook입니다.
useCallback은 주로 메모이제이션된 콜백 함수를 반환하는 데 사용됩니다.
이전에 계산된 결과를 캐시에 저장하여 동일한 입력이 들어올 경우 다시 계산하는 것이 아닌 캐시에서 값을 반환합니다.
이로 인해 함수 컴포넌트가 효율적으로 업데이트되고, 렌더링 성능이 향상됩니다.
반면, useMemo는 콜백 함수를 직접 반환하는 것이 아니라 콜백 함수가 실행된 결과의 값을 반환하는 데 사용됩니다.
즉, useMemo는 계산량이 많은 함수를 실행하는 것을 방지하고, 꼭 필요한 경우에만 함수를 실행하여 성능을 최적화합니다.
두 개의 차이점을 한눈에 알아볼 수 있는 예시는 다음과 같습니다.
const memoizedCallback = useCallback(() => {
/* 연산 */;
}, [deps]);
const memoizedValue = useMemo(() => {
/* 연산 */;
}, [deps]);
두 Hook 모두 성능을 최적화하기 위한 목적이지만,
useCallback은 주로 이벤트 핸들러와 같은 콜백 함수를 하위 컴포넌트에 전달할 때 사용됩니다.
이를 통해 하위 컴포넌트는 메모이제이션된 콜백 함수를 참조하게 되어 무분별한 리렌더링을 방지합니다.
예를 들어, 다음과 같은 상황에서 useCallback을 사용할 수 있습니다.
jsx
import React, { useCallback, useState } from'react';
function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<IncrementButton onClick={increment} />
</div>
);
}
const IncrementButton = React.memo(({ onClick }) => {
console.log('Button re-rendered');
return <button onClick={onClick}>+</button>;
});
이 예제에서 IncrementButton 컴포넌트는 onClick 속성에 메모이제이션된 콜백 함수를 전달받습니다.
이로 인해 count가 변경되더라도, increment 함수는 동일하게 유지되어 불필요한 리렌더링을 방지하게 됩니다.
반면, useMemo는 계산된 값을 반환하는 데 사용되므로, 비용이 많이 드는 연산을 진행하는 경우를 위해 사용됩니다.
예를 들어, 다음과 같은 상황에서는 useMemo를 효과적으로 사용할 수 있습니다.
jsx
const data = useMemo(() => {
// 연산이 많이 드는 작업
return processData(inputData);
}, [inputData]);
결론적으로, useCallback과 useMemo는 성능 최적화를 위해 사용되지만, 각각 다른 상황에 적합합니다.
useCallback은 메모이제이션된 콜백 함수를 반환하여 리렌더링을 줄이는 데 사용되고,
useMemo는 계산량이 많은 경우 값을 메모이제이션하여 성능을 향상시키는 데 사용됩니다.
# 예시 코드로 아래와 같은 상황을 가정해보겠습니다.
좌측 목록에서 아이템을 클릭하면 선택된 항목이 오른쪽 리스트에 추가되는 상황입니다.
jsx
import React, { useState, useCallback, useMemo } from 'react';
const List = ({ items, onClick }) => {
return (
<ul>
{items.map((item) => (
<li key={item.id} onClick={() => onClick(item)}>
{item.name}
</li>
))}
</ul>
);
};
const SelectedList = ({ items }) => {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
const App = () => {
const [items, setItems] = useState([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]);
const [selectedItems, setSelectedItems] = useState([]);
const handleClick = useCallback(
(item) => {
if (!selectedItems.some((selected) => selected.id === item.id)) {
setSelectedItems((prev) => [...prev, item]);
}
},
[selectedItems]
);
const memoizedList = useMemo(() => {
return <List items={items} onClick={handleClick} />;
}, [items, handleClick]);
return (
<div>
<h3>List:</h3>
{memoizedList}
<h3>Selected items:</h3>
<SelectedList items={selectedItems} />
</div>
);
};
export default App;
이 예시에서 사용된 useCallback과 useMemo의 사용법을 살펴보겠습니다.
useCallback:
App 컴포넌트에서 handleClick 함수는 아이템을 클릭시 선택된 목록에 추가하는 역할을 합니다.
이 함수는 List 컴포넌트로 전달되며, useCallback으로 감싸 메모이제이션되어 동일한 참조를 유지하게 합니다.
이로 인해 List 컴포넌트가 불필요한 리렌더링없이 handleClick 함수를 참조할 수 있습니다.
useMemo:
App 컴포넌트에서 memoizedList 변수는 useMemo를 사용하여 List 컴포넌트에 대한 메모이제이션을 수행합니다.
이렇게 하면 items와 handleClick이 변경될 때만 List 컴포넌트가 리렌더링되어 성능이 최적화됩니다.
이와 같이 상황에 따라 useCallback과 useMemo를 적절하게 사용하면 컴포넌트 리렌더링을 방지하고 성능을 최적화할 수 있습니다.
'TypeScript' 카테고리의 다른 글
TypeScript에서 [key: string]: any;를 사용하는 방법과 주의사항 (0) | 2023.06.20 |
---|
댓글