JavaScript - 커링(Currying)

주세환·2024년 5월 20일
0

JavaScript

목록 보기
15/16
post-thumbnail

커링이란?

출처 - hustle-dev 벨로그

커링(Currying)은 함수의 재사용성을 높이기 위해 함수 자체를 return하는 함수이다.

쉽게 말하면, '함수를 반환하는 함수'이다.

함수를 하나만 사용할 때는 필요한 모든 파라미터를 한 번에 넣어야 한다. 커링을 사용하면 함수를 분리할 수 있으므로 파라미터도 나눠 전달할 수 있다.

  • 커링과 같이 함수 자체를 인자로 받거나 반환하는 함수를 고차 함수라고 부르기도 한다.

커링이 왜 사용될까??

  • 함수의 재활용을 위해서
    - 원하는 함수들을 조합해서 사용할 수 있다.

  • 하나 이상의 인수의 함수를, 하나의 인수를 받는 함수로 축소할 수 있다.
    - 그래서 더 가벼운 함수 제작이 가능하다.


커링

일반적인 커링

function add(a,b){
  return a+b;
}

다음 add 함수를 재활용 하려면?

function addTwo(a){
  return add(a,2)
}

이렇게 2+@를 해주는 addTwo 함수를 만들 수 있다.

만약 add3, add4, add5... 등의 여러 함수가 필요하다고 하자.

function addTwo(a){
  return add(a,2);
}
function addThree(a){
  return add(a,3);
}
function addFour(a){
  return add(a,4);
}

분명 add 함수를 재활용하긴 하지만, 뭔가 이상하다.

여기에 커링을 적용해보자.

function add(a,b){
    return console.log(a+b);
}

function addX(x){
    return function(a){
      return add(a, x);
    }
  }
  
  const addTwo = addX(2);
  const addThree = addX(3);
  const addFour = addX(4);

  addTwo(2); // 2 + 2 = 4
  addThree(5); // 3 + 5 = 8
  addFour(1); // 4 + 1 = 5

다음과 같이 addX 함수를 이용해서 여러 파생 함수들을 만들 수 있다.

addX 함수는 이렇게 동작한다.

  • 인자 x를 받아서 익명함수를 반환한다. 그 함수는 function(a){ return add(a,x); } 의 형태이다.

  • 반환받은 값인 함수는 인자 a를 요구한다. a를 넣어주면 add(a,x)의 실행 결과를 반환한다.

이처럼 함수 실행을 위한 인자를 한 번에 받지 않고, 여러 차례에 나눠서 받을 수 있다.

이 예시를 화살표 함수로 나타내면,

function addX(x){
  return function(a){
    return add(a, x);
  }
}

const addX = x => a => add(a,x);

이렇게 더 간단해진다.


함수를 인자로 받는 커링함수

자바스크립트에서는 함수도 값이다.

그러므로 함수의 인자로 또 다른 함수도 받을 수 있다.

function curry(fn){
  return function(a){
    return function(b){
      return fn(a,b);
    }
  }
}

이제 이 함수를 어떻게 써야할까?

정확히 위에서 선언했던 addX와 동일하게 만들 수 있다.

function add(a,b){
  return a+b;
}

const addX = curry(add);
const addTwo = addX(2);
console.log(addTwo(5));

함수 자체를 인자로 받아서 활용할 수 있는 것을 알게 되면, 코드를 작성하는 스타일이 더 다양해진다.


객체 데이터를 가져오는 커링

커링을 사용하지 않은 경우 :

const todos = [
  { id: 3, content: 'HTML', completed: false },
  { id: 2, content: 'CSS', completed: true },
  { id: 1, content: 'Javascript', completed: false}
];

const getTodosIdArr = todos => todos.map(todo => todo.id);
const getTodosContentArr = todos => todos.map(todo => todo.content);
const getTodosCompletedArr = todos => todos.map(todo => todo.completed)

console.log(getTodosIdArr(todos));			// [ 3, 2, 1 ]
console.log(getTodosContentArr(todos));		// [ 'HTML', 'CSS', 'Javascript' ]
console.log(getTodosCompletedArr(todos));	// [ false, true, false ]

일반적인 경우 코드를 작성하면 위와 같이 작성하게 된다. 여기에 커링을 적용해보자.

커링을 사용한 경우 :

const todos = [
    { id: 3, content: 'HTML', completed: false },
    { id: 2, content: 'CSS', completed: true },
    { id: 1, content: 'Javascript', completed: false }
];

const get = property => object => object[property];

const getId = get('id'); // object => object['id'];
const getContent = get('content'); // object => object['content'];
const getCompleted = get('completed'); // object => object['completed']

const getTodosIdArr = todos => todos.map(getId);
const getTodosContentArr = todos => todos.map(getContent);
const getTodosCompletedArr = todos => todos.map(getCompleted);

console.log(getTodosIdArr(todos)); // [ 3, 2, 1 ]
console.log(getTodosContentArr(todos)); // [ 'HTML', 'CSS', 'Javascript' ]
console.log(getTodosCompletedArr(todos)); // [ false, true, false ]

커링을 사용하는 경우 인자의 순서가 중요하다.
앞에 존재하는 인자일 수록 변동 가능성이 적고, 뒤에 있는 인자일 수록 변동 가능성이 높기 때문에 이 순서를 고려하여 설계하는 것이 중요하다고 한다.


커링 응용

function 키워드 없이 화살표 함수로 표현하여 방정식 4x^(x+2)를 수행할 함수를 만들어보자.

const add = (a,b) => a + b;
const multiply = (a,b) => a * b;

const addX = x => a => add(a, x);
const addTwo = addX(2);

const multiplyX = x => a => multiply(a, x);
const multiplyFour = multiplyX(4);

이렇게 커링을 이용해 addTwo, multiplyFour 함수를 만들었다.

const compose = fn => fn2 => x => fn2(x) * fn(x);
const equation = compose(addTwo)(multiplyFour);
equation(10) // (4 * 10) * (10 + 2) = 40 * 12 = 480

equation 함수에 넣는 값이 x가 되어 4x(x+2)의 다양한 값에 대한 결과를 받을 수 있다.

만약 5x(x+2)의 결과를 반환하는 함수가 필요하다면?

const multiplyFive = multiplyX(5);
const equaion2 = compose(addTwo)(multiplyFive);
equation2(10) // (5 * 10) * (10 + 2) = 600

이렇게 표현할 수 있다.

마지막으로 위에서 선언한 equation 함수를 생성하면서 중복되는 부분을 제거하면,

const addTwo = addX(2);
const addFour = addX(4);
const multiplyFour = multiplyX(4);
const multiplyFive = multiplyX(5);

const compose = fn => fn2 => x => fn2(x) * fn(x);
const composeAddTwo = compose(addTwo);

const equation1 = composeAddTwo(multiplyFour); // 4x(x+2)
const equation2 = composeAddTwo(multiplyFive); // 5x(x+2)
const equation3 = composeAddTwo(addFour) // (x+4)(x+2)

위와 같이 인자로 받은 함수들을 교체해주면서, 유사한 함수들을 여러개 만들 수 있다.

참고 - Go devlog, hustle-dev velog

0개의 댓글