본문으로 건너뛰기
김신건의 로그

일급 함수

· 수정 · 📖 약 2분 · 775자/단어 #javascript #functional-programming #fundamentals #first-class
first-class function, first class function, 일급함수, first-class object, 일급 객체, first class object

정의

일급 함수 (First-Class Function): 함수를 숫자, 문자열, 객체처럼 값으로 다룰 수 있는 언어의 특성.

다음 세 가지가 모두 가능하면 그 언어의 함수는 일급이다.

조건설명
변수에 할당함수를 변수나 데이터 구조에 저장const fn = function() {}
인자로 전달다른 함수의 매개변수로 함수를 넘김arr.map(fn)
반환값으로 사용함수가 함수를 돌려줌const add = a => b => a + b

일급 함수는 함수를 값처럼 다룰 수 있는 권한이고, 고차 함수 는 그 권한을 이용해 다른 함수를 인자로 받거나 반환하는 함수다. (모든 고차 함수는 일급 함수를 기반으로 만들어진다.)

사용 상황

  • 콜백 패턴: 이벤트 핸들러, 타이머, 비동기 처리
  • 고차 함수: map, filter, reduce, 커링, 데코레이터
  • 함수 합성 (composition): 여러 작은 함수를 조립해 복잡한 로직 구성
  • 의존성 주입: 테스트 가능한 코드 작성
  • 모듈화: 전략 패턴, 플러그인 시스템

시각화

언어별 일급 함수 지원 여부:

flowchart TD
    FCF["일급 함수 (First-Class Function)"]

    FCF --> JS["JavaScript / TypeScript"]
    FCF --> PY["Python"]
    FCF --> RB["Ruby"]
    FCF --> GO["Go"]
    FCF --> JAVA["Java (Java 8+, 람다)"]

    JS --> JS1["변수 할당 O"]
    JS --> JS2["인자 전달 O"]
    JS --> JS3["반환값 O"]
    JAVA --> J1["변수 할당 (Functional Interface) O"]
    JAVA --> J2["람다 인자 전달 O"]
    JAVA --> J3["함수형 인터페이스 반환 O"]

기본 사용법

1. 변수에 함수 할당

// 함수 표현식: 함수를 값으로 변수에 저장
const greet = function(name) {
  return `안녕, ${name}!`;
};

// 화살표 함수도 동일
const greetArrow = name => `안녕, ${name}!`;

// 배열에 함수 저장
const operations = [
  (a, b) => a + b,
  (a, b) => a - b,
  (a, b) => a * b,
];

operations[0](3, 2);  // 5
operations[1](3, 2);  // 1
operations[2](3, 2);  // 6

// 객체 프로퍼티로 저장
const math = {
  add: (a, b) => a + b,
  sub: (a, b) => a - b,
};
math.add(3, 4);  // 7

2. 함수를 인자로 전달

// 고차 함수: 함수를 매개변수로 받음
function applyTwice(fn, x) {
  return fn(fn(x));
}

const double = x => x * 2;
applyTwice(double, 3);  // 12 (3*2=6, 6*2=12)

// 배열 메서드: 콜백을 인자로 받음
[1, 2, 3].map(x => x * 2);        // [2, 4, 6]
[1, 2, 3].filter(x => x > 1);     // [2, 3]
[1, 2, 3].forEach(x => console.log(x));

// 이벤트 핸들러 등록
document.addEventListener('click', (e) => {
  console.log('클릭:', e.target);
});

// setTimeout
setTimeout(() => console.log('1초 후'), 1000);

3. 함수를 반환값으로 사용

// 함수를 반환하는 팩토리 함수
function makeAdder(n) {
  return function(x) {
    return x + n;
  };
}

const add10 = makeAdder(10);
const add20 = makeAdder(20);

add10(5);   // 15
add20(5);   // 25

// 커리드 화살표 함수
const multiply = a => b => a * b;
const double = multiply(2);
[1, 2, 3].map(double);  // [2, 4, 6]

실전 예시

전략 패턴 (Strategy Pattern)

// 정렬 전략을 함수로 주입
function sortBy(arr, compareFn) {
  return [...arr].sort(compareFn);
}

const people = [
  { name: '홍길동', age: 30 },
  { name: '이순신', age: 45 },
  { name: '강감찬', age: 28 },
];

const byAge  = (a, b) => a.age - b.age;
const byName = (a, b) => a.name.localeCompare(b.name);

sortBy(people, byAge);   // 나이 순
sortBy(people, byName);  // 이름 순

// 정렬 방향도 HOF 로 추상화
const descending = fn => (a, b) => fn(b, a);
sortBy(people, descending(byAge));  // 나이 역순

의존성 주입으로 테스트 가능한 코드

// fetcher 를 외부에서 주입 (테스트 시 Mock 으로 교체)
function createApiService(fetcher = fetch) {
  return {
    async getUser(id) {
      const res = await fetcher(`/users/${id}`);
      return res.json();
    },
    async createPost(data) {
      const res = await fetcher('/posts', {
        method: 'POST',
        body: JSON.stringify(data),
      });
      return res.json();
    },
  };
}

// 실제 사용
const api = createApiService();

// 테스트: fetch 를 Mock 으로 교체
const mockFetch = (url) => Promise.resolve({
  json: () => ({ id: 1, name: '테스트 유저' }),
});
const testApi = createApiService(mockFetch);

