Conceptly
← 전체 목록
⬆️

Higher-Order Function

함수 활용함수를 받거나 돌려주는 함수

고차 함수는 함수를 인자로 받거나 결과로 반환하는 함수입니다. 일반 함수는 숫자나 문자열 같은 값을 입력받아 값을 돌려주지만, 고차 함수는 동작 자체를 입력받거나 새 동작을 만들어 돌려줍니다. 덕분에 '어떤 일을 할지'의 뼈대는 공통으로 두고, 그 안에서 실제로 수행할 세부 동작만 바깥에서 주입할 수 있습니다.

아키텍처 다이어그램

🔍 구조 다이어그램

점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다

왜 필요한가요?

배열의 모든 숫자에 2를 곱하는 코드와 모든 이름 앞에 인사말을 붙이는 코드를 따로 쓰다 보면, 반복문 구조는 똑같은데 가운데 한 줄만 다르다는 사실을 깨닫게 됩니다. 필터링 로직, 합계 계산, 이벤트 처리까지 전부 비슷한 패턴이 반복됩니다. 공통 구조를 함수로 묶고 싶지만, 그 안에서 해야 할 '구체적 작업'이 경우마다 달라지니 인자로 값만 받아서는 충분하지 않습니다. 이 작업 자체를 인자로 전달할 수 있어야 공통 구조와 구체 동작을 진짜로 분리할 수 있습니다.

왜 이런 방식이 등장했나요?

고차 함수는 Lisp와 ML 같은 함수형 언어의 초기부터 중심 아이디어였고, 이후 JavaScript, Python, Ruby, Scala, Kotlin 같은 언어가 배열 메서드 형태로 대중화했습니다. 특히 JavaScript의 map, filter, reduce가 2000년대 후반부터 표준이 되면서 반복문을 직접 쓰는 스타일에서 변환 함수를 넘기는 스타일로 빠르게 옮겨 갔습니다. React 같은 라이브러리도 컴포넌트를 함수로 취급하고 고차 컴포넌트(HOC) 패턴을 만들어 공통 로직을 함수로 감싸는 방식을 일상화시켰습니다. 고차 함수가 표준이 된 이유는 간결함뿐 아니라, 공통 구조에서 세부 동작을 떼어낼 수 있다는 근본적인 설계 이점 때문입니다.

내부적으로 어떻게 동작하나요?

고차 함수는 두 가지 방향에서 작동합니다. 첫째, 함수를 인자로 받는 방향입니다. 예를 들어 `filter(list, predicate)`는 리스트를 순회하는 뼈대만 가지고 있고, 각 원소를 남길지 버릴지는 넘겨받은 predicate 함수가 결정합니다. 같은 filter 함수로 짝수만 거르거나 특정 사용자만 거를 수 있는 이유입니다. 둘째, 함수를 반환하는 방향입니다. 설정 값을 미리 받아 '그 설정을 적용하는 함수'를 만들어 주면, 호출하는 쪽은 단순한 함수 하나만 들고 다니면 됩니다. `makeValidator(minLength)`가 검증 함수를 반환하는 식입니다. 두 방향 모두 공통 구조와 구체 동작의 경계를 함수 단위로 나눈다는 점에서 같습니다.

코드로 보면

함수를 받는 고차 함수

// filter는 조건 함수를 받는 고차 함수
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);  // [2, 4]
const big = numbers.filter(n => n > 3);          // [4, 5]

// 같은 filter 구조, 다른 조건 함수
// '순회하면서 조건에 맞는 것만 남긴다'는 뼈대만 filter가 가지고 있음

filter는 배열 순회와 결과 수집이라는 공통 뼈대만 가지고, '어떤 원소를 남길지' 결정은 인자로 받은 함수에 맡깁니다.

함수를 반환하는 고차 함수

// 검증 함수를 만들어 주는 고차 함수
function makeMinLengthValidator(min) {
  return (value) => value.length >= min;
}

const isLongEnough = makeMinLengthValidator(8);
isLongEnough("password");  // true
isLongEnough("short");     // false

// 같은 구조로 다른 검증기 양산
const isPassword = makeMinLengthValidator(8);
const isUsername = makeMinLengthValidator(3);

makeMinLengthValidator는 기준 길이를 받아 '그 기준에 맞는 검증 함수'를 만들어 반환합니다. 설정을 고정한 새 함수를 찍어내는 공장 역할입니다.

경계와 구분

고차 함수와 일급 함수는 자주 같이 쓰이지만 성격이 다릅니다. 일급 함수는 '함수가 값이다'라는 언어의 성질이고, 고차 함수는 그 성질을 이용해 함수를 받거나 반환하는 함수 유형입니다. 일급 함수 없이는 고차 함수를 만들 수 없고, 일급 함수가 있다고 자동으로 고차 함수가 만들어지는 것도 아닙니다. 고차 함수와 함수 합성도 구분하면 좋습니다. 고차 함수는 '함수를 다루는 함수'라는 넓은 범주이고, 함수 합성은 그 중 '여러 함수를 순서대로 이어 하나의 함수로 만드는' 특정 패턴입니다. 함수 합성은 고차 함수의 한 사례입니다.

트레이드오프

Gain 반복되는 구조를 한 번만 구현해 두고, 달라지는 동작만 함수로 갈아 끼울 수 있어 중복이 줄고 재사용성이 커집니다. Cost 함수를 인자로 넘기고 다시 함수를 반환하는 흐름이 많아지면 실제로 무엇이 언제 실행되는지 따라가기가 어려워집니다. 추상화가 너무 빨라지면 코드가 짧아져도 읽는 비용은 오히려 커질 수 있습니다. Decision Scale 같은 패턴이 여러 번 반복되고 바뀌는 부분이 분명할 때는 고차 함수가 유리합니다. 반대로 한두 군데만 다른 단순 분기라면 조건문이나 이름 있는 보조 함수를 두는 편이 더 읽기 쉽습니다.

언제 쓰나요?

고차 함수는 배열 처리, 이벤트 핸들링, 비동기 콜백, 미들웨어 파이프라인, 데코레이터 패턴처럼 공통 구조는 같고 안쪽 동작만 바뀌는 자리에서 자주 등장합니다. React 훅은 함수를 받아 렌더링 이후 동작을 조율하고, Express나 Koa의 미들웨어는 요청 처리 흐름에 공통 단계를 끼워 넣습니다. 실무에서는 검증기 팩토리, 로깅 래퍼, 재시도 래퍼처럼 기존 동작을 감싸 새 동작을 만들어 내는 패턴으로 자주 만납니다.

배열 변환이벤트 핸들러 등록데코레이터동작 생성기