최근에 다른 개발자와 이야기를 나누다가 메모이제이션에 대해 제대로 이해하지 못한체로 사용하고 있는 것을 발견했다.
그래서 그런 사람들을 위해서라도 개념을 정리해보고자 한다.
🚨React19 부터는 useMemo와 useCallback을 사용하지 않아도 자동으로 리액트 컴파일러가 최적화 해준다고 한다.🚨
메모이제이션이란?
메모이제이션(Memoization)은 컴퓨터 프로그래밍에서 사용되는 최적화 기법 중 하나.
이 기법은 함수 호출의 결과를 저장해두고, 동일한 입력이 다시 발생했을 때 함수를 재실행하는 대신 저장해둔 결과를 반환하는 방식으로 작동한다.
이를 통해 동일한 계산을 반복하는 상황에서 성능을 크게 향상시킬 수 있다.
React에서는 컴포넌트가 불필요하게 재렌더링되는 것을 방지하기 위해 메모이제이션 기법을 적용할 수 있는 여러 훅(Hook)을 제공한다.
그 중에서도 가장 많이 사용되는 것이 useMemo와 useCallback이다.
React에서 렌더링이 일어나는 원리
React에서의 렌더링 과정을 이해하는 것은 메모이제이션의 필요성을 파악하는 데 중요하다고 생각한다.
React 컴포넌트는 다음과 같은 경우에 재렌더링된다.
- 컴포넌트의 state가 변경될 때
- 부모 컴포넌트가 재렌더링될 때
- context가 변경될 때
- props가 변경될 때
특히 중요한 점은 부모 컴포넌트가 렌더링되면 기본적으로 모든 자식 컴포넌트도 재렌더링된다는 것이다. (React의 기본 동작 방식)
대부분의 경우 이러한 재렌더링은 빠르게 일어나기 때문에 문제가 되지 않지만, 컴포넌트가 복잡한 계산을 수행하거나 대규모 데이터를 처리하는 경우에는 성능 저하가 발생할 수 있다.
useMemo 훅의 이해
useMemo는 React에서 제공하는 훅 중 하나로, 메모이제이션된 값을 반환한다.
이 훅은 의존성 배열에 포함된 값이 변경될 때만 값을 재계산하고, 그렇지 않으면 이전에 계산된 값을 재사용한다.
useMemo의 기본 문법
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
첫 번째 인자는 값을 계산하는 함수,
두 번째 인자는 의존성 배열이다.
의존성 배열 내의 값이 변경되면 첫 번째 인자의 함수가 재실행되어 새로운 값을 계산한다.
🚨근데 여기서 useMemo와 React.memo를 헷갈려하는 사람이 많다.🚨
React.memo는 컴포넌트 자체를 메모이제이션한다. (컴포넌트에 전달되는 props가 변경되지 않으면 컴포넌트의 재렌더링을 방지한다.)
아래 코드를 볼까?
const MyComponent = React.memo(function MyComponent(props) {
// 컴포넌트 로직
});
이렇게 하면 MyComponent는 props가 변경되지 않는 한 부모 컴포넌트가 재렌더링되어도 다시 렌더링되지 않는다.
그럼 useMemo는 뭘까?
위에서 말한것처럼 useMemo는 값을 메모이제이션하는 Hook이다.
특정 계산 비용이 큰 값을 의존성 배열의 값이 변경될 때만 재계산하도록 한다.
이 둘은 서로 보완적인 관계라고 보면된다.
다시 한 번 정리해보자면
- React.memo는 컴포넌트 자체의 렌더링을 최적화 하고
- useMemo는 컴포넌트 내부에서 계산되는 값을 최적화 한다.
예를 들어 컴포넌트에 전달하는 객체나 함수를 useMemo나 useCallback으로 메모이제이션하고,
그 컴포넌트 자체는 React.memo로 감싸는 패턴이 자주 사용된다.
function ParentComponent() {
// 값 메모이제이션
const memoizedValue = useMemo(() => computeExpensiveValue(), [dependency]);
return <ChildComponent data={memoizedValue} />;
}
// 컴포넌트 메모이제이션
const ChildComponent = React.memo(({ data }) => {
// 컴포넌트 로직
});
요약하자면, React.memo는 "이 컴포넌트를 메모이제이션해줘"라고 하는 것이고,
useMemo는 "이 값을 메모이제이션해줘"라고 하는 것이다!
useCallback 훅의 이해
useCallback은 메모이제이션된 콜백 함수를 반환하는 훅이다.
useMemo가 값을 메모이제이션한다면,useCallback은 함수 자체를 메모이제이션한다.
이 훅은 의존성 배열에 포함된 값이 변경될 때만 새로운 함수를 생성하고, 그렇지 않으면 이전에 생성된 함수를 재사용한다.
useCallback의 기본 문법
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
첫 번째 인자는 메모이제이션할 콜백 함수,
두 번째 인자는 의존성 배열이다.
의존성 배열 내의 값이 변경되면 새로운 콜백 함수가 생성된다.
useMemo와 useCallback의 차이점
useMemo와 useCallback은 둘 다 메모이제이션을 위한 훅이지만, 그 목적과 사용법에는 차이가 있다.
반환 값의 차이
- useMemo: 메모이제이션된 값을 반환한다.
- useCallback: 메모이제이션된 함수를 반환한다.
실제로 useCallback은 useMemo의 특별한 케이스라고 볼 수 있다.
아래의 두 코드는 기능적으로 동일하다.
// useCallback 사용
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
// useMemo로 구현한 useCallback
const memoizedCallback = useMemo(() => {
return () => {
doSomething(a, b);
};
}, [a, b]);
사용 목적의 차이
- useMemo: 비용이 많이 드는 계산 결과를 캐싱하여 성능을 최적화할 때 사용한다.
- useCallback: 함수의 참조 동일성을 유지하여 자식 컴포넌트의 불필요한 재렌더링을 방지할 때 사용한다.
🚨메모이제이션 사용 시 주의사항🚨
사실 메모이제이션을 사용 할 때에 있어서 제일 주의해야 될 것은 "의존성 배열 관리"라고 본다.
메모이제이션은 의존성 배열에 전적으로 의존하는데 의존성 배열을 잘못 설정하면 버그가 발생할 수 있다.
필요한 의존성이 빠져있으면 최신 값을 참조하지 못할 수 있고, 불필요한 의존성이 포함되어 있으면 메모이제이션의 효과가 줄어들기 때문이 아닐까??
또한 의존성 배열 관리가 잘못 되어 있다면 불필요한 계산과 루프가 계속 반복되어지지 않을까?!!
'React' 카테고리의 다른 글
[React] Tesseract.js를 활용한 이미지 텍스트 인식(OCR) 구현하기📖 (0) | 2025.04.07 |
---|---|
[React] 라이브러리인가 프레임워크인가? (0) | 2025.02.27 |
[React] Query String과 Browser History를 활용한 필터링 상태 관리 구현하기 (0) | 2025.02.24 |
[React] Create React App의 지원 종료 ⚠️ (1) | 2025.02.22 |
[React] 반응형 웹 개발? react-responsive가 다해줘!!! (1) | 2025.02.20 |