일급 함수 I
- 10.1 코드의 냄새 — 함수 이름에 있는 암묵적 인자
- 10.2 리팩터링: 암묵적 인자를 드러내기
- 10.3 일급인 것과 일급이 아닌 것
- 10.4 리팩터링: 본문을 콜백으로 바꾸기
- 10.5 일급 함수가 주는 것
비슷한 함수가 여러 개 있고 이름만 다르다면, 그건 암묵적 인자가 함수 이름에 숨어 있다는 신호야.
파트 2가 시작돼. 파트 1이 액션/계산/데이터 분류와 계층형 설계였다면, 파트 2는 **일급 추상(first-class abstraction)**이야. 함수를 값처럼 다루면서 코드의 중복을 제거하고 추상화 수준을 높이는 기법이거든.
저자가 먼저 보여주는 건 **코드의 냄새(code smell)**야. 이런 코드를 봐봐:
function setPriceByName(cart, name, price) {
// ... price 필드를 설정
}
function setQuantityByName(cart, name, quantity) {
// ... quantity 필드를 설정
}
function setTaxByName(cart, name, tax) {
// ... tax 필드를 설정
}
패턴이 보이지? 함수 이름에 Price, Quantity, Tax라는 필드명이 박혀 있어. 함수 본문도 거의 같고, 다른 건 어떤 필드를 변경하느냐뿐이야. 이게 바로 "함수 이름에 있는 암묵적 인자(implicit argument in function name)" 냄새야. 필드명이 함수 이름에 "암묵적으로" 들어 있지, 실제 인자로는 전달되지 않고 있거든.
해결법은 간단해 — 암묵적 인자를 명시적 인자로 바꾸기.
// Before: 함수 이름에 필드명이 박혀 있음
function setPriceByName(cart, name, price) { /* ... */ }
function setQuantityByName(cart, name, quantity) { /* ... */ }
function setTaxByName(cart, name, tax) { /* ... */ }
// After: 필드명을 인자로 받음
function setFieldByName(cart, name, field, value) {
var item = cart[name];
var updated = objectSet(item, field, value);
return objectSet(cart, name, updated);
}
세 개의 함수가 하나로 합쳐졌어. field라는 인자가 추가되면서, "어떤 필드를 바꿀 것인가"가 암묵적 지식에서 명시적 인자로 바뀐 거야. 이 리팩터링이 가능한 이유가 있어 — 자바스크립트에서 **문자열은 일급(first-class)**이기 때문이야. 문자열을 변수에 저장하고, 함수에 전달하고, 함수에서 리턴할 수 있으니까 필드명을 인자로 넘길 수 있는 거지.
여기서 핵심 개념 — **일급(first-class)**의 정의. 어떤 것이 일급이라는 건 변수에 저장 가능, 함수의 인자로 전달 가능, 함수의 리턴값으로 사용 가능, 배열이나 객체에 담기 가능하다는 뜻이야. 자바스크립트에서 일급인 것들은 숫자, 문자열, 불리언, 배열, 객체, 그리고 함수(이게 핵심!)야. 일급이 아닌 것들은 +, - 같은 연산자, for, if 같은 키워드/구문, try/catch 같은 제어 구조야. 일급이 아닌 것은 함수에 인자로 넘길 수 없어. 하지만 연산자를 함수로 감싸면 일급으로 만들 수 있지 — function plus(a, b) { return a + b; } 이렇게.
이번에는 더 강력한 리팩터링 — 본문을 콜백으로 바꾸기(replace body with callback). 반복문 구조는 같고 본문만 다른 코드가 있으면, for 루프는 일급이 아니라서 인자로 넘길 수 없지만, 본문을 함수로 빼서 콜백으로 넘길 수 있어:
function forEach(array, f) {
for (var i = 0; i < array.length; i++) {
f(array[i]);
}
}
forEach(foods, function(food) {
cook(food);
eat(food);
});
forEach(dishes, function(dish) {
clean(dish);
putAway(dish);
});
for 루프라는 일급이 아닌 구문을 forEach라는 일급 함수로 감쌌어. 이제 "반복"이라는 개념을 재사용할 수 있게 됐지. 이 패턴의 핵심은 비슷한 코드에서 달라지는 부분을 찾고, 달라지는 부분을 **함수(콜백)**으로 빼내고, 달라지지 않는 부분을 고차 함수로 만드는 거야.
일급 함수를 활용하면 코드 중복 제거(비슷한 함수 10개를 고차 함수 1개로 대체), 더 높은 추상화("무엇을 할 것인가"와 "어떻게 할 것인가"를 분리), 새로운 기능 추가가 쉬움(기존 고차 함수에 새로운 콜백만 전달), API 표면적 축소(함수 개수가 줄어들면 배워야 할 것도 줄어듦)를 얻을 수 있어. 하지만 고차 함수를 과도하게 쓰면 코드가 오히려 읽기 어려워지니 적절한 수준을 유지해야 해.
정리
10장 읽고 기억할 거 세 가지:
- 함수 이름에 암묵적 인자가 보이면 명시적 인자로 드러내기. 비슷한 함수 여러 개가 하나로 합쳐진다
- 일급 = 변수에 저장, 인자로 전달, 리턴값으로 사용 가능. 자바스크립트에서는 함수도 일급
- 본문을 콜백으로 바꾸기 = 달라지는 부분을 함수로 빼서 고차 함수 만들기. 코드 중복의 핵심 해결책