LHJ

I'm a FE developer.

13.6.3 함수를 반환하는 함수

20 May 2020 » js_lj

13.6.3 함수를 반환하는 함수

함수를 반환하는 함수는 아마 함수의 가장 난해한 사용법이겠지만, 그만큼 유용하기도 합니다.
어떤 기능이 있는 것을 만든다는 점에서, 함수를 반환하는 함수를 일종의 3D 프린터라고 생각할 수 있을 겁니다.
3D 프린터에 입력하는 설계도를 바꾸는 것과 마찬가지로, 반환받는 함수 역시 마음대로 바꿀 수 있습니다.

sum 함수를 다시 생각해 봅시다.
이 함수는 각 요소를 더하기 전에 해당 요소를 바꾸는 함수를 받습니다.
원한다면 sumOfSquares 함수를 만들 수도 있다고 했습니다.
그런데 상황이 바뀌어서, 그런 함수가 정말 필요하다고 해봅시다.
배열과 함수를 받는 함수로는 만족스러운 결과를 얻을 수 없고, 배열 하나만 받아서 제곱의 합을 반환하는 함수가 필요합니다(그런 상황이 언제 필요할지 잘 떠오르지 않겠지만, API에서 sum 함수를 허용하되 그 함수의 매개변수를 하나만 허용하는 경우가 있을 수 있습니다).

먼저, 이미 만들어 둔 sum 함수를 활용하는 방법이 있습니다.

function sum(arr, f) {
    // 함수가 전달되지 않았으면 매개변수를 그대로 반환하는 null 함수를 씁니다.
    if (typeof f != 'function') f = x => x;

    return arr.reduce((a, x) => a += f(x), 0);
}

function sumOfSquares(arr) {
    return sum(arr, x => x*x);
}

물론 이렇게 해도 됩니다.
필요한 것이 함수 하나라면 가장 간단한 해결책이 될 수 있습니다.
하지만 제곱근의 합을 구하는 함수, 세제곱의 합을 구하는 함수, 하는 식으로 이런 패턴이 계속 반복된다면 어떻게 해야 할까요?
필요한 함수를 반환하는 함수를 만들어 문제를 해결할 수 있습니다.

function newSummer(f) {
    return arr => sum(arr, f);
}

새 함수 newSummer가 반환하는 함수는 단 하나의 매개변수만 받으면서도, 우리가 원하는 중간 함수를 마음대로 쓸 수 있습니다.
다음 예제를 보십시오.

function sum(arr, f) {
    // 함수가 전달되지 않았으면 매개변수를 그대로 반환하는 null 함수를 씁니다.
    if (typeof f != 'function') f = x => x;

    return arr.reduce((a, x) => a += f(x), 0);
}

function newSummer(f) {
    return arr => sum(arr, f);
}

const sumOfSquares = newSummer(x => x*x);
const sumOfCubes = newSummer(x => Math.pow(x, 3));
sumOfSquares([1, 2, 3]);    // return 14
sumOfCubes([1, 2, 3]);      // return 36

NOTE_
이 예제처럼 매개변수 여러 개를 받는 함수를 매개변수 하나만 받는 함수로 바꾸는 것을 커링(currying)이라 부릅니다.
커링이라는 이름은 이 패턴을 만든 미국의 수학자 하스켈 커리(Haskell Curry)의 이름을 딴 것입니다.

함수가 함수를 반환하는 패턴은 좀 복잡한 편입니다.
함수를 반환하는 함수의 예제를 더 보고싶다면 자바스크립트 웹 개발 프레임워크로 널리 쓰이는 익스프레스(Express)Koa 같은 미들웨어 패키지를 살펴보십시오.
미들웨어는 대개 함수를 반환하는 함수 형태로 만들어집니다.