👩💻 이 글을 쓰게 된 계기...
React는 Virtual DOM을 사용하여 컴포넌트의 렌더링 성능을 최적화한다. 그러나 불필요한 렌더링이 발생할 수 있는 상황이 있다. 이러한 상황에서는 React의 useCallback 훅을 사용하여 함수를 재사용하면 성능을 향상시킬 수 있다고 하는데 정확히 어떤 상황에서 써야 이게 잘 썼는지 소문이 날까? 란 생각에 정리해 보았다.
👁🗨 useCallback을 사용해야 하는 경우
일단 이론적으로 React에서 useCallback을 사용해야 하는 경우는 다음과 같다.
✅ 이벤트 핸들러 함수가 자주 재생성되는 경우
useCallback을 사용하지 않으면 이벤트 핸들러 함수는 매번 새로운 인스턴스가 생성된다. 그러나 useCallback을 사용하면 함수가 처음 생성될 때 한 번만 생성되며, 나중에는 동일한 함수 인스턴스를 재사용하게 된다.
✅ 하위 컴포넌트에 props로 전달되는 함수가 자주 재생성되는 경우
React에서 props로 함수를 전달하는 경우, 해당 함수가 변경되면 하위 컴포넌트가 재렌더링된다. 따라서, useCallback을 사용하여 함수를 재사용하면 하위 컴포넌트의 재렌더링을 방지할 수 있다.
✅ 렌더링 최적화가 필요한 경우
useCallback을 사용하여 함수를 재사용하면, 렌더링 최적화를 수행할 수 있다. 이는 컴포넌트의 불필요한 재렌더링을 방지하고, 성능을 향상시킬 수 있다.
📝 useCallback 사용 예시
이론은 충분하다. 그래서 어쩌라는 건가. 나같이 뇌주름 없이 맑고 깨끗한 사람은 코드 예시로 보는걸 좋아한다.
1. 콜백 함수가 자식 컴포넌트에 props로 전달되는 경우
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<ChildComponent onClick={handleClick} />
</div>
);
}
function ChildComponent({ onClick }) {
return (
<button onClick={onClick}>Click me</button>
);
}
handleClick 함수가 ParentComponent 컴포넌트에서 생성되어 ChildComponent 컴포넌트에 props로 전달된다. 만약 useCallback을 사용하지 않으면 ParentComponent가 리렌더링 될 때마다 handleClick 함수가 새로 생성된다. 이 경우 ChildComponent는 onClick props가 변경되었으므로 불필요한 리렌더링이 발생하게 된다. 이를 방지하기 위해 useCallback을 사용하여 handleClick 함수를 메모이제이션한다.
💡 메모이제이션 이란?
이전에 계산한 값을 메모리에 저장해두고, 동일하게 다시 사용할 수 있는 곳에서 재사용하여 반복적으로 발생하는 계산의 리소스를 줄이는 기법
2. 콜백 함수가 의존성 배열(dependency array)에 있는 경우
function ParentComponent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const handleChangeText = useCallback((e) => {
setText(e.target.value);
}, []); // handleChangeText 함수는 의존성이 없음
const handleClick = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, [count]); // handleClick 함수는 count 변수에 의존함
return (
<div>
<input type="text" value={text} onChange={handleChangeText} />
<button onClick={handleClick}>Click me ({count})</button>
</div>
);
}
handleChangeText 함수는 text 상태만을 참조하므로 의존성 배열이 비어있다. handleClick 함수는 count 상태를 참조하므로 의존성 배열에 count를 추가해준다. count가 변경될 때마다 handleClick 함수가 새로 생성된다. 이를 방지하기 위해 useCallback을 사용하여 handleClick 함수를 메모이제이션한다.
3. useEffect와 같이 사용해 컴포넌트 최적화 하는 경우
import React, { useCallback, useEffect, useState } from "react";
function ExampleComponent(props) {
const [data, setData] = useState([]);
const fetchData = useCallback(async () => {
const response = await fetch("https://example.com/data");
const data = await response.json();
setData(data);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<div>
{data.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
export default ExampleComponent;
위 예시 코드에서는 useCallback과 useEffect를 사용하여 데이터를 불러오는 fetchData 함수를 최적화해주는 코드이다. useEffect의 의존성 배열에 fetchData 함수를 추가하여, fetchData 함수가 변경될 때에만 useEffect가 실행되도록 한다.
이렇게 함으로써, 컴포넌트가 렌더링될 때마다 fetchData 함수가 새로 생성되는 것을 방지하고, fetchData 함수가 변경될 때에만 데이터를 다시 불러오도록 최적화할 수 있다.
✨ 결론
useCallback은 React의 성능 최적화에 매우 유용한 훅이다. 이벤트 핸들러 함수나 props로 전달되는 함수가 자주 재생성되는 경우, useCallback을 사용하여 함수를 재사용하여 성능을 향상시킬 수 있다. 그러나 useCallback은 모든 상황에서 사용해야 하는 것은 아니다. 함수가 자주 재사용되지 않거나, 상태나 props에 의존하지 않는 경우에는 사용하지 않아도 무방하며 오히려 불필요하게 많은 함수를 생성하면 렌더링 성능이 떨어질 수 있다.
useCallback은 React의 렌더링 최적화를 위해 함께 사용되는 다른 훅들과 함께 사용할 때 더욱 효과를 볼 수 있다. 예를 들어, useMemo를 사용하여 계산 결과를 캐싱하거나, React.memo를 사용하여 컴포넌트를 메모이제이션할 수 있다.
React 개발자는 useCallback의 사용 시기와 사용 방법을 잘 이해하고 있어야 하는데 그게 어려워 일단 기록해 본다. 무튼 잘 사용하면 이를 통해, React 애플리케이션의 성능을 최적화하고 사용자 경험을 향상시킬 수 있으니 잘 사용하도록 노력해 보자.
'React' 카테고리의 다른 글
CSR(Client-side Rendering)이란? (+SSR 비교) (0) | 2023.06.28 |
---|---|
[React] Redux 와 Context-API의 비교, 사용 사례 및 코드 예시 (0) | 2023.04.05 |
댓글