- 함수형 프로그래밍이 정확히 어떤 것일까 했는데, 자세히 공부하려고 사놨던 책(함수형 자바스크립트)을 이제서야 읽어보기 시작했다(ㅎㅎ).
- 이전의 내 짧은 지식으로는 함수를 위주로 구성하는 프로그래밍(?) 정도의 얕팍한 이해 정도만 하고 있었는데, 1장을 읽어보며 조금의 이해를 쌓아보기로.
함수형 프로그래밍이 무엇인가요?
- 말 그대로 ‘함수의 사용을 강조하는 프로그래밍 스타일’이다.
- 다만, 단순히 ‘함수를 사용하자’는 것은 아니고 고려하고 반영해야 하는 원칙이 존재한다.
함수형 프로그래밍의 원칙
- 부수 효과 (Side Effect)를 방지한다.
- 상태 변이를 줄이기 위해 데이터의 제어 흐름 및 연산을 추상화한다.
⇒ 이 원칙을 준수하기 위해 ‘재사용성 & 신뢰성(Reliability)’을 고려하여, 가능한 작게 기능을 나눈다.
FP(Functional Programming)의 기본 개념
선언적(Declarative)이다
- 선언적 프로그래밍에서 ‘이 로직을 어떻게 구현하는가?’ 는 중요하지 않다.
- 내부 매커니즘은 추상화되어있는 채로, 이 로직의 결과가 어떻게 나오는지에 집중한다.
- JavaScript에서는 이를 구현할 때, 선언적 함수인 HoF(Hider order Function)을 사용하여 구현한다.
- HoF에는 map, filter, reduce 같은 함수들이 존재한다.
ex) 숫자로 이루어진 배열의 각 요소에 1씩 더한다고 가정할 때. 명령형 프로그래밍과 함수형 프로그래밍은 아래와 같이 구현할 수 있다.
- 명령형 프로그래밍
const arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
arr[i] += 1;
}
→ 각 배열 요소에 어떻게 접근할지, 접근한 배열 요소에 어떤 작업을 해야 하는지에 대해 상세히 서술되어있다.
- 함수형 프로그래밍
const arr = [1, 2, 3, 4, 5];
arr.map((el) => el + 1);
→ 각 배열 요소에 접근하는 등의 세세한 로직은 생략되고, 오직 배열 요소에 ‘무엇을 할지(여기서는 1을 더하기)’에 대해 서술되어있다.
무상태성 & 불변성을 지향한다
- 위의 예제 명령형 프로그래밍 예제 코드에서, 원본 배열 arr의 값들은 반복문 실행 후에 바뀌게 된다.
- 명령형 프로그래밍에서 루프와 같은 구조는 반복할 때마다 값/상태가 계속 바뀌고, 재사용이 어렵다.
- 함수형 프로그래밍 코드에서는 map함수를 통해 원하는 동작이 배열 각 요소에 실행된 새로운 배열이 반환된다.
- 즉, 원본 배열은 변하지 않는다.
- 이러한 방식으로, 함수형 프로그래밍은 상태나 값을 바꾸지 않는 무상태성과 불변성을 지향한다.
- 무상태성과 불변성을 지키기 위해서, ‘순수함수(Pure Function)’를 사용한다.
순수함수
순수함수는 아래와 같은 특징을 갖는다.
- 외부 값과는 부관하게 작동한다.
- 외부 값이나 매개변수를 변경하지도 않는다.
- 입력 값에만 의존한다.
- 부수 효과(Side Effect)를 일으키지 않는다.
부수효과란, 아래와 같은 것들을 지칭한다.
- 전역 변수나 속성 등을 변경한다.
- 예외 처리 없이 throw 한다.
- 매개변수를 변경한다.
- HTML 문서, 브라우저 쿠키, DB에 질의한다.
- 화면이나 로그를 출력한다.
→ 위와 같은 부수효과를 전부 근절하기란 어렵지만, 상태 변이를 최소화하면서 순수 함수의 사용을 지향하자는 것이 핵심.
참조 투명성 & 치환성
- 참조 투명성이란 ‘어떤 함수가 동일한 입력을 받았을 때, 동일한 결과를 내는 성질’을 의미한다.
- 순수 함수의 조건이기도 하다.
// Example
let count = 0;
const increment1 = () => { return ++counter }; // 참조 투명하지 않다.
const increment2 = (count) => { return count + 1 }; // 참조 투명하다.
- 코드를 테스트하기 쉽고, 전체 로직 파악이 쉽다는 이점이 있다.
- 참조 투명하지 않은 함수는 외부 요소의 변화에 따라 영향을 받기 때문에, 흐름을 파악하기 어렵다.
- 참조 투명하게 구현된 프로그램은 재작성 혹은 치환 시에도 원하는 결과를 얻을 수 있다.
불변 데이터의 유지
- 기본적으로 JavaScript의 기본형은 모두 불변하다.
- 그러나 객체(ex. 배열 등)는 불변하지 않기 때문에, 뜻하지 않게 부수효과를 낳을 수 있다.
- 함수형 프로그래밍은 모든 변수를 ‘불변하게’ 관리한다.
함수형 프로그래밍의 좋은 점
- 간단하고 작은 함수들로 작업을 나눈다.
- 흐름 체인(Fluent Chain)으로 데이터를 처리한다.
- 리액티브 패러다임을 실현하여 이벤트 중심 코드의 복잡성을 줄인다.
작업의 분해
- 함수형 프로그래밍의 하위 작업의 단위는 ‘함수’이다.
- 즉, 작업을 작은 함수로 쪼개나간다는 뜻이다.
- 이 때, 함수는 한 가지 목표(기능)만을 실행해야 한다.
→ 단일성을 추구해야 한다.
체이닝(Chaining)
- 체인: 같은 객체를 반환하는 순환적인 함수 호출을 일컫는다.
ex) Lodash를 사용해 복수과목을 수강한 학생들의 평균 점수를 계산하는 로직은 아래와 같이 표현할 수 있다.
import * as _ from 'lodash';
_.chain(enrollment)
.filter(e => e.enrolled > 1)
.pluck('grade')
.average()
.value();
- 함수 체인은 필요한 시점까지 함수의 실행을 미루는 ‘느긋한 평가’를 수행한다.
비동기 어플리케이션에서의 처리(+ 리액티브 패러다임)
- 리액티브 패러다임(혹은 프로그래밍): 데이터의 흐름과 변경에 중점을 두는 선언적 프로그래밍 방식이다.
- 데이터의 변화를 Observer를 통해 관찰하고, 이벤트 발생 시 로직을 수행한다.
- 코드를 추상화하여 비즈니스 로직에만 집중할 수 있게 해준다.
- 함수형 프로그래밍과 리액티브 프로그래밍을 같이 사용함으로써, 데이터의 흐름과 변경 로직을 간결하고 가독성 있게 구현할 수 있다. ⇒ 복잡성이 줄어든다.
- 읽다보며... 내 평소 스타일은 함수형 프로그래밍의 그것에 가까우나, 별개로 ‘그래서 함수들을 어떻게 만들고 관리해야 하는지’에 대해서는 알고 있는게 없어서 그닥 제대로 된 함수형 프로그래밍을 해왔단 생각은 들지 않았다.
- 함수형 프로그래밍의 추구 목표와 그 특성에 대해 간단히 살펴봤으니, 남은 부분을 천천히 읽어보며 어떻게 코드를 개선할 수 있을지 고민해봐야겠다.
'공부 > JS' 카테고리의 다른 글
[Javascript] 프로토타입 알아보기 - 2 (0) | 2024.02.02 |
---|---|
[Javascript] 프로토타입 알아보기 - 1 (0) | 2024.01.24 |
[Javascript] 콜백 함수 알아보기 (1) | 2024.01.21 |
[Javascript] 클로저 알아보기 - 2 (1) | 2024.01.08 |
[Javascript] 클로저 알아보기 - 1 (1) | 2024.01.05 |