기존에 수행한 연산의 결과값을 저장해두고 동일한 입력이 들어오면 재활용하는 기법이다. Memoized된 내용을 재사용하여 렌더할 시, 가상 DOM에서 바뀐 부분을 확인하지 않아 성능이 향상된다.
리액트 메모이제이션의 핵심은 바로 이전의 값만 메모이제이션 한다는 것이다.
일반적인 재귀함수로 피보나치 수열 을 계산하는 함수를 구현하려고 한다면 다음과 같다.
피보나치 수열은 바로 앞 두 항의 합으로 이루어진 수열이다.
ex ) 0, 1, 1, 2, 3, 5, 8, 13, 21, ......
// 피보나치 수열을 재귀 함수로 계산하는 함수
let count = 0;
let fibonacci = function(number) {
count ++;
return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2);
}
for(var i = 0; i <= 10; i++) {
console.log(fibonacci(i)); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
}
console.log("count : " + count) // count : 453
위 처럼 반복문으로 구현은 가능하지만 파라미터로 10이라는 비교적 작은 수를 주었는데도 fibonacci
함수는 453번이나 실행된다. 여기서 11번은 직접 호출한 것이지만, 나머지 442번은 이미 계산한 값들을 다시 계산하기 위해 호출한 것이다.
이때, 메모이제이션 패턴을 이용하면 반복되는 연산을 줄일 수 있다.
이미 계산한 내용들을 저장해두고, 다시 연산할 필요없이 저장된 데이터를 불러오는 방식이다.
let count = 0;
let fibonacci = function() {
let memo = [0, 1];
let fib = function(number) {
count++;
var result = memo[number];
if(typeof result !== 'number') {
result = fib(number - 1) + fib(number - 2);
memo[number] = result;
}
return result;
};
return fib;
}();
for(var i = 0; i <= 10; i++) {
console.log(fibonacci(i)); // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
}
console.log("count : " + count) // count : 29
우선함수 실행 횟수가 줄어든 것을 확인할 수 있다. 불필요한 연산을 줄였기 때문이다.
메모이제이션 한 함수 안에 state
값이 있으면 state값 까지 memo되어 값이 바뀌지 않고 렌더링도 되지 않는다. 이런 경우는 useCallback
을 잘못 사용한 경우이다.
// 잘못된 사용
const onClickCountState = useCallback(() => {
setCountState(countState + 1);
}, []);
// state 대신 prev 사용
const onClickCountState = useCallback(() => {
setCountState((prev)=> prev + 1);
}, []);
결론
메모이제이션은 속도면에서 큰 이점이 있지만 속도를 위해 많은 메모리 사용량이 소비되기 때문에, 많은 RAM을 사용하는 함수를 처리해야 할 경우 영향을 준다고 한다.
아직까지 연산이 많이 필요한 코드를 작성해볼 일이 없어서 사용해보지 못했지만, 복잡한 연산이 있는 경우에 유용하게 쓰일 것 같다.
참고
https://frontsom.tistory.com/11
https://ko.reactjs.org/docs/hooks-faq.html#how-to-memoize-calculations