13.6 변수로서의 함수
프로그래밍을 처음 공부한다면 커피도 한 잔 새로 따르고 잠시 일어나서 기분전환을 하는 게 좋을 수도 있습니다.
이 섹션에서 다루는 내용은 초보자들이 종종 헤매는 부분이지만, 꼭 이해해야 하는 중요한 개념입니다.
숫자나 문자열, 배열은 변수라고 생각해도 별 거부감이 없습니다.
변수는 데이터라는 생각, 배열이나 객체는 데이터의 모임이라는 생각은 익숙하니까요.
하지만 변수를 이렇게 이해하면 함수의 잠재력을 완전히 깨닫기 어려울 수 있습니다.
함수도 다른 변수와 마찬가지로 이리저리 전달할 수 있다는 사실을 떠올리기 어렵기 때문입니다.
함수는 능동적인 것이므로, 우리가 보통 수동적이라고 생각하는 데이터와 연결이 잘 되지 않을 수 있습니다.
물론 함수는 호출되었을 때는 능동적입니다.
하지만 호출하기 전에는 다른 변수와 마찬가지로 수동적입니다.
이해를 돕는 비유를 하나 소개하겠습니다.
슈퍼마켓에 과일을 사러 간다고 합시다.
이때 과일은 데이터라고 생각할 수 있습니다.
그런데 사과와 바나나를 넣어 스무디를 만들고 싶어져서, 블렌더도 하나 사기로 했습니다.
블렌더는 함수라고 생각할 수 있습니다.
과일을 넣으면 스무디를 만드는 능동적인 것이니까요.
하지만 블렌더는 전원을 연결하고 과일을 넣기 전에는, 과일과 마찬가지로 하나의 물건일 뿐입니다.
카트에서 꺼내 계산대에 올리고, 결제하고, 쇼핑백에 넣어 집으로 가져옵니다.
과일과 다를 게 하나도 없습니다.
블렌더가 과일과 달라질 때는 전원을 연결하고 과일을 넣었을 때뿐입니다.
자, 변수가 있을 수 있는 곳에는 함수도 있을 수 있습니다.
그럼 이 사실은 무엇을 의미할까요?
간추려 보면, 다음과 같은 일을 할 수 있다는 뜻입니다.
- 함수를 가리키는 변수를 만들어 별명을 정할 수 있습니다.
- 배열에 함수를 넣을 수 있습니다. 물론 다른 타입의 데이터와 섞일 수 있습니다.
- 함수를 객체의 프로퍼티로 사용할 수 있습니다(9장에서 설명했습니다).
- 함수를 함수에 전달할 수 있습니다.
- 함수가 함수를 반환할 수 있습니다.
- 함수를 매개변수로 받는 함수를 반환하는 것도 물론 가능합니다.
머리가 핑핑 도나요?
이렇게 써 놓으니 너무 추상적으로 보이고, 대체 왜 이런 복잡한 일이 필요한 것인가 의아할 수도 있습니다.
하지만 이런 유연성은 정말 강력한 특징이고, 매우 자주 사용되기도 합니다.
우선 가장 이해하기 쉬운 것, 함수에 별명을 붙이는 것부터 생각해 봅시다.
짧은 코드 안에서 여러 번 호출해야 하는 함수가 있습니다.
그런데 이 함수의 이름이 너무 길어서 타이핑하기 번거로울 뿐 아니라, 코드를 읽기도 무척 어려울 것 같습니다.
함수도 데이터이므로 짧은 이름의 변수에 저장할 수 있습니다.
function addThreeSquareAddFiveTakeSquareRoot(x) {
// 설마 이런 이름을 짓지는 않겠지만...
return Math.sqrt(Math.pow(x+3, 2)+5);
}
// 별명을 쓰기 전
const answer = (addThreeSquareAddFiveTakeSquareRoot(5) +
addThreeSquareAddFiveTakeSquareRoot(2)) /
addThreeSquareAddFiveTakeSquareRoot(7);
// 별명을 쓰면 이렇게 바뀝니다.
const f = addThreeSquareAddFiveTakeSquareRoot;
const answer = (f(5) + f(2)) / f(7);
별명을 지을 때 addThreeSquareAddFiveTakeSquareRoot 뒤에 괄호를 붙이지 않았다는 점에 주목하십시오.
괄호를 붙이면 함수를 호출하고, f에는 addThreeSquareAddFiveTakeSquareRoot 자체가 아니라 호출 결과가 저장됩니다.
그러면 f를 f(5)처럼 함수로 사용하려 하면 에러가 일어납니다.
f는 함수가 아니고 호출할 수 있는 것은 함수뿐이니까요.
물론 이 예제는 완전히 인위적이고, 실제 이런 상황에 처할 일은 거의 없을 겁니다.
하지만 20장에서 배울 노드 개발에서 자주 쓰이는 네임스페이스에는 계속 쓰는 패턴입니다.
다음 예제를 보십시오.
const Money = require('math-money'); // require는 라이브러리를 불러오는
// 노드 함수입니다.
const oneDollar = Money.Dollar(1);
// Money.Dollar도 길게 느껴지면 이렇게 해도 됩니다.
const Dallar = Money.Dollar;
const twoDollars = Dollar(2);
// oneDollar와 twoDollars는 같은 타입의 인스턴스입니다.
여기서는 단순히 Money.Dollar
를 Dollar
로 줄였을 뿐 별명을 붙였다고 할 정도는 아닙니다.
하지만 이해하기 쉽고 코드 길이도 줄어드니 이렇게 할 이유는 충분합니다.
본격적인 문제에 앞서 정신적인 스트레칭을 했다고 할 수 있으니, 이제 더 추상적으로 생각해 봅시다.