← 블로그로 돌아가기

함수형 프로그래밍이란 무엇인가

함수형 프로그래밍은 변수의 사용을 지양하고 순수함수들을 사용해서 프로그래밍을 하는 것

코딩 중 오류의 소지가 될 수 있는 변수의 사용을 지양하고 순수함수들을 사용해서 프로그래밍을 하는 것

예를 들어,

어떤 데이터를 특정 데이터로 걸러낸 다음, 최초 몇개 값만 추리고, 특정 형식으로 변환한 다음, 한 문자열로 합치는 과정 을 진행한다고 쳤을 때

함수형이 아닌 프로그래밍

const numbers = [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20
]

let count = 0   // 변수 -> 프로그래밍 실행 중 값이 바뀔 수 있음
let result = '' // 변수 -> 프로그래밍 실행 중 값이 바뀔 수 있음

// let i는 변수 -> 프로그래밍 실행 중 값이 바뀔 수 있음
for (let i = 0; i < numbers.length - 1 && count < 5; i++){
    if(numbers[i] % 2 === 0){
        result += (result === '' ? '' : ', ') + Math.pow(numbers[i], 2)
        count++
    }
}

소프트웨어가 커지고 복잡해지면 count, result, i와 같이 코드에 노출된 변수들은 위험요소가 된다.

이 변수들은 해당 소프트웨어가 돌아가는 맥락, 즉 상태값으로 작용하게 되는데 프로그래머가 이런 값들을 하나하나 체크하면서 코드를 짜야 하기 때문에 오류의 소지가 많아진다.

특히 여러 작업이 동시에 돌아가는 멀티스레딩 환경에서는 둘 이상의 스레드가 한 변수에 접근할 때, 이를 감안해서 제대로 조치를 취해놓지 않으면 파악하기 어려운 버그가 발생할 수 있다. multi_thread_error

위의 그림처럼 ’출력하기’에서 어떤 값을 출력해야 할지 알 수 없는 상황이 발생할 수 있다.

함수형 프로그래밍

위와 같은 문제 때문에 함수형 프로그래밍에서는 가능한 변수들을 쓰지 않고, 순수 함수들, 즉 외부의 데이터를 변경하지 않고 받아온 값들을 내부에서 처리해서 밖으로 반환하는 이런 함수들, 또는 이런 함수들의 연쇄작용을 사용한다.

const numbers = [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20
]

const result = numbers.
                filter(n => n % 2 === 0)
                .slice(0, 5)
                .map(n => Math.pow(n, 2))
                .join(', ')

물론 이 함수들의 내부에서는 변수들이 사용되겠지만 적어도 그게 개발자가 작성하는 코드로 노출되지 않기 때문에 프로그래머의 착오로 인한 오류는 방지된다.

또한 스레드들의 동시접근에 의한 오류 또는 교착 문제들로부터도 자유로워진다.


함수형이 아닌 방식으로 짜인 코드는 아래처럼 이걸로 뭘 하고 이걸로 뭘 해라 이런 식으로 동사들을 사용하는 명령조의 느낌으로 짜인다.

인덱스가 0일떄부터 5보다 작을떄까지 인덱스를 하나씩 올리면서
해당 인덱스의 숫자가 짝수면 result에 그걸 제곱한 값을 더하고
result가 빈 문자열이 아니면 그 앞에 쉼표와 공백을 더해주고 카운트를 올려라

하지만 함수형 프로그래밍은, 이거는 이것들을 거친 이거다처럼 어떤 과정을 선언하는 형태를 띠고 있다.

result는 numbers에서 짝수에 최초 5개에 각각을 제곱한 값을 쉼표와 공백으로 이어붙인 것이다.

때문에 함수형 프로그래밍은 일반적으로 선언형 프로그래밍의 특성도 가진다.

그리고 각종 알고리즘을 직접 구현하기 보다는 언어나 라이브러리들마다 주어진 다양한 순수 함수들을 필요한 곳에 가져다 쓰는 방식으로 코딩이 이뤄진다.

때문에 다양한 함수들의 기능과 활용법을 알아두는게 중요하다.