함수형 프로그래밍 (Functional Programming)

한눈에 보기

함수형 프로그래밍은 계산을 상태 변경이 아닌 함수의 평가로 바라보는 패러다임이다. 순수 함수와 부수 효과, 불변성, 고차 함수, 명령형과의 비교, 리액트 등 실무 적용 사례를 정리한다.

최종 업데이트 2026.09.17

함수형 프로그래밍(Functional Programming)은 계산을 상태 변경이 아니라 수학적 함수의 평가로 바라보는 프로그래밍 패러다임이다. 함수형 프로그래밍은 부수 효과를 최소화한 순수 함수와 불변 데이터를 기본 단위로 삼아, 프로그램을 작은 함수들의 조합으로 구성한다. 하스켈(Haskell) 같은 순수 함수형 언어만의 이야기가 아니라, 자바스크립트(JavaScript), 자바(Java), 코틀린(Kotlin), 파이썬(Python) 등 주류 언어 대부분이 함수형 기법을 흡수해 실무에서 일상적으로 쓰이고 있다.

핵심 개념: 순수 함수와 부수 효과

순수 함수(Pure Function)는 두 가지 조건을 만족하는 함수다. 첫째, 같은 입력에 대해 항상 같은 출력을 반환한다. 둘째, 반환값을 만드는 일 외에 바깥세상에 아무 영향을 주지 않는다. 전역 변수 수정, 파일 쓰기, 네트워크 호출, 콘솔 출력처럼 함수 밖에서 관찰 가능한 변화를 부수 효과(Side Effect)라고 부르며, 순수 함수에는 이것이 없다.

// 순수하지 않은 함수: 외부 상태에 의존하고 이를 변경한다
let total = 0;
function addToTotal(x) {
  total += x;
  return total;
}

// 순수 함수: 입력만으로 출력이 결정된다
function add(a, b) {
  return a + b;
}

순수 함수는 참조 투명성(Referential Transparency)을 가진다. 함수 호출을 그 결과값으로 바꿔 놓아도 프로그램의 의미가 변하지 않는다는 뜻이다. 이 성질 덕분에 테스트가 쉬워지고, 결과를 캐싱하는 메모이제이션(Memoization)이 안전해지며, 실행 순서를 바꾸거나 병렬화해도 결과가 같다는 보장이 생긴다. 물론 실용적인 프로그램은 입출력 없이 존재할 수 없으므로, 함수형 프로그래밍의 실제 전략은 부수 효과를 없애는 것이 아니라 프로그램의 가장자리로 몰아넣고 핵심 로직을 순수하게 유지하는 것이다.

불변성과 일급 함수

불변성(Immutability)은 한 번 만든 데이터를 변경하지 않는 원칙이다. 값을 바꿔야 하면 원본을 수정하는 대신 바뀐 부분을 반영한 새 값을 만든다. 데이터가 변하지 않으면 어떤 함수가 언제 값을 바꿨는지 추적할 필요가 없어지고, 여러 스레드가 같은 데이터를 읽어도 경쟁 조건(Race Condition)이 발생하지 않는다. 복사 비용이 걱정될 수 있지만, 함수형 언어의 영속 자료구조(Persistent Data Structure)는 변경되지 않은 부분을 공유하는 방식으로 이 비용을 크게 줄인다.

일급 함수(First-Class Function)는 함수를 값처럼 다룰 수 있다는 성질이다. 함수를 변수에 담고, 인자로 넘기고, 반환값으로 돌려줄 수 있다. 함수를 인자로 받거나 반환하는 함수를 고차 함수(Higher-Order Function)라고 하며, map, filter, reduce가 대표적이다.

const orders = [
  { status: "PAID", amount: 12000 },
  { status: "CANCELLED", amount: 5000 },
  { status: "PAID", amount: 30000 },
];

// 반복문과 임시 변수 대신 변환의 조합으로 표현한다
const paidTotal = orders
  .filter(order => order.status === "PAID")
  .map(order => order.amount)
  .reduce((sum, amount) => sum + amount, 0);
