JavaScript

[JavaScript] 고차함수와 콜백함수, 퍼스트 클래스

Gray Park 2020. 8. 16. 10:00
728x90
반응형

어려운 표현은 최대한 필터링하고, 아주 쉽게 이해할 수 있도록 설명하자. 기본적으로 우리가 아는 함수는 변수를 파라미터로 받거나, 받지 않을 수 있다. 또, 파라미터를 받더라도 안쓰는 경우도 있다. 물론 이런 경우라면 파라미터를 굳이 받을 필요가 없겠지만.

 

그렇다면, 파라미터로 받아오는 변수는 무엇을 담고 있을까?

 

퍼스트 클래스 ( First Class Citizen )

고차 함수는 파라미터로 함수를 받아온다. 머리가 아프다. 파라미터로는 변수만 오는 거 아니었어? 아래의 예시를 보자

const a = 1; // 변수 a에 숫자 1을 할당한다.
const b = [1, 2, 3]; // 변수 b에 배열 [1, 2, 3]을 할당한다
const c = { key: 'value' }; // 변수 c에 객체 { key: 'value' }를 할당한다
const d = function () { return 0; } // 변수 d에 0을 리턴하는 함수를 할당한다

우리는 함수 표현식을 사용해 알게 모르게 이미 함수를 변수에 할당하고 있었다. 그러니 당연하게도 함수는 변수에 담긴 채 파라미터로 전달이 가능하다. 그리고 우리는 함수를 퍼스트 클래스 시티즌 ( First Class Citizen ) 이라고 부른다. 또 어려운 말이 나온다. 일급객체? 퍼스트클래스? 그게 뭐야. 

 

쉽게 이해하는 게 내 블로깅의 목적이다. 그냥 모든 권리를 가지고 있는 걸 퍼스트 클래스라고 한다.

비행기 탈 때 보면 퍼스트 클래스 라운지가 따로 있고, 퍼스트 클래스 이용객이면 퍼스트 클래스 라운지는 물론, 비즈니스 클래스 라운지를 포함한 공항의 거의 모든 시설을 이용하는 데에 제약이 없다.

( staff only 만 빼고. 그건 여기 나오는 퍼스트 클래스인 함수도 안된다 )

그럼 더 쉽게 이해하자. 퍼스트 클래스가 있다면, 비즈니스 클래스나 이코노미도 있지않을까?

간단한 예로 문자열을 할당받은 변수는 다른 문자열을 할당받지 못한다. 문자열은 절대 퍼스트클래스가 될 수 없는 거다.

 

그럼 변수를 선언할 때 사용하는 const는 어떨까?

const는 하나의 변수에 한 번밖에 할당을 할 수가 없다. 하지만 그렇다고 해서 변수가 퍼스트 클래스를 담지 못하는 것은 아니다.

머리가 어지러워지기 시작한다. 더 쉽게 이해하자. 클래스를 나누는 기준은 변수가 아니다.

 

타입을 기준으로 생각해보자.

함수는 타입인가? ---> YES

변수는 타입인가? ---> NO

함수는 퍼스트 클래스인가? ---> YES

변수는 퍼스트 클래스인가? --->NO

클래스는 타입이 함수인가? ---> YES

타입이 함수이면, 퍼스트 클래스인가? ---> YES

 

이제 부정할 수 없는 무적의 삼단논법으로 아주 쉽게 이해를 해보자.

전제 1. 클래스는 함수이다.

전제 2. 함수는 퍼스트 클래스이다.

( A는 B이고 B가 C이면, A는 C이다 )

클래스는 퍼스트 클래스이다.

 

전제 1. 변수에 함수를 담으면 변수는 함수이다.

전제 2. 함수는 퍼스트 클래스이다.

함수를 담고있는 변수는 퍼스트 클래스이다.

 

이 정도 이해했으면 이미 퍼스트 클래스를 구분할 줄 아는 것이고, 구분할 줄 알면 개념의 이해가 훨씬 쉬워진다.

 

위키피디아 : 컴퓨터 프로그래밍 언어 디자인에서, 일급 객체(영어: first-class object)란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다. 보통 함수에 매개변수로 넘기기, 수정하기, 변수에 대입하기와 같은 연산을 지원할 때 일급 객체라고 한다.

고차함수 ( High Order Function )

퍼스트 클래스가 무엇인지 알아보았다. 함수는 퍼스트 클래스이다. 퍼스트 클래스는 변수에 대입할 수 있고, 다른 함수에 매개변수 ( 파라미터 ) 로 전달할 수 있으며, 마찬가지로 매개변수로 다른 함수에 들어갈 수 있다.

