First-Class Function
일급 함수는 언어가 함수를 다른 값과 똑같이 취급한다는 성질입니다. 숫자나 문자열처럼 함수를 변수에 담고, 인자로 넘기고, 반환 값으로 돌려받고, 배열이나 객체에 넣을 수 있습니다. 이것 자체는 함수 정의 방식이 아니라 '함수가 값이다'라는 언어 차원의 약속입니다. 이 약속이 있어야 고차 함수, 콜백, 함수 합성 같은 함수형 패턴이 전부 성립합니다.
▶아키텍처 다이어그램
🔗 관계 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
어떤 언어에서는 함수가 특별한 문법 구조일 뿐 값으로 다룰 수 없습니다. 함수를 변수에 담을 수 없으니 실행 시점에 동작을 바꾸려면 거대한 switch 문이나 조건 분기를 써야 했고, 정렬 기준이나 이벤트 처리 방식을 바꾸려면 함수 자체를 만들 게 아니라 매번 다른 함수 이름을 호출해야 했습니다. 공통 로직을 추출해 재사용하려 해도, 그 '공통 로직'에 동작 일부를 전달할 방법이 없으니 똑같은 구조가 여러 곳에 복사됐습니다. 함수를 값처럼 다룰 수 없으면 추상화의 도구가 매우 제한됩니다.
초기 명령형 언어들은 함수를 '실행되는 블록'으로만 취급했습니다. 포인터로 함수 주소를 넘기는 방식이 있었지만 일반 값과 같은 문법으로 다루기는 어려웠습니다. Lisp 계열 언어는 처음부터 함수를 값으로 보는 설계를 택했고, 이 발상이 수십 년 뒤 JavaScript, Python, Ruby 같은 주류 언어로 퍼졌습니다. 특히 JavaScript가 웹 전역에서 쓰이면서 이벤트 처리, 비동기 콜백, 배열 메서드 같은 맥락에서 함수를 인자로 넘기는 스타일이 일상이 됐습니다. 오늘날 대부분의 모던 언어는 일급 함수를 기본 제공하며, 이것이 없으면 고차 함수와 함수형 스타일 전체가 성립하지 않습니다.
일급 함수는 특별한 메커니즘이라기보다 언어의 타입 시스템이 함수를 값의 한 종류로 인정하는 것입니다. JavaScript에서 `const greet = function(name) { return 'Hi ' + name; }`라고 쓰면 greet은 함수를 가리키는 변수이고, 다른 변수처럼 재할당하거나 배열에 넣거나 객체 속성으로 저장할 수 있습니다. 함수를 인자로 받는 다른 함수에 넘기면 그 함수 안에서 호출할 수 있고, 함수가 함수를 반환하면 호출 결과가 새로운 동작 단위가 됩니다. 이 유연함 덕분에 '무엇을 할지'를 호출 시점에 결정하는 동적 행동 조합이 가능해집니다.
함수를 값으로 다루기
// 변수에 할당
const add = (a, b) => a + b;
// 인자로 전달
function apply(fn, x, y) {
return fn(x, y);
}
apply(add, 2, 3); // 5
// 반환 값으로 돌려줌
function makeMultiplier(n) {
return (x) => x * n;
}
const double = makeMultiplier(2);
double(5); // 10
// 배열, 객체에 담기
const handlers = {
click: (e) => console.log('clicked'),
hover: (e) => console.log('hovered')
};
handlers.click(event);add는 변수에 담긴 함수이고, apply는 함수를 인자로 받아 호출합니다. makeMultiplier는 함수를 만들어 반환하며, handlers는 함수를 객체 값으로 담고 있습니다. 네 경우 모두 함수가 다른 값과 똑같이 다뤄집니다.
일급 함수와 고차 함수는 서로 붙어 다니지만 층위가 다릅니다. 일급 함수는 언어가 제공하는 성질입니다. '함수를 값처럼 쓸 수 있다'는 가능성입니다. 고차 함수는 그 성질을 실제로 활용하는 함수의 유형입니다. 함수를 인자로 받거나 반환하는 함수를 말합니다. 일급 함수가 없는 언어에서는 고차 함수를 만들 수 없고, 일급 함수가 있어도 그것을 쓰지 않으면 고차 함수는 등장하지 않습니다. 일급 함수는 재료이고, 고차 함수는 그 재료로 만든 패턴입니다.
일급 함수는 따로 '도입'하는 기능이 아니라 JavaScript, Python, Kotlin, Swift, Dart 같은 모던 언어를 쓰는 순간 이미 깔려 있는 전제입니다. 이벤트 리스너에 함수를 등록하고, 배열 메서드에 콜백을 넘기고, Promise의 then에 완료 처리 함수를 연결하는 모든 패턴이 일급 함수를 활용합니다. 설계 관점에서는 '동작을 값으로 주고받을 수 있다'는 감각이 중요합니다. if-else로 분기하던 로직을 함수 맵(dispatch table)으로 바꾸거나, 비슷한 구조의 함수를 공통 추상화로 뽑아내는 일이 자연스러워집니다. Flutter, React 개발에서 위젯이나 컴포넌트에 빌더 함수를 넘기는 패턴도 일급 함수가 있기 때문에 가능합니다.