[React Hooks (2)] useMemo() 알아보기
리액트를 활용하여 개발을 하면서 화면이 복잡한 경우, 성능 최적화에 신경을 쓰게 될 경우가 있다.
최근 진행한 프로젝트에서는 종합적으로 관리하는 페이지를 만들다보니 특정 페이지에서 렌더링 하는데 시간이 조금 더 소요되는 경우가 있었다.
그래서 특정 상황에서 useCallback()을 사용하는 경우가 생겼는데 useCallback()에 대해 알아보기 전에 동일하게 Memorization으로 최적화에 사용하는 useMemo()에 대해 먼저 정리해보려고 한다.
useMemo란?
리액트에서 컴포넌트의 성능을 최적화하는데 사용하는 hook으로 특정 함수의 결과 값을 임시 메모리에 담아두고(캐싱) 사용하여 렌더링을 줄이고 계산을 최적화 하는데 사용한다. (Memoization 개념)
useCallBack과의 차이점은 useCallback은 함수를 저장해서 사용하고, useMemo는 값을 저장해서 사용한다.
공식 문서를 보면 사용 방법은 다음과 같은 것들이 나온다.
1. 비용이 높은 로직의 재계산을 생략할 때
2. 컴포넌트의 재렌더링을 건너뛸때
3. Effect의 과도한 실행을 방지할때
4. 다른 hook의 종속성 메모화
5. 함수의 메모화
리액트 공식 사이트
(https://ko.react.dev/reference/react/useMemo)
개인적으로는 위의 말들을 처음엔 이해하기 어려워서 공부해보고 사용해보면서 내용을 알아보았다.
우선 리액트에서 사용하는 함수형 컴포넌트는 함수이고, 함수형 컴포넌트가 렌더링이 되는 과정은 호출되고 난 뒤에 모든 내부 변수가 초기화 된다. 그리고 내부의 계산(ex. 단순 숫자 계산부터 화면을 그리는 함수 등)들은 컴포넌트가 호출될 때마다 다시 진행된다.
이 과정에서 비효율적으로 계산들이 반복되기 때문에 특정 hook을 사용하여 Memorized된 함수를 재사용하며 효율성을 높이게 됩니다. 예를 들어 이미 기록된 점수를 합산하는 'getSum()'이라는 함수가 있다면 useMemo를 사용하여 그 결과 값을 메모리에 저장했다가 계산이 이전과 동일하다면 'getSum()' 함수를 다시 호출하지 않고 저장된 값을 가져와 재사용한다.
실제로 프로젝트를 보는 중 조회해온 list 아이템을 보여주는 화면을 useMemo()로 사용하는 케이스를 확인한 적이 있다.
하지만 useMemo()나 useCallback() 같이 메모리를 사용하여 최적화 하는 hook들을 무분별하게, 혹은 많이 사용할 경우 오히려 성능 정하를 초대할 수 있으니 조심해야 한다.
왜냐면 두 hook을 사용하면 메모리를 사용하게 되고 이를 위해 의존성 배열을 사용하기 때문에 복잡성이 높아진다.
사용 방법
const memo = useMemo(calculateValue, dependencies);
괄호 안의 첫 번째 인자로는 콜백 함수, 두 번째 인자로 의존성 배열(Dependency Array)을 받는다.
구조는 useEffect와 같고 의존성 배열에 있는 값이 업데이트 될 때 콜백 함수(값)를 다시 호출하여 임시 메모리에 저장된 값을 업데이트 해준다.
저는 공식 문서나 강의에서 소개하는 '비용이 높은 계산(혹은 비싼 계산)'을 예시로 들어 사용해봤습니다.
import { useMemo, useState } from "react";
// 소수를 판별하는 함수
const isPrime = (num) => {
if (num <= 1) return false;
for (let i = 2; i <= Math.sqrt(num); i++) {
if (num % i === 0) return false;
}
return true;
};
const UseMemoExample = () => {
const [numbers, setNumbers] = useState([2, 3, 4, 7, 10, 13, 17, 19]);
const [number, setNumber] = useState("");
// 소수 제곱의 합 계산을 useMemo로 최적화
const primeSquaresSum = useMemo(() => {
console.log("Expensive calculation in progress...");
return numbers
.filter(isPrime) // 소수만 걸러내고
.map((num) => num ** 2) // 제곱한 뒤
.reduce((acc, val) => acc + val, 0); // 합산
}, [numbers]);
const handleAddNumber = () => {
if (number !== "") {
setNumbers((prev) => [...prev, Number(number)]);
setNumber("");
}
};
return (
<div className="frame">
<h2>소수의 제곱 합: {primeSquaresSum}</h2>
<input
type="number"
value={number}
onChange={(e) => setNumber(e.target.value)}
/>
<button onClick={handleAddNumber}>숫자 추가</button>
<ul>
{numbers.map((num, index) => (
<li key={index}>{num}</li>
))}
</ul>
</div>
);
};
export default UseMemoExample;
코드 위치
https://github.com/HyonWooKang/reacthooks.git
GitHub - HyonWooKang/reacthooks
Contribute to HyonWooKang/reacthooks development by creating an account on GitHub.
github.com
이전 글
[React Hooks (1)] Hooks란? 그리고 useState()
https://spencer.tistory.com/44
[React Hooks (1)] Hooks란? 그리고 useState()
어쩌다보니 내가 2년차에 현재 회사에 입사 했을 때 모든 웹 개발자는 퇴사하고 없는 상황이었다.그래서 처음에는 다른 인원에 맞춰 모바일 네이티브 언어로 개발도 했었고 그 뒤로 웹앱과 웹을
spencer.tistory.com
참고 자료
1. 리액트 공식 사이트
https://ko.react.dev/reference/react/useMemo
2. 코딩병원님의 블로그
3. 별코딩님의 강의
https://www.youtube.com/watch?v=e-CnI8Q5RY4
useMemo 관련하여 읽어보면 좋은 글
useMemo, useCallback으로 최적화가 꼭 필요할까?
https://5kdk.github.io/blog/2023/07/04/necessary-with-usememo-and-usecallback
useMemo, useCallback으로 최적화가 꼭 필요할까? | 5kdk 개발 블로그
React의 메모제이션과 최적화에 대한 고찰.
5kdk.github.io