하얀 코딩

[JavaScript - 16] 고차 함수 / 콜백 함수 본문

JavaScript

[JavaScript - 16] 고차 함수 / 콜백 함수

whitecoding 2023. 1. 11. 19:25

이해하기

고차 함수(higher order function)는 함수를 전달인자(argument)로 받을 수 있고, 함수를 리턴할 수 있는 함수입니다.

함수는 변수에 저장할 수 있습니다. 그리고 함수는, 함수를 담은 변수를 전달인자로 받을 수 있습니다.

마찬가지로, 함수 내부에서 변수에 함수를 할당할 수 있습니다. 그리고 함수는 이 변수를 리턴할 수 있습니다.

여기서 변수에 할당하지 않고 함수를 바로 이용할 수 있습니다.

어떤 고차 함수에 함수를 전달인자로 전달하고, 고차 함수는 함수 자체를 리턴합니다. 변수가 빠졌을 뿐, 동일하게 동작합니다.

 

이때 다른 함수(caller)의 전달인자(argument)로 전달되는 함수를 콜백 함수(callback function)라고 합니다.

어떤 작업이 완료되었을 때 호출하는 경우가 많아서, 답신 전화를 뜻하는 콜백 함수라는 이름이 붙여졌습니다.

 

콜백 함수를 전달받은 고차 함수(caller)는, 함수 내부에서 이 콜백 함수를 호출(invoke)할 수 있고, 조건에 따라 콜백 함수의 실행 여부를 결정할 수도 있습니다. 아예 호출하지 않을 수도 있고, 여러 번 실행할 수도 있습니다. 특정 작업의 완료 후에 호출하는 경우는 이후에 충분히 접할 수 있습니다.

 

'함수를 리턴하는 함수'는 모양새가 특이한 만큼, 부르는 용어가 따로 있습니다. '함수를 리턴하는 함수'를 고안해 낸 논리학자 하스켈 커리(Haskell Curry)의 이름을 따, 커링 함수라고 합니다. 따로 커링 함수라는 용어를 사용하는 경우에는, 고차 함수라는 용어를 '함수를 전달인자로 받는 함수'에만 한정해 사용하기도 합니다. 그러나 정확하게 구분하자면, 고차 함수가 커링 함수를 포함합니다. 이번 유닛부터는 '함수를 리턴하는 함수'와 '함수를 전달인자로 받는 함수' 모두, 고차 함수로 사용합니다.

 


1. 다른 함수를 인자로 받는 경우

function double(num) {
  return num * 2;
}

function doubleNum(func, num) {
  return func(num);
}

/*
 * 함수 doubleNum은 다른 함수를 인자로 받는 고차 함수입니다.
 * 함수 doubleNum의 첫 번째 인자 func에 함수가 들어올 경우
 * 함수 func는 함수 doubleNum의 콜백 함수입니다.
 * 아래와 같은 경우, 함수 double은 함수 doubleNum의 콜백 함수입니다.
 */
let output = doubleNum(double, 4);
console.log(output); // -> 8

2. 함수를 리턴하는 경우

function adder(added) {
  return function (num) {
    return num + added;
  };
}

/*
 * 함수 adder는 다른 함수를 리턴하는 고차 함수입니다.
 * adder는 인자 한 개를 입력받아서 함수(익명 함수)를 리턴합니다.
 * 리턴되는 익명 함수는 인자 한 개를 받아서 added와 더한 값을 리턴합니다.
 */

// adder(5)는 함수이므로 함수 호출 연산자 '()'를 사용할 수 있습니다.
let output = adder(5)(3); // -> 8
console.log(output); // -> 8

// adder가 리턴하는 함수를 변수에 저장할 수 있습니다.
// javascript에서 함수는 일급 객체이기 때문입니다.
const add3 = adder(3);
output = add3(2);
console.log(output); // -> 5

3. 함수를 인자로 받고, 함수를 리턴하는 경우

function double(num) {
  return num * 2;
}

function doubleAdder(added, func) {
  const doubled = func(added);
  return function (num) {
    return num + doubled;
  };
}

/*
 * 함수 doubleAdder는 고차 함수입니다.
 * 함수 doubleAdder의 인자 func는 함수 doubleAdder의 콜백 함수입니다.
 * 함수 double은 함수 doubleAdder의 콜백으로 전달되었습니다.
 */

// doubleAdder(5, double)는 함수이므로 함수 호출 기호 '()'를 사용할 수 있습니다.
doubleAdder(5, double)(3); // -> 13

// doubleAdder가 리턴하는 함수를 변수에 저장할 수 있습니다. (일급 객체)
const addTwice3 = doubleAdder(3, double);
addTwice3(2); // --> 8

고차 함수를 사용하는 이유

추상화(abstraction)

 

  • 추상화란 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것이다.
  • 일상생활에서 추상화가 아닌 것을 찾아보기 힘들다.
  • 버스, 지하철을 타기위한 교통카드, 브라우저 사이트 주소창, 입력창과 메시지 등..
  • 자바스크립트 포함한 많은 프로그래밍 언어 또한 추상화의 결과이다.

추상화 = 생산성(productivity)의 향상

 

  • CPU는 0과 1만 이해한다. 코드가 해석되는 복잡한 것들은 자바스크립트 엔진이 대신해준다.
  • 우리는 자바스크립트 문법(syntax)을 올바르게 사용하는 것만으로, 다양한 프로그램을 비교적 쉽게 작성할 수 있다.
  • 이처럼 고민거리가 줄어들고, 문제 해결이 더 쉬워지는 것이 추상화의 이점이다.

함수라는 추상화

 

  • 프로그램 작성 시 반복되는 로직은 별도의 함수로 작성하는 것 역시 추상화의 좋은 사례이다.
  • 추상화의 관점에서 함수를 바라보면, 함수는 사고(thougth) 또는 논리(logic)의 묶음이다.

고차 함수는 함수 추상화를 한 단계 높인 것

 

  • 함수를 통해 얻은 추상화를 한 단계 높인 것이 고차 함수이다.
  • 함수 = 값을 전달받아 값을 리턴한다. = 값에 대한 복잡한 로직은 감춰있다. = 값 수준에서의 추상화
  • 고차 함수는 이 추상화의 수준을 사고의 추상화 수준으로 끌어올린다.
  • 값 수준의 추상화: 단순히 값(value)을 전달받아 처리하는 수준
  • 사고의 추상화: 함수(사고의 묶음)을 전달받아 처리하는 수준
  • 다시 말하자면, 고차 함수를 통해 보다 높은 수준(higher order)에서 생각할 수 있다.
  • 고차함수 = 함수를 전달받거나 함수를 리턴한다. = 사고(함수)에 대한 복잡한 로직은 감춰있다. = 사고 수준에서의 추상화
  • 추상화의 수준이 높아진 만큼 생산성도 높아진다.