Memoization : useMemo, useCallback
Memoization
메모이제이션이란 이미 해본 연산결과를 기억해뒀다가 동일한 계산을 시키면 기억해두었던 데이터를 반환하는 최적화를 말한다. 쉽게말해 불필요하게 다시 호출되어 실행되는 함수를 줄이고 코드를 효율적으로 사용할 수 있게 도와주는 기술이다.
useMemo는 두번째 인자로 받는 의존성 배열이 달라졌을 때 메모이제이션된 값을 다시 계산한다.
data의 길이, 즉 데이터가 추가되거나 삭제되었을 경우에만 일기 분석을 다시 시작하고 싶다고 할 때
getDiaryAnalysis 화살표 함수에 useMemo를 간단히 추가할 수 있다.
useMemo는 연산의 수행 결과를 저장해두고 계속 사용하다가, deps가 변경될 때만 다시 연산을 시작한다.
즉 불필요한 재연산은 막으면서 필요시에는 최신상태로 유지할 수 있다.
const getDiaryAnalysis = useMemo(() => {
console.log('일기 분석 시작');
const goodCount = data.filter(item => item.emotion >= 3).length;
const badCount = data.length - goodCount;
const goodRatio = (goodCount / data.length) * 100;
return { goodCount, badCount, goodRatio };
}, [data.length]);
전체 컴포넌트에 useMemo를 사용하기 위해서는 export default ~ 이하를 React.memo로 감싸면 된다.
export default React.memo(DiaryEditor);
다만 useMemo는 deps에 변동이 있을 때 마다 연산을 수행하기 때문에 잦은 변동이 있거나, 메모해둘만큼 연산이 복잡한 계산식이 아님에도 사용하는 경우에는 오히려 유지보수를 어렵게 만든다는 단점이 있다.
useCallback은 useMemo와 유사하지만 자바스크립트의 함수의 동등성 문제를 해결하기 위한 메모이제이션 Hook이라고 할수있다.
import React, { useState, useEffect } from "react";
function Profile({ userId }) {
const [user, setUser] = useState(null);
const fetchUser = useCallback(
() =>
fetch(`https://your-api.com/users/${userId}`)
.then((response) => response.json())
.then(({ user }) => user),
[userId]
);
useEffect(() => {
fetchUser().then((user) => setUser(user));
}, [fetchUser]);
// ...
}
함수 동등성 문제는, 함수는 데이터가 마운트 될 때 즉, 컴포넌트가 리랜더링 될 때 마다 함수의 메모리 주소가 변경되어 동등하지 않아도 판단하는 문제를 말한다. 만약 useEffect의 deps가 함수로 설정되어 있다면, 그 함수의 실제 데이터가 변하는지 변하지 않는지와 무관하게 매번 useEffect가 다시 실행되는 무한 루프에 빠지게 된다.
useCallback은 컴포넌트가 리랜더링 되더라도 함수의 참조값을 동일하게 유지시킬 수 있다.