React에서 useCallback은 성능 최적화를 위해 자주 사용되는 훅 중 하나입니다. 컴포넌트가 다시 렌더링될 때 함수 참조가 불필요하게 변경되는 문제를 방지하여, 자식 컴포넌트로 전달되는 props 변화나 불필요한 re-render를 줄일 수 있습니다. 이번 블로그에서는 useCallback의 작동 원리, 사용법, 주의점, 그리고 다양한 예제를 통해 그 실용성을 알아보겠습니다.
1. useCallback이란?
useCallback은 React에서 제공하는 훅으로, 함수를 메모이제이션(Memoization)하여 컴포넌트가 다시 렌더링되더라도 동일한 함수 참조를 유지하도록 합니다. 이는 주로 다음과 같은 상황에서 유용합니다:
- 자식 컴포넌트에 함수를 props로 전달해야 할 때
- 함수 참조 변경으로 인해 불필요한 연산이나 렌더링이 발생할 때
2. 사용법
useCallback의 기본 문법은 다음과 같습니다:
const memoizedCallback = useCallback(
() => {
// 함수 로직
},
[의존성 배열] // 이 배열이 변경될 때만 함수가 새로 생성됩니다.
);
매개변수 설명:
- 첫 번째 인자: 메모이제이션할 함수.
- 두 번째 인자: 의존성 배열. 배열 내 값이 변경될 때에만 함수 참조를 새로 생성합니다.
3. 예제 1: 기본 사용법
다음은 useCallback을 사용하여 간단히 버튼 클릭 핸들러를 메모이제이션하는 예제입니다.
import React, { useState, useCallback } from "react";
function Counter() {
const [count, setCount] = useState(0);
// 메모이제이션된 함수
const handleClick = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []); // 의존성 배열이 비어 있으므로, 함수 참조는 컴포넌트의 라이프사이클 동안 유지됨
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default Counter;
이점: handleClick 함수는 렌더링마다 새로 생성되지 않으므로 성능이 향상됩니다.
4. 예제 2: 자식 컴포넌트와의 연동
useCallback은 주로 자식 컴포넌트에 함수를 props로 전달할 때 사용됩니다. 그렇지 않으면 자식 컴포넌트는 부모 컴포넌트의 함수 참조 변경으로 인해 불필요한 렌더링이 발생합니다.
코드 예시
import React, { useState, useCallback } from "react";
const Child = React.memo(({ onClick }) => {
console.log("Child component rendered");
return <button onClick={onClick}>Click me</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<Child onClick={increment} />
</div>
);
}
export default Parent;
설명
- React.memo를 사용하여 Child 컴포넌트를 메모이제이션합니다.
- useCallback을 사용해 increment 함수 참조가 변경되지 않도록 보장합니다.
결과: 부모 컴포넌트가 렌더링될 때도, Child 컴포넌트는 함수 참조가 변경되지 않아 재렌더링되지 않습니다.
5. 예제 3: 의존성 배열의 역할
useCallback의 의존성 배열이 어떻게 동작하는지 이해하는 것은 중요합니다. 다음 예제를 통해 의존성 배열에 따라 함수가 새로 생성되는 시점을 살펴봅니다.
코드
import React, { useState, useCallback } from "react";
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const handleChange = useCallback(
(e) => {
setText(e.target.value);
},
[text] // text가 변경될 때만 새로운 함수 참조 생성
);
return (
<div>
<input type="text" value={text} onChange={handleChange} />
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default App;
분석
- handleChange는 text가 변경될 때만 새로 생성됩니다.
- 의존성 배열을 올바르게 관리하지 않으면, 함수 내부에서 사용하는 값이 최신 상태를 유지하지 못할 수 있습니다.
6. 주의점
useCallback을 사용할 때 유의해야 할 몇 가지 사항이 있습니다:
1. 불필요한 사용 자제
useCallback을 모든 함수에 적용하면 오히려 코드 가독성을 해치고, 메모리 사용량이 증가할 수 있습니다. 성능 최적화가 필요한 경우에만 사용하는 것이 좋습니다.
2. 의존성 배열 관리
의존성 배열을 잘못 설정하면 메모이제이션의 의도가 깨지거나, 최신 상태 값을 참조하지 못할 수 있습니다.
const memoizedCallback = useCallback(() => {
console.log(count); // 의존성 배열에 count가 없으면 최신 값이 아님
}, []);
7. 결론
useCallback은 React 애플리케이션에서 성능을 최적화하는 강력한 도구입니다. 하지만 모든 함수에 남용하기보다는, 컴포넌트의 렌더링 최적화가 필요한 상황에서 신중하게 사용하는 것이 중요합니다. 위 예제를 통해 useCallback을 실무에서 활용할 수 있는 자신감을 얻으시길 바랍니다!
질문이나 더 알아보고 싶은 주제가 있다면 댓글로 남겨주세요!
'React' 카테고리의 다른 글
React에서 key 속성의 중요성 (0) | 2024.12.12 |
---|---|
React에서 useMemo 훅 사용법 (0) | 2024.12.12 |
React에서 React.memo 사용법 (0) | 2024.12.12 |
React에서 memoization 이해하기 (0) | 2024.12.12 |
React에서 상태(state)와 props 비교하기 (0) | 2024.12.12 |