함수 합성으로 유효성 검사

const validators = [
  str => str.length >= 8 || '8자 이상이어야 합니다',
  str => /[A-Z]/.test(str) || '대문자가 포함되어야 합니다',
  str => /[0-9]/.test(str) || '숫자가 포함되어야 합니다',
];

function validate(value, rules) {
  return rules
    .map(rule => rule(value))
    .filter(result => typeof result === 'string');
}

validate('abc', validators);
// ['8자 이상이어야 합니다', '대문자가 포함되어야 합니다', '숫자가 포함되어야 합니다']
validate('Abc12345', validators);
// []

언어별 비교

언어변수 할당인자 전달반환값비고
JavaScriptOOO함수 자체가 일급 객체
PythonOOOdef / lambda 모두 일급
RubyOOOProc, lambda, 블록
GoOOO함수형 프로그래밍 지원
Java (8+)OOOFunctional Interface 필요
C (함수 포인터)OOO포인터 문법 필요, 클로저 없음

Python 비교

# 1. 변수에 함수 할당
double = lambda x: x * 2

# 2. 함수를 인자로 전달
result = list(map(double, [1, 2, 3]))  # [2, 4, 6]

# 3. 함수를 반환값으로 사용
def make_adder(n):
    return lambda x: x + n

add5 = make_adder(5)
add5(3)  # 8

Python 의 lambda 는 표현식만 허용. 여러 줄 함수는 def 후 반환해야 한다.

Java 비교

// Java 8+ 람다: Functional Interface 를 통해 일급 함수 흉내
import java.util.function.*;

// 1. 변수에 할당
Function<Integer, Integer> double_ = x -> x * 2;

// 2. 인자로 전달
List<Integer> result = List.of(1, 2, 3).stream()
    .map(double_)
    .collect(Collectors.toList());  // [2, 4, 6]

// 3. 함수를 반환
Function<Integer, Function<Integer, Integer>> add = a -> b -> a + b;
Function<Integer, Integer> add5 = add.apply(5);
add5.apply(3);  // 8

Java 는 함수가 일급 객체가 아니라 인터페이스 구현체다. 타입 명시가 장황해지는 단점이 있다.

함정

WARNING

function 선언문과 const fn = function 의 차이: 선언문은 호이스팅되어 정의 전에 호출 가능, 표현식은 TDZ 에 걸린다.

// ✅ 선언문: 호이스팅
console.log(add(2, 3));  // 5
function add(a, b) { return a + b; }

// ❌ 표현식: 정의 전 호출 불가
console.log(sub(2, 3));  // ReferenceError: Cannot access 'sub' before initialization
const sub = (a, b) => a - b;

WARNING

typeof class === 'function': 클래스도 함수처럼 취급되어 혼동 여지가 있다.

typeof function() {}  // 'function'
typeof (() => {})     // 'function'
typeof class Foo {}   // 'function'  (!)

클래스는 new 없이 호출하면 TypeError 가 발생하지만, typeof 는 구분하지 못한다.

CAUTION

함수를 배열/객체에 저장할 때 this 손실: 메서드를 꺼내서 콜백으로 사용하면 this 가 사라진다.

const obj = {
  value: 42,
  getValue() { return this.value; },
};

const fn = obj.getValue;
fn();            // undefined (this 손실)
fn.call(obj);   // 42 (명시적 바인딩)

// 화살표 함수로 래핑
const safeFn = () => obj.getValue();
safeFn();        // 42

관련 위키

이 글의 용어 (7개)
[Javascript] 콜백javascript
정의 콜백 (callback): 다른 함수의 인자로 넘겨져, 받은 쪽(caller)이 적절한 시점에 호출하는 함수. 특성이 있어야 가능한 패턴이다. JavaScript 에서는 함…
[Javascript] 클로저javascript
정의 클로저 (Closure): 함수가 자신이 정의된 시점의 외부 변수들을 계속 참조할 수 있는 성질. 함수가 일급 객체 () 인 언어의 자연스러운 결과. 메커니즘은 + 함수 객…
[Javascript] 호이스팅 (Hoisting)javascript
정의 호이스팅 (Hoisting) 은 JavaScript 엔진이 코드를 실행하기 전 바인딩 생성 단계 에서 스코프 내의 선언을 파악하고 등록하는 메커니즘이다. 마치 선언이 스코프…
[Javascript] Arrow Functionjavascript
정의 화살표 함수 (arrow function) 는 ES2015 (ES6) 에서 도입된 함수 표현식. 모양. 더 짧은 문법뿐 아니라 가 lexical 로 바인딩 되는 본질적 차이…
[Javascript] functionjavascript
정의 은 JavaScript 에서 함수를 만드는 가장 기본적인 키워드. 호이스팅, 동적 바인딩, 객체, 호출 등 과 구분되는 고유한 특성을 가진다. 함수가 값 으로 다뤄지는 의 …
[Javascript] this 바인딩javascript
정의 JavaScript 의 는 호출 방식에 따라 결정 되는 동적 바인딩. 4 가지 호출 방식 + arrow function (lexical) 규칙. 5 가지 규칙 | # | 규…
고차 함수javascript
정의 고차 함수 (Higher-Order Function, HOF): 다음 조건 중 하나 이상을 만족하는 함수. 1. 함수를 인자로 받는다. 2. 함수를 반환한다. 특성이 있어야…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기