
패러다임이란 한 시대의 사람들의 견해나 사고를 근본적으로 규정하고 있는 테두리로서의 인식의 체계, 또는 사물에 대한 이론적인 틀이나 체계를 의미하는 개념이다.
프로그래밍 패러다임이란 말 그대로 프로그래밍의 패러다임 형태를 정의하는 것이다. 이것은 프로그래머에게 프로그래밍의 관점을 갖게 해 주고, 결정하는 역할을 한다. 각각의 프로그래밍 언어들은 서로 다른 프로그래밍 패러다임을 지원하며, 다수의 패러다임을 지원하는 경우도 존재한다.
프로그래밍 패러다임은 크게 두가지 종류로 나뉜다.
함수형 프로그래밍은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다. 함수의 응용을 강조하며, 계산가능성, 결정문제, 함수정의, 함수응용과 재귀를 연구하기 위해 개발된 형식체계인 람다 대수에 근간을 두고 있다.
함수형 프로그래밍이란 순수 함수(pure function)를 조합하고 공유 상태(shared state), 변경 가능한 데이터(mutable data) 및 부작용(side-effects)을 피하여 소프트웨어를 만드는 프로세스다.
개념이 좀 어려워 보이는데 하나씩 풀어서 해석해보자.
순수 함수는 다음과 같다
입력에만 의존하고 외부의 상태를 변경하지 않으며, 동일한 입력에 대해 항상 동일한 출력을 반환한다.
즉, 이름 그대로 정해진 값만 순수하게 반환할 뿐 그 이외의 어떤 것도 변경하지 않는 함수를 말한다.
function add(a, b) {
return a + b;
}
// 동일한 input에 해당되는 동일한 output을 반환
console.log(add(3, 5)); // output: 8
console.log(add(3, 5)); // output: 8
함수형 프로그래밍에서는 공유 상태를 피하고, 데이터의 불변성을 유지한다. 이는 한 번 생성된 데이터는 변경되지 않으며, 새로운 데이터를 생성할 때 기존 데이터를 변경하는 대신 새로운 데이터를 반환한다.
이렇게 말해도 어려우니까 좀 더 쉽게 설명해보겠다.
프로그램이 메모리에 저장된 값을 호출하는 방식은 두 가지가 있다.
‘값에 의한 호출’은 저장된 값을 복사하여 가져오기 때문에 실제 값에는 영향이 가지 않지만, ‘참조에 의한 호출’은 값이 저장된 메모리 주소를 가져와 실제 값에 영향을 미친다.

함수형 프로그래밍에서는 두 가지 방식 중 ‘값에 의한 호출’을 사용한다.
한가지 예를 들어 보겠다.
function foo(str) {
return str.substring(0, 2);
}
foo 함수는 문자열의 2번째 글자까지 반환해주는 함수이다. 즉, 문자열을 수정하여 반환해준다.
const str = "Hello, World";
foo(str); // output: He
foo 함수는 ‘str’ 변수를 수정하여 ‘He’라는 값을 반환하였다. 이제, 기존의 str을 출력해보자.
console.log(foo(str)); // output: He
console.log(str); // output: Hello, World
처음 할당한 값이 그대로 반환된 것을 볼 수 있다.
함수형 프로그래밍에서는 값에 의한 호출을 사용하기 때문에 기존에 값이 그대로 저장된 것을 볼 수 있다. 만약, 참조에 의한 호출을 사용하였다면 값이 ‘He’로 변경되었겠지만, 그렇지 않기 때문에 변경되지 않았다.
이제 아까했던 말을 다시 보자.
함수형 프로그래밍에서는 공유 상태를 피하고, 불변성을 유지한다고 했었다.
함수는 값을 공유하지 않기 때문에 실제 값에 영향을 미치지 않고, 이것은 값을 변경하지 않는다는 말과 같다. 즉, 한번 생성된 데이터는 변경되지 않으며, 새로운 데이터가 생성될 때는 기존의 데이터를 유지한 채 새로운 데이터를 반환한다.
흔히 아는 그 말과 비슷하다. 말 그대로 어떤 것이 기대하는 효과 외에 나타나는 부수적인 효과를 의미한다. 보다 프로그래밍적으로 이야기하자면 부작용은 함수가 외부의 상태를 변경하거나 다른 부수 효과를 발생시키는 것을 말한다.
순수 함수에 특징에서 말했던 부작용이 없다는 의미는 외부의 상태를 변경하는 등의 다른 부수 효과를 발생시키지 않아 예측된 결과만을 반환한다는 의미이다.
함수형 프로그래밍에서는 이러한 부작용을 최소화하여 코드를 예측 가능하고 안정적으로 유지한다.
이를 보다 깊게 이해하기 위해서는 클로저라는 개념을 알아야 한다.
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
출처: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
위에서 이야기 하는 렉시컬 환경은 함수가 정의된 스코프에서 사용할 수 있는 변수들의 집합을 나타낸다.
말이 좀 어려운데 쉽게 이야기 하자면 함수를 정의할 때 그 함수 내에서 사용할 수 있는 변수들의 집합을 말하는 것이다.
흔히 이야기 하는 전역 변수, 지역 변수가 위에서 이야기 하는 내용과 이어진다.
let globalNum = 1;
function scope1 () {
let num = 2;
console.log(num, globalNum);
}
function scope2 () {
let num = 3;
console.log(num, globalNum);
num++;
globalNum++;
}
scope1(); // output: 2, 1
scope2(); // output: 3, 1
scope1(); // output: 2, 2
scope1과 scope2는 사용할 수 있는 변수가 각각 정해져 있다.
지역 변수인 num들은 해당 함수 안에서만 영향을 미치지만, 전역 변수인 globalNum은 함수 외부적으로 영향을 미치는 것을 볼 수 있다.
함수들이 해당 변수들을 사용할 수 있는 이유는 클로저가 해당 변수들을 사용할 수 있는 범위를 지정하고 기억하였기 때문이다.
그렇다면 클로저와 부작용이 어떤 관계가 있는 것일까?
클로저는 함수형 프로그래밍에서 부작용을 줄일 수 있는 도구로 작용한다. 클로저가 함수가 영향을 미칠 수 있는 범위를 지정하여 외부로의 주는 영향을 차단하기 때문이다. 또한, 클로저가 생성됨을 알고 있기 때문에 우리는 함수가 미치는 영향을 예측할 수 있다.
즉, 클로저를 통해 부작용을 예측할 수 있고, 최소화할 수 있는 것이다.
Wikipedia, Programming paradigm, https://en.wikipedia.org/wiki/Programming_paradigm
Wikipedia, 함수형 프로그래밍, https://ko.wikipedia.org/wiki/함수형_프로그래밍
MDN Web docs, Closure, https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
Dico’s_footPrint.log, [Javascript] Functional Programming(함수형 프로그래밍), https://velog.io/@grinding_hannah/JavaScript-Functional-Programming함수형-프로그래밍