메모이제이션(Memoization)이란?
"메모이제이션이란 프로그래밍을 할 때 반복되는 결과(복잡한 연산의 함수)를 메모리에 저장해서 다음 호출에도 같은 결과가 나올 때 캐시된 값을 가져오는 코딩 기법을 말합니다."
아래 내용에서는 전역변수와 클로저 그리고 재귀함수를 통하여 메모이제이션이 JavaScript에서 어떻게 활용되는지 알아보자.
재귀함수란?
함수 안에 자신의 함수를 다시 호출하는 함수를 의미한다. 이러한 재귀함수는 자신의 로직을 내부적으로 반복하다가, 일정한 조건이 만족되면 함수를 이탈하여 결과를 도출한다.
클로저란?
클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.
출처: https://poiemaweb.com/js-closure
1. 전역변수와 재귀함수를 활용한 메모이제이션(Memoization).
재귀함수와 전역변수를 사용하여 아래 팩토리얼 함수로 메모이제이션기법 예시를 살펴보자.
let save = {}; // 이전에 계산된 팩토리얼 값을 저장하는 객체
const factorial = (number) => {
if (number > 0) {
// 이미 저장된 값이 있으면 사용하고, 없으면 재귀적으로 팩토리얼 계산
const saved = save[number - 1] || factorial(number - 1);
// 현재 수(number)와 이전 팩토리얼 값(saved)을 곱하여 결과 계산
const result = number * saved;
// 계산된 결과를 저장
save[number] = result;
// 저장된 값과 결과를 출력 (확인용)
console.log(saved, result);
return result;
} else {
return 1;
}
};
//첫번째 호출
console.time('memoization_test_1');
factorial(7);
console.timeEnd('memoization_test_1');
//두번째 호출
console.time('memoization_test_2');
factorial(7);
console.timeEnd('memoization_test_2');
- save 객체는 이전에 계산된 팩토리얼 값을 저장하기 위한 전역변수.
- 매개변수(number)가 0보다 큰 경우,** 이미 저장된 값이 있는지 확인하고 없으면 재귀적으로 팩토리얼 값을 계산**.
- 현재 수(number)와 이전 팩토리얼 값(saved)을 곱하여 결과를 계산하고, 계산된 결과를 save 객체에 저장.
- console.log를 통해 저장된 값과 결과를 출력하여 계산 과정을 확인.
- 주어진 수가 0보다 작거나 같은 경우, 0!은 1이므로 1을 반환.
이렇게 전역변수를 활용한 메모이제이션 기법을 통하여 factorial(7)을 두 번 연속 해봤고 결과는
첫번째 호출 : 1 1, 1 2, 2 6, 6 24, 24 120, 120 720, 720 5040
두번째 호출 : 720 5040
두 번째 호출 때는 한 번 만에 실행 결과가 나오게 된다. 이전에 했던 계산이 전역변수에 저장되어있기 때문이다. 이렇게 메모이제이션 기법을 활용하면 반복되는 결과에서 *성능을 최적화 *할 수 있다.
2. 클로저(Closure)를 활용한 메모이제이션(Memoization).
이번엔 클로저와 즉시실행함수를 사용하여 팩토리얼 함수로 예시를 살펴보자.
즉시실행함수란(IIFE, Immediately Invoked Function Expression)?
함수가 선언되자마자 실행되도록 하는 문법. 단 한번의 사용이 필요한 함수.
- 필요없는 전역변수의 생성을 줄일 수 있다.
- 외부에서 접근 할 수 없는 private한 변수를 만들 수 있다.
const factorial = (() => {
const save = {}; // 이전에 계산된 팩토리얼 값을 저장하는 지역변수
const fact = (number) => {
if (number > 0) {
// 이미 저장된 값이 있으면 사용하고, 없으면 재귀적으로 팩토리얼 계산
const saved = save[number - 1] || fact(number - 1);
// 현재 수(number)와 이전 팩토리얼 값(saved)을 곱하여 결과 계산
const result = number * saved;
// 계산된 결과를 저장
save[number] = result;
// 저장된 값과 결과를 출력 (확인용)
console.log(saved, result);
return result;
} else {
return 1;
}
};
return fact; // 팩토리얼 계산 함수 반환
})();
//첫번째 호출
console.time('memoization_test_1');
factorial(7);
console.timeEnd('memoization_test_1');
//두번째 호출
console.time('memoization_test_2');
factorial(7);
console.timeEnd('memoization_test_2');
- save 객체는 이전에 계산된 팩토리얼 값을 저장하기 위한 지역변수.
- fact 함수는 factorial(7)함수의 인자값 팩토리얼을 계산하는 함수.
- 매개변수(number)가 0보다 큰 경우,** 이미 저장된 값이 있는지 확인하고 없으면 재귀적으로 팩토리얼 값을 계산**.
- 현재 수(number)와 이전 팩토리얼 값(saved)을 곱하여 결과를 계산하고, 계산된 결과를 save 객체에 저장.
- console.log를 통해 저장된 값과 결과를 출력하여 계산 과정을 확인.
- 주어진 수가 0보다 작거나 같은 경우, 0!은 1이므로 1을 반환.
- factorial 함수는 fact 함수를 반환하여 외부에서 팩토리얼 계산을 수행.
클로저를 만든 후 factorial(7)을 두 번 연속 해봤고 결과는 전역변수때와 마찬가지로
첫번째 호출
- console.log: 1 1, 1 2, 2 6, 6 24, 24 120, 120 720, 720 5040
- 함수 실행 시간: 1.354736328125 ms
두번째 호출
- 720 5040
- 함수 실행 시간: 0.105224609375 ms
두 번째 호출 때는 한 번 만에 실행 결과가 나오게 되고, 함수실행 시간을 보면 확연히 그 차이를 알 수 있다. 이전에 했던 계산이 메모리에 저장되어있기 때문이다.
두 가지 예시, 권장하는 방법은 클로저
- 캡슐화: 클로저를 사용하면 데이터를 함수의 범위 내에 캡슐화하여 외부 코드에서 접근할 수 없도록 유지할 수 있습니다. 이를 통해 전역 변수와의 의도하지 않은 수정이나 충돌을 방지할 수 있다.
- 전역 네임스페이스 오염 감소: 전역 변수는 전역 네임스페이스를 빠르게 혼란스럽게 만들 수 있다.
예시코드 출처 : https://www.zerocho.com/category/JavaScript/post/579248728241b6f43951af19
'JavaScript' 카테고리의 다른 글
[JavaScript] BFcache란 ? (0) | 2024.09.09 |
---|---|
[JavaScript] 호이스팅(Hoisting)이란? (0) | 2024.02.12 |