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

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

자바스크립트는 다양한 프로그래밍 패러다임을 지원하는 언어로, 그 중에서도 함수형 프로그래밍은 코드의 예측 가능성과 유지보수성을 높이는 데 큰 도움이 된다. 함수형 프로그래밍은 순수 함수, 불변성, 고차 함수, 재귀 등을 강조하며, 이를 통해 부작용을 최소화하고 코드를 더 간결하고 이해하기 쉽게 만든다. 이번에는 함수형 프로그래밍의 기본 개념과 자바스크립트에서 이를 어떻게 활용할 수 있는지 단계별로 자세히 다룬다.


함수형 프로그래밍을 잘 이해하고 활용하면 코드의 품질을 높이고, 복잡한 문제를 더 쉽게 해결할 수 있다. 하나씩 차근차근 알아보자.


함수형 프로그래밍이란?

함수형 프로그래밍은 함수를 일급 시민으로 다루는 프로그래밍 패러다임이다. 이는 함수를 변수에 저장하거나, 다른 함수의 인자로 전달하거나, 함수의 반환값으로 사용할 수 있다는 것을 의미한다. 함수형 프로그래밍은 다음과 같은 특징을 가진다:


  • 순수 함수: 동일한 입력에 대해 항상 동일한 출력을 반환하며, 외부 상태를 변경하지 않는다.
  • 불변성: 데이터가 한 번 생성되면 변경되지 않는다. 대신, 새로운 데이터를 생성한다.
  • 고차 함수: 함수를 인자로 받거나, 함수를 반환하는 함수.
  • 재귀: 반복적인 작업을 함수가 자신을 호출하는 방식으로 처리한다.

이와 달리, 명령형 프로그래밍은 상태를 변경하고, 절차를 순차적으로 나열하는 방식으로 코드를 작성한다. 함수형 프로그래밍은 이러한 상태 변경을 피하고, 함수의 조합으로 문제를 해결하는 방식이다.


1. 순수 함수 (Pure Functions)

순수 함수는 동일한 입력에 대해 항상 동일한 출력을 반환하며, 외부 상태를 변경하지 않는 함수이다. 이는 코드의 예측 가능성을 높이고, 디버깅을 쉽게 만든다.


예를 들어, 다음과 같은 함수는 순수 함수이다:

function add(a, b) {
    return a + b;
}

반면, 외부 상태를 변경하거나, 외부 상태에 의존하는 함수는 불순한 함수이다:

let total = 0;
function addToTotal(value) {
    total += value;
    return total;
}

위 함수는 외부 변수 `total`을 변경하므로, 동일한 입력에 대해 항상 동일한 출력을 보장하지 않는다. 이는 함수형 프로그래밍에서는 피해야 할 방식이다.


2. 불변성 (Immutability)

불변성은 데이터가 한 번 생성되면 변경되지 않는다는 개념이다. 이는 상태 변경으로 인한 부작용을 방지하고, 코드의 안정성을 높인다. 자바스크립트에서 불변성을 유지하기 위해, 객체와 배열을 변경하는 대신 새로운 객체와 배열을 생성한다.


예를 들어, 배열에 요소를 추가할 때, 기존 배열을 변경하는 대신 새로운 배열을 생성한다:

const arr = [1, 2, 3];
const newArr = [...arr, 4]; // [1, 2, 3, 4]

객체의 경우에도 마찬가지로, 스프레드 연산자를 사용해 새로운 객체를 생성한다:

const obj = { name: '홍길동', age: 30 };
const newObj = { ...obj, age: 31 }; // { name: '홍길동', age: 31 }

이렇게 하면 원본 데이터는 그대로 유지되며, 새로운 데이터가 생성된다. 이는 함수형 프로그래밍에서 중요한 원칙이다.


3. 고차 함수 (Higher-Order Functions)

고차 함수는 함수를 인자로 받거나, 함수를 반환하는 함수이다. 자바스크립트에서는 `map`, `filter`, `reduce` 등이 대표적인 고차 함수이다.


예를 들어, `map` 함수는 배열의 각 요소에 함수를 적용해 새로운 배열을 반환한다:

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8]

`filter` 함수는 조건에 맞는 요소만 추출해 새로운 배열을 반환한다:

const evens = numbers.filter(n => n % 2 === 0); // [2, 4]

`reduce` 함수는 배열을 하나의 값으로 축약한다:

const sum = numbers.reduce((acc, curr) => acc + curr, 0); // 10

고차 함수를 사용하면 코드를 더 간결하고 declarative하게 작성할 수 있다.


4. 재귀 (Recursion)

재귀는 함수가 자신을 호출하는 방식으로, 반복적인 작업을 처리한다. 함수형 프로그래밍에서는 반복문 대신 재귀를 사용하는 경우가 많다.


예를 들어, 팩토리얼을 재귀로 구현할 수 있다:

function factorial(n) {
    if (n === 0) return 1;
    return n * factorial(n - 1);
}

재귀는 문제를 작은 부분으로 나누어 해결하는 방식으로, 트리 구조나 깊이 우선 탐색 등에 유용하다. 다만, 자바스크립트에서는 스택 오버플로우를 주의해야 한다.


5. 함수형 프로그래밍의 장점과 단점

함수형 프로그래밍은 다음과 같은 장점을 가진다:


  • 예측 가능성: 순수 함수와 불변성 덕분에 코드의 동작을 예측하기 쉽다.
  • 디버깅 용이: 부작용이 없어 문제가 발생했을 때 원인을 찾기 쉽다.
  • 모듈화: 함수를 조합하여 코드를 작성하므로, 재사용성이 높다.
  • 병렬 처리: 상태 변경이 없어, 병렬 처리가 용이하다.

반면, 다음과 같은 단점도 있다:


  • 성능: 불변성을 유지하기 위해 새로운 데이터를 생성하므로, 메모리 사용이 증가할 수 있다.
  • 학습 곡선: 명령형 프로그래밍에 익숙한 개발자에게는 새로운 개념일 수 있다.
  • 재귀의 한계: 자바스크립트에서는 재귀 깊이가 제한되어 있어, 깊은 재귀는 스택 오버플로우를 일으킬 수 있다.

따라서, 함수형 프로그래밍을 사용할 때는 이러한 장단점을 고려하여 적절한 상황에서 활용해야 한다.


6. 실제 활용 예제

함수형 프로그래밍을 활용한 간단한 애플리케이션 예제를 살펴보자. 예를 들어, 사용자 목록에서 특정 조건을 만족하는 사용자를 필터링하고, 그들의 이름을 대문자로 변환하는 작업을 함수형 프로그래밍 방식으로 처리할 수 있다.


const users = [
    { name: '홍길동', age: 25 },
    { name: '김철수', age: 30 },
    { name: '이영희', age: 22 }
];

const filteredUsers = users
    .filter(user => user.age > 25)
    .map(user => user.name.toUpperCase());

console.log(filteredUsers); // ['김철수']

위 코드에서는 `filter`와 `map`을 조합하여 원하는 결과를 얻었다. 이는 명령형으로 작성할 때보다 더 간결하고 이해하기 쉽다.


마무리

함수형 프로그래밍은 자바스크립트에서 코드를 더 예측 가능하고 유지보수하기 쉽게 만드는 강력한 도구이다. 순수 함수, 불변성, 고차 함수, 재귀 등의 개념을 이해하고 활용하면, 복잡한 문제를 더 쉽게 해결할 수 있다. 함수형 프로그래밍의 장단점을 잘 파악하여 적절한 상황에서 활용해보자.


+ Recent posts