chp. 10 일급 함수 I
이번 장에서는 일급 함수의 개념을 바탕으로, 코드 속 중복을 제거하고 추상화를 강화하는 리팩토링 기법을 알아봅니다.
1. 함수 이름에 있는 암묵적 인자를 명시적으로 바꾸기
아래 두 함수는 비슷한 패턴을 따릅니다.
function setPriceByName(cart, name, price) {
var item = cart[name];
var newItem = objectSet(item, "price", price);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
function setShippingByName(cart, name, ship) {
var item = cart[name];
var newItem = objectSet(item, "shipping", ship);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
function objectSet(object, key, value) {
var copy = Object.assign({}, object);
copy[key] = value;
return copy;
}차이점은 오직 필드명(price, shipping)뿐인데, 이 값이 함수 이름 속에 숨어 있습니다.
따라서 필드가 바뀔 때마다 새로운 함수가 필요해지고, 코드가 점점 늘어나는 구조가 됩니다.
1.1 리팩토링: 암묵적 인자 드러내기
리팩토링 절차는 다음과 같습니다.
- 함수 이름 속 암묵적 인자를 식별한다.
- 해당 값을 명시적인 함수 인자로 추가한다.
- 본문에 하드코딩된 값을 인자로 대체한다.
setPriceByName 리팩토링 예시
// 기존
function setPriceByName(cart, name, price) {
var item = cart[name];
var newItem = objectSet(item, "price", price);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
// 리팩토링 후
function setFieldByName(cart, name, field, value) {
var item = cart[name];
var newItem = objectSet(item, field, value);
var newCart = objectSet(cart, name, newItem);
return newCart;
}field는 함수 이름 속에 숨어 있던 암묵적 인자가 아니라, 명시적으로 다룰 수 있는 값이 됩니다.
1.2. 일급값
이 과정에서 객체의 속성이 단순히 코드 속 하드코딩된 문자열에서, 자유롭게 전달하고 조작할 수 있는 인자로 변경되었습니다.
이처럼 변수에 담고, 함수 인자로 전달하고, 반환할 수 있는 대상을 일급값이라고 합니다.
TIP
일급값의 특징
- 변수에 할당할 수 있다.
- 함수 인자로 전달할 수 있다.
- 함수 반환값으로 사용할 수 있다.
- 배열이나 객체에 담을 수 있다.
일급값이 아닌 것
+연산자if,for,try/catch같은 제어 구조
2. 함수 본문을 콜백으로 바꾸기
두 번째 리팩토링 방법은 코드의 앞뒤 흐름은 동일하지만, 중간 동작만 달라지는 경우에 유용합니다.
대표적으로 예외 처리, 반복문, 공통 로직이 해당합니다.
2.1. 에러 로깅 예시
프로젝트 전역에서 쓰일 수 있는 에러 로그를 전달하는 코드 예시입니다.
try {
// 앞 부분
saveUserData(user); // 본문
} catch (error) {
logToSnapErrors(error); // 뒷 부분
}이 패턴은 시스템이 커질수록 수백 번 반복됩니다.
본문을 콜백으로 바꾸는 리팩토링을 적용해보면,
function withLogging(f) {
try {
f();
} catch (error) {
logToSnapErrors(error);
}
}
withLogging(function () {
saveUserData(user);
});앞뒤 제어 흐름은 withLogging이 담당하고, 달라지는 부분만 콜백으로 전달한 것입니다.
2.2. 반복문 예시
반복문에서도 같은 원리를 적용할 수 있습니다.
// 요리하고 먹기
for (var i = 0; i < foods.length; i++) {
var food = foods[i];
cook(food);
eat(food);
}
// 설거지하기
for (var i = 0; i < dishes.length; i++) {
var dish = dishes[i];
wash(dish);
dry(dish);
putAway(dish);
}두 반복문은 구조는 같고, 본문만 다릅니다. 이를 forEach 같은 고차 함수로 바꾸면:
forEach(foods, function (food) {
cook(food);
eat(food);
});
forEach(dishes, function (dish) {
wash(dish);
dry(dish);
putAway(dish);
});공통적인 반복 제어는 forEach가 담당하고, 실제로 하고 싶은 일만 콜백으로 넘긴 구조입니다.
2.3. 얻는 이점
- 중복 제거: 제어 흐름은 한 번만 구현
- 가독성 향상: 함수 이름이 의도를 드러냄 (
withLogging,forEach) - 재사용성 증가: 새로운 동작을 콜백으로 전달하기만 하면 됨
- 버그 예방: 인덱스 처리, 예외 처리에서 발생할 실수를 줄임
- 확장성 확보:
map,filter,reduce같은 함수형 API로 발전 가능
정리하며
일급 함수는 단순히 “함수를 값처럼 쓸 수 있다”는 특성에 그치지 않습니다.
- 암묵적 인자 드러내기: 함수 이름 속에 숨어 있는 값을 인자로 꺼내, 중복을 줄이고 유연성을 높입니다.
- 본문을 콜백으로 바꾸기: 공통 흐름을 함수로 일반화하고, 달라지는 부분만 콜백으로 분리해 재사용성과 가독성을 확보합니다.
두 가지 리팩토링은 작은 코드 중복 제거에서 시작해, 점차 함수형 프로그래밍의 강력한 추상화 도구로 확장될 수 있습니다.