자바스크립트는 함수가 일급 객체(First-Class Citizen)이므로 함수형 프로그래밍 스타일을 자연스럽게 적용할 수 있습니다. 면접에서 "순수 함수가 뭔가요?"라는 질문이 나오면, 사이드 이펙트와 참조 투명성을 언급할 수 있어야 합니다.

순수 함수 (Pure Function)

같은 입력에 항상 같은 출력을 반환하고, 사이드 이펙트가 없는 함수입니다.

JS
// 순수 함수
function add(a, b) {
  return a + b;
}

function getFullName(firstName, lastName) {
  return `${firstName} ${lastName}`;
}

// 비순수 함수 — 외부 상태에 의존
let count = 0;
function increment() {
  count++; // 외부 변수 변경 (사이드 이펙트)
  return count;
}

// 비순수 함수 — 매번 다른 결과
function getRandom() {
  return Math.random(); // 같은 입력인데 다른 출력
}

// 비순수 → 순수로 변환
function addToArray(arr, item) {
  return [...arr, item]; // 원본 변경 없이 새 배열 반환
}

순수 함수의 장점

  • **예측 가능 **: 같은 입력 → 같은 출력
  • ** 테스트 용이 **: 외부 의존성 없음
  • ** 캐싱 가능 **: 메모이제이션 적용 가능
  • ** 병렬 처리 안전 **: 공유 상태 없음

커링 (Currying)

여러 인자를 받는 함수를 단일 인자 함수의 연쇄로 변환합니다.

JS
// 일반 함수
function multiply(a, b) {
  return a * b;
}

// 커링된 함수
function curriedMultiply(a) {
  return function (b) {
    return a * b;
  };
}

// 화살표 함수로 더 간결하게
const curriedMultiply2 = (a) => (b) => a * b;

const double = curriedMultiply2(2);
const triple = curriedMultiply2(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

범용 커링 함수

JS
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    }
    return (...moreArgs) => curried(...args, ...moreArgs);
  };
}

const add = curry((a, b, c) => a + b + c);
add(1)(2)(3);    // 6
add(1, 2)(3);    // 6
add(1)(2, 3);    // 6
add(1, 2, 3);    // 6

실전 활용

JS
// 로그 함수 커링
const log = curry((level, prefix, message) => {
  console.log(`[${level}] ${prefix}: ${message}`);
});

const errorLog = log("ERROR");
const apiError = errorLog("API");
apiError("요청 실패"); // "[ERROR] API: 요청 실패"

// 이벤트 핸들러
const handleEvent = curry((type, handler, event) => {
  if (event.type === type) handler(event);
});

const handleClick = handleEvent("click");

함수 합성 (Composition)

여러 함수를 조합하여 새 함수를 만듭니다.

JS
// compose — 오른쪽에서 왼쪽으로 실행
const compose = (...fns) => (x) =>
  fns.reduceRight((acc, fn) => fn(acc), x);

// pipe — 왼쪽에서 오른쪽으로 실행 (더 직관적)
const pipe = (...fns) => (x) =>
  fns.reduce((acc, fn) => fn(acc), x);

// 사용
const trim = (str) => str.trim();
const toLowerCase = (str) => str.toLowerCase();
const split = (sep) => (str) => str.split(sep);

const processInput = pipe(trim, toLowerCase, split(" "));
processInput("  Hello World  "); // ["hello", "world"]

// 데이터 변환 파이프라인
const processUsers = pipe(
  (users) => users.filter((u) => u.active),
  (users) => users.map((u) => u.name),
  (names) => names.sort()
);

고차 함수 (Higher-Order Function)

함수를 인자로 받거나 함수를 반환하는 함수입니다.

JS
// 함수를 반환
function withLogging(fn) {
  return function (...args) {
    console.log(`호출: ${fn.name}(${args.join(", ")})`);
    const result = fn(...args);
    console.log(`결과: ${result}`);
    return result;
  };
}

const loggedAdd = withLogging((a, b) => a + b);
loggedAdd(2, 3);
// 호출: (2, 3)
// 결과: 5

// 메모이제이션
function memoize(fn) {
  const cache = new Map();
  return function (...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const fibonacci = memoize((n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
});

모나드 맛보기 — Maybe

null/undefined를 안전하게 처리하는 패턴입니다.

JS
class Maybe {
  constructor(value) {
    this.value = value;
  }

  static of(value) {
    return new Maybe(value);
  }

  map(fn) {
    return this.value == null ? this : Maybe.of(fn(this.value));
  }

  flatMap(fn) {
    return this.value == null ? this : fn(this.value);
  }

  getOrElse(defaultValue) {
    return this.value ?? defaultValue;
  }
}

// null 안전한 체이닝
const userName = Maybe.of(user)
  .map((u) => u.profile)
  .map((p) => p.name)
  .map((n) => n.toUpperCase())
  .getOrElse("익명");
// user가 null이어도 에러 없이 "익명" 반환

함수형 vs 명령형

JS
// 명령형 — HOW를 기술
function getAdultNames(users) {
  const result = [];
  for (let i = 0; i < users.length; i++) {
    if (users[i].age >= 18) {
      result.push(users[i].name.toUpperCase());
    }
  }
  return result;
}

// 함수형 — WHAT을 기술
const getAdultNames = (users) =>
  users
    .filter((u) => u.age >= 18)
    .map((u) => u.name.toUpperCase());

**기억하기 **: 순수 함수는 "같은 입력, 같은 출력, 사이드 이펙트 없음"입니다. 커링은 인자를 하나씩 받아 재사용 가능한 함수를 만들고, 합성(pipe/compose)은 작은 함수를 조합해 복잡한 로직을 구성합니다. 자바스크립트에서는 map, filter, reduce가 이미 함수형 프로그래밍입니다.

댓글 로딩 중...