일급 객체와 고차 함수
💡 Key Concepts
일급 (First-class)
: 어떤 값을 변수에 저장하고, 함수의 인자로 전달하거나 함수의 결과로 반환할 수 있음을 의미한다.자바스크립트에서 함수가 일급 객체인 이유
: 함수를 일반 값처럼 변수에 할당하고, 다른 함수의 인자나 반환 값으로 사용할 수 있기 때문이다.고차 함수
: 다른 함수를 인자로 받거나, 함수를 결과로 반환하는 함수다.어플리케이티브(Applicative) 프로그래밍
: 함수를 값으로 다루고, 필요한 시점에 특정 인자를 적용해 실행하는 프로그래밍 스타일이다.클로저
: 함수가 내부 함수를 생성해 반환할 때, 반환된 함수가 자신이 생성될 당시의 외부 환경(변수)을 기억하는 개념이다.
이 개념들은 함수를 일급 값으로 다룰 때, 고차 함수와 클로저를 통해 함수형 프로그래밍의 기반을 이룬다.
평가 (Evaluation)
코드가 실행되어 값을 만들어내는 과정
함수형 프로그래밍에서 함수를 값으로 다룬다는 것은, 단순히 함수를 전달할 수 있다는 의미에 그치지 않는다.
이는 곧, 함수의 평가 시점을 코드로 다룰 수 있다는 뜻이다.
즉, 실행 시점을 로직이 아닌 구조로 표현할 수 있게 된다.
함수형 프로그래밍에서 Evaluation이 중요한 이유
대표적인 평가 전략:
- 즉시 평가(Eager Evaluation): 값이 필요하지 않아도 즉시 계산
- 지연 평가(Lazy Evaluation): 값이 실제로 필요한 시점에 계산
이러한 전략들은 평가 시점을 다루며, 함수형 프로그래밍의 동작 방식과 성능에 큰 영향을 준다.
🧩 Evaluation
평가(Evaluation)는 프로그래밍에서 표현식이 실제로 실행되어 값(value)으로 바뀌는 전 과정을 포괄하는 용어이다. 단순히 수를 더하는 ‘계산(calculation)’보다 더 넓은 개념이기 때문에, 개발자들은 ‘평가’라는 표현을 쓴다.
| 구분 | 계산 (Calculation) | 평가 (Evaluation) |
|---|---|---|
| 의미 | 수학적인 연산을 수행해 숫자 결과를 얻는 것 | 표현식(expression)이 실행되어 값(value)으로 바뀌는 과정 전체 |
| 예시 | 2 + 3 = 5 | x + 3 → x가 2일 때 → 2 + 3 → 5 |
| 범위 | 숫자 연산 중심 | 변수 참조, 함수 호출, 조건문 분기 등도 포함 |
즉, 평가는 단순한 수학 연산을 넘어, 코드의 표현식을 실제로 실행해 최종 값을 만들어내는 과정 전체를 의미한다.
const x = 10;
const y = x * 2 + 1;x * 2 + 1;이라는 표현식이 평가되어21이라는 값을 만들어 낸다.
이 전체 과정을 Evaluation이라고 부른다.
일급 객체 (First-Class Object)
일급 객체란 프로그래밍 언어에서 다른 값들과 동등하게 취급되는 객체를 말한다.
const a = 10;
// 함수도 값처럼 변수에 할당할 수 있다.
const add10 = (a) => a + 10;
// 함수는 다른 함수에 인자로 전달할 수 있고
add10(a);
// 함수의 실행 결과도 값이 되어 변수에 담을 수 있다.
const r = add10(a);
console.log(r); // 20일급 객체의 조건:
- 값으로 다룰 수 있다.
- 변수에 담을 수 있다.
- 함수의 인자로 사용될 수 있다.
- 함수의 결과로 사용될 수 있다.
자바스크립트에서는 함수가 일급 객체이기 때문에, 고차 함수의 인자나 반환값으로 자유롭게 사용할 수 있다.
일급 함수 (First-Class Function)
자바스크립트에서 함수는 일급 객체다.
즉, 숫자나 문자열처럼 함수를 값으로 다룰 수 있으며, 이를 통해 조합성과 추상화의 도구로 활용할 수 있다.
앞서 일급 객체는 일반적인 개념이었다면, 일급 함수는 자바스크립트에서 함수가 일급 객체로 동작하는 구체적인 예시를 보여준다.
일급 함수의 특징
1. 값으로 다룰 수 있다.
함수 자체를 값처럼 다루어 출력할 수 있다.
const add10 = (a) => a + 10;
console.log(add10); // (a) => a + 102. 변수에 담을 수 있다.
함수를 변수에 저장해 필요할 때 호출할 수 있다.
const add10 = (a) => a + 10;
console.log(add10(10)); // 203. 함수의 인자로 사용할 수 있다.
함수를 다른 함수의 인자로 전달할 수 있다.run은 전달받은 함수 f를 값 v에 적용하여 실행하는 고차 함수이다.
const add10 = (a) => a + 10;
const run = (f, v) => f(v);
console.log(run(add10, 5)); // 154. 함수의 결과로 사용할 수 있다
함수를 반환하고, 반환된 함수를 나중에 실행할 수 있다.
const makeFunc = () => () => 1;
const f1 = makeFunc();
console.log(f1); // () => 1
console.log(f1()); // 1요약
- 함수는 값처럼 변수에 저장, 인자로 전달, 결과로 반환될 수 있다.
- 이러한 성질 덕분에 조합성과 추상화가 가능해진다.
- 함수형 프로그래밍은 바로 이 일급 함수 특성을 기반으로 한다.
고차 함수 (Higher-Order Functions)
고차 함수란 함수를 값으로 다루는 함수를 말한다.
- 함수를 인자로 받거나
- 함수를 반환하는 함수
이러한 함수들은 반복, 조건, 실행 시점 같은 패턴을 함수로 추상화하여 코드를 더 재사용 가능하고 선언적으로 만든다.
1. 함수를 인자로 받아서 실행하는 고차 함수
아래 예제는 어떤 함수를 실행할지를 외부에서 결정하도록 만든 고차 함수다.
const apply1 = (f) => f(1);
const add2 = (a) => a + 2;
console.log(apply1(add2)); // 3
// === add2(1)
// === ((a) => a + 2)(1)
console.log(apply1((a) => a - 1)); // 0
// === ((a) => a - 1)(1)apply1은 함수f를 받아서, 그 함수에1을 적용해 실행한다.apply1은 함수의 내부 로직에 관심이 없고, 오직 언제, 어떤 값으로 실행할지만 책임진다.- 즉, 실행 로직과 실행 대상(함수)을 분리한 구조다.
실행할 동작을 함수로 외부에서 주입받는 패턴
2. 반복 패턴을 추상화한 고차 함수
다음 예제는 반복이라는 패턴 자체를 함수로 추상화한 고차 함수다.
const times = (f, n) => {
let i = -1;
while (++i < n) f(i);
};
times(console.log, 3);
// 0
// 1
// 2
times((a) => console.log(a + 10), 3);
// 10
// 11
// 12times는 얼마나 반복할지n무엇을 실행할지f를 분리한다.- 반복 제어는
times가 담당하고, 실제 실행할 동작은 함수로 외부에서 전달된다.
// 내부에서 실제로 일어나는 일
f(0);
f(1);
f(2);- 이 구조 덕분에 반복 방식은 고정하고, 행동만 바꿀 수 있다.
🧩 어플리케이티브(Applicative) 프로그래밍
함수를 값으로 다루고, 필요한 시점에 특정 인자를 적용해 실행하는 방식은 어플리케이티브 스타일의 프로그래밍이라고 부른다.
3. 함수를 만들어 반환하는 함수 (클로저)
아래 예제는 함수를 만들어 반환하는 고차 함수다.
// 1. addMaker(10) 실행
const addMaker = (a) => (b) => a + b;
const add10 = addMaker(10);
// 2. 이 시점에 a = 10인 환경이 생성됨
// 3. addMaker의 실행은 끝났지만, 반환된 함수는 a를 여전히 참조할 수 있음
console.log(add10(5)); // 15
// add10 내부: (b) => a + b
// a는 이미 사라진 addMaker의 변수지만 클로저 덕분에 여전히 10을 기억하고 있음클로저란?
- 함수가 자신이 생성될 당시의 렉시컬 환경(Lexical Environment)을 기억하는 것
- 외부 함수의 실행이 끝나도, 내부 함수는 외부 변수에 접근할 수 있다.
addMaker는 함수를 반환하는 고차 함수이고, 반환된 함수는 외부 변수a를 기억하는 클로저다
이렇게 함수 + 함수가 만들어질 당시의 환경을 함께 기억하는 것을 클로저(Closure)라고 부른다.
고차 함수와 클로저의 활용 예시
이 개념들은 이론에 그치지 않고, 실제 개발에서 자주 사용된다.
1. 배열 메서드 (고차 함수)
const numbers = [1, 2, 3, 4, 5];
// map: 함수를 인자로 받아 각 요소를 변환
const doubled = numbers.map((n) => n * 2);
// filter: 함수를 인자로 받아 조건에 맞는 요소만 선택
const evens = numbers.filter((n) => n % 2 === 0);
// reduce: 함수를 인자로 받아 누적 연산
const sum = numbers.reduce((acc, n) => acc + n, 0);2. React Hook과 클로저
function Counter() {
const [count, setCount] = useState(0);
// handleClick은 count를 기억하는 클로저
const handleClick = () => {
console.log(count); // 현재 count 값을 기억
setCount(count + 1);
};
return <div>Count: {count}</div>;
}// useEffect의 의존성 배열과 클로저
useEffect(() => {
// 이 함수는 count를 기억하는 클로저
console.log(count);
}, [count]); // count가 변경될 때마다 새로운 클로저 생성3. 커스텀 Hook (함수를 반환)
// 함수를 반환하는 고차 함수 패턴
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
// toggle 함수는 value를 기억하는 클로저
const toggle = () => setValue(!value);
return [value, toggle];
}
// 사용
function App() {
const [isOpen, toggleOpen] = useToggle();
return <div>{isOpen ? 'Open' : 'Closed'}</div>;
}4. 이벤트 핸들러 팩토리
// 함수를 반환하는 고차 함수
function createHandler(id) {
return () => {
console.log(`Clicked item ${id}`);
};
}
function List() {
const items = [1, 2, 3];
return (
<ul>
{items.map((id) => (
<li key={id} onClick={createHandler(id)}>
Item {id}
</li>
))}
</ul>
);
}이처럼 고차 함수와 클로저는 React 개발에서 핵심적인 개념으로, 상태 관리, 이벤트 처리, Hook 구현 등 다양한 곳에서 활용된다.