여기까지 이해가 되면, 이미 고차 함수를 알고 있는 거다. 이 블로그를 앞에서 부터 찬찬히 살펴보았다면, 이미 고차 함수를 본 적 있다.

배열의 메소드를 설명하면서 함수를 인자로 받는다는 개념이 이해가 되지 않는다는 부분이 있다. 여기에서 등장하는 개념이라 그랬던 거다.

우리는 함수를 변수에 저장할 수 있으므로, 함수를 품고있는 변수를 파라미터의 형태로 다른 함수에 넣어줄 수 있다. 같은 말이 맴맴 도니까 아래의 예시를 보자.

const IsLargerThanThree = function (x) {
  return x > 3;
}

let arr = [1, 2, 3, 4, 5];
let filteredArr = arr.filter(IsLargerThanThree);
console.log(filteredArr);
// [4, 5]

인증

IsLargerThanThree 라는 변수는 파라미터로 x를 받아 x가 3보다 크면 true, 아니면 false를 반환하는 함수이다.

배열 메소드 filter() 는 배열의 요소를 하나씩 차례대로 입력받아 괄호() 안에 입력해준 함수를 실행한 결과가 true인 요소만 리턴한다.

이 때 배열 메소드인 함수 filter()는 고차함수이다. 당연히, 우리가 알고 있는 map, reduce 등 함수를 넣어 실행하는 모든 메소드는 고차함수이다.

 

콜백함수 ( Callback Function )

그리고 이제 대망의 콜백함수이다. 사실 오늘 설명한 거 중에 가장 이해하기 쉬운 친구가 바로 이 콜백함수이다. 하나씩 구분 지어놓으면 더 명확하다. 우리는 위의 예시에서, filter() 메소드의 괄호() 안에 IsLargerThanThree()라는 함수를 넣어주었다. 여기서 filter의 괄호() 안으로 들어간 IsLargerThanThree 라는 함수가 콜백함수이다.

 

더더 쉽게 예시를 보자.

// 들어온 파라미터에 1을 더해 리턴하는 함수 
const addOne = function (param) { // param은 parameter의 준말
  return param + 1;
}

// 함수를 파라미터로 전달받는 고차함수
const addOneAddOneMore = function (func, param) {
// 함수 func는 파라미터로 입력되어 리턴될 때 실행되었다
  return func(param) + 1; // 파라미터로 들어온 함수를 실행하고 1을 더해 리턴
}

const result = addOneAddOneMore ( addOne, 1 );
console.log(result);
// 3 // param === 1 + addOne(1) === 1 + 1 = 3

위의 예시에서 addOne 함수는 파라미터에 1을 더해 리턴하는 함수이다. 아직까진 콜백함수라고 불리지 않는다.

addOneAddOneMore 이라는 함수는 파라미터로 함수와 하나의 값을 받는 고차함수이다. // 이해 안되면 바로 위의 고차함수를 다시 보자. 그래도 이해가 안될 땐 댓글 각

addOneAddOneMore 에 파라미터로 들어가는 함수. 여기에서는 addOneAddOneMore 이 실행될 때 들어가는 addOne 함수를 콜백함수라고 부른다.

콜백함수라고 부르는 이유는 간단하다. 우리는 부재중 전화가 찍히면 상대방에게 다시 전화를 건다 ( Callback 한다 )

함수가 아닌, 퍼스트 클래스가 아닌 값을 담고 있는 변수가 내 집 안으로 들어올 때에는 신경도 안썼지만, 같은 수준의 함수가 집 안으로 들어왔는데 무시하면 그건 예의가 아니다. 그래서 사회생활 차 콜백해야 한다. 그래서 콜백함수다. 고차함수는 함수를 인자로 받고, 인자로 받는 이 함수를 콜백함수라고 부른다. 그리고 일반적으로 콜백함수는 고차함수 내에서 그 역할을 다한다. 그렇다. 함수끼리의 세계는 결국 사회생활인 거다. 이사한 김에 집자랑 하려고 인스타에 누구나 놀러오라고 올렸더니 부장님이 찾아온거다. 무시할 수 있음 해보시지

 

 

최대한 쉽게 설명을 하려고 했다. 시간이 너무 늦었다. 말이 짧다. 생각은 더 짧다. 좋은 밤 보내시길.

728x90
반응형