2025-08-08 00:01
Tags:
순수 함수 핸드북
1. 만들어진 이유: 예측 불가능성과의 싸움
소프트웨어 개발 초창기, 코드는 마치 살아있는 생물처럼 예기치 않게 상태를 바꾸고 예상치 못한 결과를 낳았습니다. 함수 하나를 수정했을 뿐인데, 전혀 상관없어 보이는 다른 기능에서 오류가 발생하는 일은 흔했습니다.
이 문제의 핵심에는 **부수 효과(Side Effect)**가 있었습니다. 부수 효과란 함수가 자신의 범위(scope)를 넘어서 외부의 상태를 변경하거나 영향을 받는 것을 의미합니다. 예를 들어, 함수가 전역 변수의 값을 바꾸거나, 데이터베이스에 정보를 저장하거나, 화면에 무언가를 출력하는 모든 행위가 부수 효과에 해당합니다.
이러한 부수 효과는 코드의 예측 가능성을 현저히 떨어뜨립니다. 함수를 이해하려면 그 함수 내부뿐만 아니라, 함수와 상호작용하는 모든 외부 환경까지 고려해야 했기 때문입니다. 이는 마치 요리사가 레시피만 보고 요리하는 것이 아니라, 주방의 온도, 습도, 다른 요리사의 움직임까지 모두 신경 써야 하는 것과 같았습니다.
순수 함수는 이러한 복잡성과 예측 불가능성을 해결하기 위한 아이디어에서 탄생했습니다. “만약 함수가 외부 세계와 완전히 격리된 채, 오직 주어진 재료(입력값)만으로 항상 동일한 요리(결과값)를 만들어낸다면 어떨까?” 라는 생각이었죠. 이렇게 외부 상태에 의존하지도, 변경하지도 않는 함수를 통해 코드의 동작을 명확하게 예측하고, 테스트와 디버깅을 용이하게 만들고자 한 것이 순수 함수의 근본적인 탄생 배경입니다.
2. 구조: 두 가지 핵심 원칙
순수 함수는 두 가지 간단하고 명확한 규칙으로 정의됩니다.
원칙 1: 동일한 입력에 대해 항상 동일한 출력을 반환 (Same input, Same output)
함수에 같은 값을 전달하면, 언제, 어디서, 몇 번을 호출하든 항상 똑같은 결과가 나와야 합니다. 이는 함수가 자신의 매개변수 외에 다른 어떤 정보(전역 변수, 외부 파일, 네트워크 상태 등)에도 의존하지 않기 때문에 가능합니다.
순수 함수 예시:
function add(a, b) {
return a + b;
}
add(2, 3); // 항상 5를 반환
add(2, 3); // 100번을 호출해도 5를 반환
순수하지 않은 함수 예시:
let minimumAge = 18;
function isAdult(age) {
return age >= minimumAge; // 외부 변수 minimumAge에 의존
}
isAdult(20); // true
minimumAge = 21; // 외부 상태가 변경됨
isAdult(20); // 이제 false를 반환 (동일한 입력, 다른 출력)
원칙 2: 부수 효과(Side Effect)를 일으키지 않음
함수는 결과값을 반환하는 것 외에 외부 세계에 어떤 영향도 미치지 않아야 합니다.
-
전역 변수나 정적 변수를 수정하지 않음
-
함수의 인자로 받은 객체를 직접 수정하지 않음
-
console.log
,alert
등 콘솔이나 화면에 출력하지 않음 -
파일을 쓰거나 읽지 않음
-
네트워크 요청을 보내지 않음
-
데이터베이스에 접근하지 않음
순수 함수 예시:
function createGreeting(name) {
return `Hello, ${name}!`; // 오직 새로운 문자열을 생성하여 반환
}
순수하지 않은 함수 예시:
let user = { name: "Alice", age: 30 };
// 인자로 받은 객체를 직접 수정 (부수 효과!)
function celebrateBirthday(person) {
person.age += 1; // 원본 객체를 변경함
return person;
}
celebrateBirthday(user);
console.log(user.age); // 31. 함수 호출로 인해 user 객체의 상태가 변경됨
3. 사용법: 순수함수가 가져다주는 이점
순수 함수를 사용하면 코드의 품질과 유지보수성이 크게 향상됩니다.
-
예측 가능성 (Predictability)
- 함수의 동작을 이해하기 위해 함수 내부만 보면 되므로 코드 분석이 매우 쉬워짐.
-
테스트 용이성 (Testability)
- 외부 환경을 설정하거나 모의(mocking)할 필요 없이, 단순히 입력값과 기대하는 결과값만으로 간단하게 단위 테스트를 작성할 수 있음.
-
참조 투명성 (Referential Transparency)
add(2, 3)
은 항상5
이므로, 코드에서add(2, 3)
을5
로 대체해도 프로그램의 동작에는 아무런 영향이 없음. 이는 코드의 추론을 쉽게 만들고 컴파일러 최적화에도 도움을 줌.
-
병렬 처리 및 동시성 (Concurrency)
- 순수 함수는 외부 상태를 공유하지 않으므로, 여러 스레드에서 동시에 실행해도 경쟁 상태(Race Condition)나 교착 상태(Deadlock) 같은 복잡한 동시성 문제에서 자유로움.
-
캐싱 가능 (Cacheable)
- 동일한 입력에 대해 항상 동일한 결과를 반환하므로, 입력값을 키(key)로 하여 함수의 결과값을 캐싱(메모이제이션, Memoization)할 수 있음. 이를 통해 비용이 큰 연산의 성능을 향상시킬 수 있음.
4. 심화: 불변성과 프로그래밍 패러다임
불변성 (Immutability)
순수 함수를 작성할 때 가장 중요한 개념 중 하나는 불변성입니다. 특히 객체나 배열을 다룰 때, 원본 데이터를 직접 수정하는 대신 항상 새로운 복사본을 만들어 변경 사항을 적용하고 그 복사본을 반환해야 합니다.
나쁜 예 (원본 배열 수정):
function sortByAge(users) {
return users.sort((a, b) => a.age - b.age); // 원본 배열을 직접 정렬
}
좋은 예 (복사본 생성 후 수정):
function sortByAge(users) {
const newUsers = [...users]; // ES6 스프레드 문법으로 얕은 복사
return newUsers.sort((a, b) => a.age - b.age);
}
함수형 프로그래밍 (Functional Programming)
순수 함수는 함수형 프로그래밍이라는 더 큰 패러다임의 핵심 구성 요소입니다. 함수형 프로그래밍은 부수 효과를 최소화하고, 상태 변화를 엄격하게 제어하며, 순수 함수들을 조합(Composition)하여 소프트웨어를 만드는 방식을 지향합니다. 이를 통해 더 안정적이고, 확장 가능하며, 이해하기 쉬운 코드를 작성하는 것을 목표로 합니다.
순수 함수에 대한 이해는 단순히 코딩 기술 하나를 배우는 것을 넘어, 소프트웨어의 복잡성을 다루는 새로운 관점을 제공해 줄 것입니다.
이 핸드북이 순수 함수를 이해하는 데 도움이 되었기를 바랍니다. 혹시 순수 함수를 실제 프로젝트에 적용하는 방법이나, 특정 상황에서 부수 효과를 어떻게 다루어야 할지에 대해 더 궁금한 점이 있으신가요?