[JS] 커링(Currying)이란? 장점과 실전 사용 예제 살펴보기

2022. 8. 7. 14:23Javascript

728x90

이번 포스트에서는 많은 개발자들이 헷갈려하는 커링(currying) 기법에 대해 우선적으로 알아보고 어떻게 하면 잘 활용할 수 있는지 알아보도록 하겠습니다. 

 


커링(Currying) 이란?

일단 정의부터 시작을 해보자면 Currying이라는 단어에 따로 뜻이 있는 건 아니고 해당 기법을 발전시킨 수학자 하스켈 커리로부터 유래했다고 합니다. 프로그래밍 세계에서 커링을 요약해보자면 아래와 같이 요약을 할 수 있습니다.

 

  • 인자를 여러 개 받는 함수를 분리하여, 인자를 하나씩만 받는 함수로 만드는 방법
  • 함수형 프로그래밍 기법 중 하나로 함수를 재사용하고 리팩트링하기 쉽게 하는 방법

 

 

특정 언어의 경우 커링이 내부적으로 구현이 되어 있기도 하지만 다중 패러다임 언어인 자바스크립트는 커링이 따로 내장되어 있지는 않기 때문에 필요에 따라 구현해 사용할 수 있습니다.

 


 

일단 이해를 돕기 위해 커링에 대해 설명할 때 가장 많이 나오는 기초 예제부터 한번 살펴보도록 하겠습니다.

 

// 커링 적용전 곱셈함수
const multiply_v1 = (a, b, c) => {
    return a * b * c;
}

// 커링 적용한 곱셈함수
const multiply_v2 = (a) => (b) => (c) => {
    return a * b * c;
}

 

위 예제의 첫 번째 함수는 커링을 적용하기 전에 3개의 인자를 받아 곱셈 연산을 하고 있고 두 번째 함수는 커링을 적용해 인자를 하나씩만 받는 함수로 변형해 사용하고 있습니다. 

 

화살표 함수를 사용하지 않고 함수 선언문으로 두 번째 함수를 작성해보면 아래와 같이 작성할 수 있습니다

 

function multiply_v2(a) {
    return function (b) {
        return function (c)  {
            return a * b * c
        }
    }
}

// 호출: multiple_v2(2)(3)(4) = 24

 

보시다시피 첫 번째 함수처럼 a, b, c를 곱한 결괏값을 바로 반환하지 않고 새로운 인자 b와 c를 순차적으로 요구하는 익명 함수를 반환하게 됩니다. 가장 내부에 있는 함수가 반환되어 호출되었을 때 비로소 첫 번째 함수와 같이 3 인자를 곱한 결괏값을 반환하게 됩니다.

 

해당 예제를 보면서 첫 번째 함수가 훨씬 이해하기 쉽고 직관적인데 왜 굳이 커링을 사용하는지 의아해하시는 분들도 계실 겁니다. 물론 함수 선언 방식에 있어서는 필자도 이에 동의하지만 커링은 해당 함수를 호출하고 재사용할 때 진가를 발휘합니다. 

 

예를 들어서 장바구니에 담긴 물건들의 가격을 계산한다고 가정을 해보겠습니다.

커링을 적용하기 전 multiple_v1 함수로는 아래와 같이 작성할 수 있습니다.

 

const VAT = 1.05; // 5% VAT

// a -> 부과세, b -> 가격, c-> 갯수

const shirts_price = multiply_v1(VAT, 20000, 4);
const pants_price = multiply_v1(VAT, 15000, 4);

 

이제 커링을 적용한 함수를 사용했을 때는 어떤 식으로 사용 가능한지 확인해보겠습니다.

 

const VAT = 1.05;
const multiply_VAT = multiply_v2(VAT); // 부가세가 첫번째 인자로 전달된 함수를 multply_VAT 변수에 선언

const shirts_price = multiply_VAT(20000)(4);
const pants_price = multiply_VAT(15000)(2);

 

커링을 사용하지 않았을 경우 VAT값은 고정값임에도 불구하고 매번 전달해줘야 했지만 커링을 적용한 뒤에는 부가세가 인자로 전달된 multiply_VAT 함수를 가격과 수량만 별도의 인자로 전달하여 호출해주었습니다. 현재는 인자의 개수가 애초에 읽기 힘들 정도로 많은 정도가 아니기 때문에 큰 변화라 느껴지진 않을 수는 있지만 추후에 인자가 많아지거나 재사용의 필요성이 느껴질 경우 커링 기법은 이와 같이 많은 도움이 될 수 있습니다.

 


Currying(커링)으로 OnClick 핸들러 작성하기

 

실전에서 적용할 수 있는 한 가지 예시를 더 들어보도록 하겠습니다. 위에 적용했던 방식처럼 커링 기법을 통해 이벤트 핸들러를 좀 더 깔끔하게 작성할 수가 있습니다.

 

우선 기존의 이벤트 핸들러가 작동하는 방식부터 보도록 하겠습니다.

 

// 인자가 없을경우
<button onClick={handleClick}>전송</button>

// 인자가 있을경우 (익명 함수로 전달)
<button onClick={() => handleClick(value)}>전송</button>

 

인자가 없을 경우 그냥 핸들러 함수(handleClick)만 정의한 뒤 넘겨주기만 하면 되지만, 인자가 있을 경우는 함수가 바로 호출되지 않도록 익명 함수로 전달을 해줘야 합니다.

 

익명 함수로 전달하는 방법에 문제가 있는 건 아니지만 아래와 같이 handleClick함수에 커링 기법을 사용하면 익명 함수를 전달해줄 필요가 없게 되어 좀 더 깔끔하게 작성이 가능하게 됩니다.

 

const handleClick = (value) => () => {  console.log(value) };
<button onClick={handleClick(value)}>전송</button>

지금까지 커링에 대해 알아보았는데요. 언뜻 보면 어려운 개념 같지만 실제로는 아주 간단하고 함수 프로그래밍에 기초가 되는 기법이 아닐까라고 생각이 듭니다. 이 외에도 혹시 본인만의 커링 사용법 같은 게 있다면 댓글에 남겨주시길 바랍니다.