// 42000

이 스타일은 어떻게 순회할지가 아니라 무엇을 얻고 싶은지를 서술하므로 선언형(Declarative)이라고 부른다. 그 밖에 여러 인자를 받는 함수를 인자 하나씩 받는 함수들의 연쇄로 바꾸는 커링(Currying), 작은 함수들을 이어 붙여 새 함수를 만드는 함수 합성(Function Composition), 필요한 시점까지 계산을 미루는 지연 평가(Lazy Evaluation)도 함수형의 주요 도구다.

명령형 프로그래밍과의 비교

구분명령형, 객체지향 중심함수형 중심
계산 모델상태를 순차적으로 변경식을 평가해 값을 생성
데이터가변 상태가 기본불변 값이 기본
코드 스타일절차를 서술하는 명령형결과를 서술하는 선언형
재사용 단위객체와 클래스작고 합성 가능한 함수
동시성락으로 공유 상태를 보호불변성으로 공유 문제 자체를 회피
주의점상태 추적이 어려워질 수 있음추상화에 익숙해지는 학습 비용

두 패러다임은 배타적이지 않다. 객체지향으로 큰 구조를 잡고 각 메서드 내부와 데이터 변환 로직은 함수형으로 쓰는 혼합 스타일이 현대 실무의 표준에 가깝다.

실무에서 만나는 함수형

함수형 개념은 이미 일상 도구에 스며들어 있다. 리액트(React)는 UI를 상태에 대한 순수 함수로 보는 관점 위에 서 있고, 상태 변경을 불변 업데이트로 표현해야 변경 감지가 올바로 동작한다. 리덕스(Redux)의 리듀서는 이전 상태와 액션을 받아 새 상태를 반환하는 순수 함수여야 한다. 자바는 8부터 람다와 스트림(Stream) API로 컬렉션 처리를 선언형으로 쓸 수 있게 했고, 코틀린과 스위프트(Swift)는 불변 선언과 고차 함수를 언어 차원에서 권장한다. 대용량 데이터 처리 프레임워크인 스파크(Spark)의 변환 연산이나 맵리듀스(MapReduce) 모델도 순수 함수와 불변 데이터라는 함수형 전제 위에서 분산 처리를 안전하게 수행한다. 다만 모든 것을 함수형으로 밀어붙이는 것이 목표는 아니다. 지나친 추상화는 가독성을 해치고, 성능이 극도로 중요한 구간에서는 지역적인 가변 상태가 더 적절할 수 있다. 핵심 비즈니스 로직을 순수하게 유지해 테스트 가능성을 확보하는 것이 실무적 타협점이다.

값이 없을 수 있는 상황을 다루는 방식에서도 함수형의 영향이 보인다. 널(null) 검사를 흩뿌리는 대신 자바의 Optional이나 코틀린의 널 허용 타입처럼 값의 부재를 타입으로 표현하면, 부재를 처리하지 않은 코드를 컴파일러가 잡아낸다. 실패할 수 있는 계산을 값으로 감싸 연쇄적으로 합성하는 이런 구조를 함수형 언어에서는 모나드(Monad)라는 개념으로 일반화하는데, 실무에서는 이름보다 패턴 자체, 즉 예외와 널 대신 결과를 값으로 다루는 습관이 중요하다.

면접에서 이렇게 물어본다

  • 순수 함수의 조건을 말하고, 순수 함수가 테스트와 동시성에 유리한 이유를 설명해 보라.
  • 부수 효과를 완전히 없앨 수 없다면 함수형 프로그래밍은 이를 어떻게 다루는가.
  • map, filter, reduce를 사용한 코드가 반복문보다 나은 점과 주의할 점을 말해 보라.
  • 불변성이 리액트 같은 프레임워크에서 왜 중요한지 설명해 보라.
  • 참조 투명성이란 무엇이며 어떤 최적화를 가능하게 하는가.
소프트웨어 공학 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기