자바스크립트는 강력하고 다재다능한 프로그래밍 언어이며 보다 효율적이고, 유지 보수가 용이하며, 가독성이 높은 코드를 작성하는 데 도움이 되는 기능들을 많이 내장하고 있습니다.
이 글에서는, 내장된 기능을 사용해 성능을 향상하고 코드를 더 멋지게 보이도록 하는 아주 강력한 함수들을 만드는 방법을 설명하려고 합니다. 이는 자바스크립트 작업을 더욱 즐겁고 효율적으로 만들어 줍니다. 자바스크립트 개발자로서 코드 품질을 최적화하기 위해 유틸리티 파일/클래스에 저장할 수 있는 Debounce, Throttle, Once, Memoize, Curry, Partial, Pipe, Compose, Pick, Omit, 그리고 Zip까지 다뤄보겠습니다.
Memoize는 자바스크립트 함수로, 동일한 인수로 연산 비용이 많이 드는 루틴을 여러 번 호출하는 것을 방지하기 위해 특정 함수의 결과를 캐싱하는 용도로 사용됩니다.
function memoize(func) {
const cache = new Map();
return function() {
const key = JSON.stringify(arguments);
if (cache.has(key)) {
return cache.get(key);
}
const result = func.apply(this, arguments);
cache.set(key, result);
return result;
};
}
memoize()
함수는 주어진 함수의 결과를 캐싱하고 동일한 인수로 다시 호출될 때 그 결과 값을 가져오기 위해 인수를 키로 사용합니다.
입력 변수를 기반으로 복잡한 계산을 수행하는 함수가 있다면, 결과 값을 캐싱하고 동일한 입력값으로 여러 번 호출될 때 즉시 값을 가져올 수 있도록 memoize()
함수를 사용할 수 있습니다.
memoize()
함수의 이점을 보기 위해, 피보나치수열을 계산하는 데에 사용할 수 있습니다.
// 계산을 수행하는 함수 정의
function fibonacci(n) {
if (n < 2)
return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 메모화된 함수 생성
const memoizedFibonacci = memoize(fibonacci);
// 여러 입력 값으로 메모화된 함수 호출
console.time('total')
console.time('sub1')
const result1 = memoizedFibonacci(30);
console.timeEnd('sub1')
console.time('sub2')
const result2 = memoizedFibonacci(29);
console.timeEnd('sub2')
console.time('sub3')
const result3 = memoizedFibonacci(30);
console.timeEnd('sub3')
console.timeEnd('total')
이 예제에서, finbonacci()
함수는 memoizedFibonacci
함수로 변환될 겁니다. 그 후 memoized()
함수가 호출되고 실행 시간이 콘솔에 나타납니다.
결과는 다음과 같습니다.
두 번째 호출은 29에 대한 피보나치 수를 계산함에도 불구하고, 30에 대한 피보나치 수를 계산하는 세 번째 호출보다 더 오래 걸렸습니다. 세 번째 호출은 memoize()
함수에 의해 캐싱된 값을 사용했기 때문입니다.
Curry(커링 이라고도 알려진) 함수는 일부 인수를 "미리 채움"으로써 이미 존재하는 함수에서 새로운 함수를 만드는 데 사용되는 고급 자바스크립트 함수입니다. 커링은 여러 인수를 받는 함수로 작업할 때 자주 사용되는데, 항상 같을 인수들을 제외한 나머지 인수만을 받는 함수로 변환합니다.
Curry 함수는 여러 장점이 있습니다.
function curry(func, arity = func.length) {
return function curried(...args) {
if (args.length >= arity) return func(...args);
return function(...moreArgs) {
return curried(...args, ...moreArgs);
};
};
}
Curry 함수는 다른 함수(func
)를 인수로 받고 func
의 인수의 길이를 기본값으로 가지는 arity
인수를 선택적으로 가집니다. 함수는 arity
수만큼의 인수와 함께 호출되는 새로운 함수(curried
) 함수를 반환합니다. 모든 인수가 제공되지 않은 경우, 필요한 인수가 모두 주어질 때까지 더 많은 인수로 호출할 수 있는 새로운 함수를 반환합니다. 모든 인수가 주어지면, 원래 함수(func
)가 호출되고, 이 함수의 결과 값이 반환됩니다.
Curry 함수의 이점을 이해하기 위해, 한 면에 있는 두 점의 거리를 계산하는 메서드를 가정해 볼 수 있습니다. Curry 함수를 이용하면, 여러 점들 중 한 점만 필요한 함수를 만들 수 있어 더욱 쉬워집니다.
다음 스니펫은 이전에 정의된 curry 함수가 어떻게 구현의 가독성을 최적화하는지를 보여줍니다.
// 두 점 간의 거리를 계산하는 함수 정의
function distance(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}
// 한 점만 필요로 하게끔 함수를 curry된 버전으로 생성
const distanceFromOrigin = curry(distance, 3)(0, 0);
// 다른 점을 인수로 하여 curry된 함수를 호출
const d1 = distanceFromOrigin(1, 1);
const d2 = distanceFromOrigin(2, 2);
이 예제에서, distance
함수는 curry
함수에 첫 번째 인수로 넘겨지고 두 번째 인수(arity
)로 3
을 함께 넘겨 커리된 함수(distanceFromOrigin
)로 생성되었습니다. 또한, 커리된 함수와 함께 0, 0
두 인수를 전달하여 호출합니다.
결과적으로 생성된 distanceFromOrigin
함수는 두 개의 인수만을 필요로 하는 새로운 함수가 되었고, 첫 번째 점으로 항상 0, 0
을 사용하게 됩니다.
자바스크립트에서 Partial 함수는 Curry 함수와 유사합니다. 하지만 Curry 함수는 커링 체인에서 다른 함수를 반환하고, Partial 함수는 결과를 즉시 반환한다는 큰 차이가 있습니다.
function partial(func, ...args) {
return function partiallyApplied(...moreArgs) {
return func(...args, ...moreArgs);
}
}
자바스크립트에서 partial
함수는 일반적으로 함수와 하나 이상의 입력 인수를 받고, 새로운 함수가 호출될 때 인수로 받은 함수에 추가로 받은 인수들을 전달하여 호출하는 새로운 함수를 반환합니다.
다음 경우에서, calculate
함수에 처음 두 개의 인수를 미리 채우고 더 가독성 있는 이름을 가지는 새로운 함수를 생성합니다.
// 계산하는 함수 정의
function calculate(x, y, z) {
return (x + y) * z
}
// 마지막 인수(z)만 필요로 하도록 parital이 적용된 함수 버전으로 생성
const multiply10By = partial(calculate, 8, 2);
// 반복 횟수 값을 전달하여 partial이 적용된 함수를 호출
const result = multiply10By(5);
이 예제에서, 일반 calculate
함수를 부분적으로 적용하고 미리 8과 2라는 첫 두 개의 인수를 미리 채우는 방식으로 multiply10By
함수를 생성합니다. 이렇게 생성된 multiply10By
함수는 10을 얼마나 곱할지를 정하는 하나의 인수만을 필요로 합니다. 또한, 이 방식을 통해 코드의 가독성과 이해도를 더 향상할 수 있습니다.
오늘은 여기까지 알아보고 다음 시간에는 Pipe 부터 알아보겠습니다.