현재 "객체에서 함수로" 라는 책을 바탕으로 함수형 프로그래밍 스터디를 진행하고 있습니다.
처음 함수형 프로그래밍을 접하면서 느꼈던 함수형 프로그래밍만의 매력을 복기하며 정리하고자 합니다.
0. 함수란?
📌 수학적 관점
함수는 수학에서 유래한 개념으로, 특정 입력을 받아 출력으로 변환하는 규칙입니다. 수학적 함수는 입력과 출력 사이의 명확한 관계를 정의하며, 외부 환경에 의존하거나 영향을 주지 않습니다.
- 입력과 출력: 동일한 입력은 항상 동일한 출력 반환 (→ 순수성 / 순수 함수)
- 독립성: 외부 상태나 변수에 의존하지 않음
- 투명성: 함수 호출은 그 결과값으로 대체 가능 (→ 참조 투명성)
f(x, y) = x + y
f(2, 3) = 5
항상 입력값 x=2, y=3에 대해 동일하게 5를 반환합니다.
💻 프로그래밍 관점
프로그래밍에서 함수는 입력(매개변수)을 받아 출력(반환값)을 생성하거나 작업을 실행하는 독립적인 코드 블록입니다.
함수형 프로그래밍에서는 수학적 함수의 특성을 그대로 따르도록 함수를 설계합니다.
- 입력과 출력: 매개변수를 받아 결과 반환
- 재사용성: 동일한 로직을 여러 곳에서 호출 가능
- 순수성: 동일한 매개변수 → 항상 동일한 결과 반환 시 순수 함수
fun square(num: Int): Int = num * num
1. 함수형 프로그래밍(FP)의 기본 개념 - 순수함수와 참조투명성
순수 함수 : 동일한 입력에 대해 항상 동일한 출력을 반환하며, 외부 상태를 변경하지 않는 함수
참조 투명성 : 표현식이 그 값으로 대체되어도 프로그램의 동작이 변하지 않는 성질
✅ 순수 함수란?
순수 함수란 다음과 같은 특징을 갖는 함수입니다.
- 사이드 이펙트(side effect)가 없음 → 함수 실행이 외부 상태에 영향을 끼치지 않음
- 불변성(immutability) → 함수 내부에서 입력 값을 수정하지 않음
- 즉, 100번 같은 입력을 넣으면, 100번 같은 출력이 나오는 함수
- 반례: DB 커넥션, 파일 I/O, 네트워크 통신 등의 코드가 섞여 있다면, 그 함수는 비순수 함수
이해를 돕기 위해 비순수 함수의 예시로 findUserByEmail이라는 함수를 간단하게 만들어보겠습니다. (with. JPA)
fun findUserByEmail(email: String): User? {
return userRepository.findByEmail(email) // DB 조회 결과에 따라 출력 다름
}
위 코드는 사용자의 email 값을 파라미터로 전달받아 DB에서 일치하는 user가 있는지 찾는 비순수 함수입니다.
- 외부 상태 의존
- 이 함수는 DB에 저장된 데이터에 따라 결과가 달라집니다.
- DB에 유저가 존재할 경우 → User 반환 / 존재하지 않음 → null 반환
- 참조 투명성 위배
- findUserByEmail("testuser@example.com") 이 항상 같은 결과를 반환하지 않음
- DB 상태가 변하면 동일한 호출이어도 다른 결과가 나올 수 있음
→ 같은 입력이라도 출력이 달라짐 → 참조 투명하지 않음
만약 testuser@example.com를 사용하는 유저가 2명이라면?, 아예 없다면? DB 커넥션 풀에 문제가 생겨 에러를 반환한다면?
이처럼 DB 조회와 같이 외부 환경에 의존하는 함수는 외부 환경에 따라 같은 입력값이라도 다른 값을 출력할 가능성이 있습니다.
이런 함수는 순수하지 않으며, 참조 투명성을 만족하지 않기 때문에 함수형 프로그래밍의 핵심 개념에서 벗어납니다.
함수형 프로그래밍에서는 가능한 위와 같은 부수 효과를 격리하고,핵심 로직은 순수 함수로 구성하여 예측 가능성과 안정성을 확보하는 것이 중요합니다.

✅ 참조 투명성이란?
이와 같이 함수형 프로그래밍(Function Programming, FP)은 순수 함수에 기반한 수학적 함수의 개념에서 출발하여, 코드의 예측 가능성과 유지보수성을 향상시키는 데 중점을 둔 프로그래밍 패러다임입니다.
이를 종합했을 때 함수형 프로그래밍에서 가장 중요한 개념으로 귀결되는 것이 바로 참조 투명성입니다.
책 "객체에서 함수로"에서는 참조 투명성에 대해 이렇게 설명합니다.
"참조 투명성은 매우 간단하지만 강력한 개념이다.
코드 내에서 어떤 식을 그와 동등한 값으로 바꿔도 프로그램의 행동이 변하지 않는다면 그 코드는 참조 투명하며, 따라서 함수형이다."
참조 투명성은 어떤 표현식(코드 조각)을 그 표현식이 계산된 결과값으로 바꿔도 프로그램의 전체 동작이 달라지지 않는 성질을 말합니다.
쉽게 말해, 특정 함수 호출이나 값을 다른 곳에서 동일한 결과로 대체해도 프로그램이 똑같이 작동해야 한다는 뜻입니다.
✅ 참조 투명한 함수 예시
fun square(num: Int): Int = num * num
- square(3)은 항상 9를 반환합니다.
- 외부 상태에 의존하지 않기 때문에 순수 함수입니다.
- 코드에서 square(3)을 9로 바꿔도 프로그램의 전체 동작은 변하지 않습니다.
→ 따라서 이 함수는 참조 투명하다고 할 수 있습니다.
❌ 참조 투명하지 않은 함수 예시
var counter = 0
fun increment(): Int {
counter += 1
return counter
}
- 이 함수는 호출할 때마다 counter 값을 변경합니다.즉, 부수 효과(Side Effect)를 일으키며 외부 상태를 바꿉니다.
- increment()의 반환 값은 호출 시점에 따라 항상 다릅니다.
- 첫 호출 시 result는 2, 다음 호출 시 3이 됩니다.
- 이 표현식을 2로 단순 치환하면 이후 프로그램 로직이 엉망이 됩니다.
→ 따라서 이 함수는 참조 투명하지 않습니다.
2. 순수 함수와 참조 투명성이 중요한 이유
함수형 프로그래밍에서 참조 투명성은 단순한 개념을 넘어, 코드의 예측 가능성과 안정성, 나아가 테스트성과 병렬 처리의 안전성까지 보장하는 핵심 원칙입니다.
📌 참조 투명성이 주는 이점
- 예측 가능성
동일한 입력 & 동일한 출력 → 쉬운 디버깅 - 유지보수성
표현식과 그 결과값을 자유롭게 치환 가능 → 간편한 리팩토링 - 병렬 처리에 유리
외부 상태에 의존하지 않음 → 안전한 멀티스레드 환경 확보 - 테스트 용이성
외부 상태와 격리된 함수 → 간단한 단위 테스트
결국, 참조 투명성은 "함수가 외부 환경에 의존하지 않고, 항상 같은 방식으로 동작함을 보장"하는 신뢰할 수 있는 코드의 기준입니다. 이러한 성질이 있기 때문에, 복잡한 시스템도 수학처럼 안정적으로 관리할 수 있게 됩니다.