2025. 4. 17. 09:38ㆍ프로그래밍 공부/javascript & typescript
함수형 프로그래밍을 공부하다 보면, Lodash나 Ramda과 더불어 최근에 공개된 es-toolkit 같은 라이브러리를 통해 여러 개념을 체험하게 됩니다.
Functional Programming 시리즈에서 타입스크립트 기반으로 함수형 유틸리티 라이브러리 fx
를 직접 설계하고 구현해보고자 합니다.
1. 함수형 프로그래밍이란?
함수형 프로그래밍은 수학적 함수 개념에 기반하여, 상태 변화와 부작용(side effect)을 최소화하고 데이터 흐름을 함수 조합으로 표현하는 프로그래밍 패러다임입니다.
중심 개념은 다음과 같습니다:
- 불변성 (Immutability): 데이터는 변경되지 않고, 항상 복사본을 리턴
- 순수 함수 (Pure Function): 같은 입력에 대해 항상 같은 결과를 리턴
- 고차 함수 (Higher-order Function): 함수를 인자로 받거나 반환
- 지연 평가 (Lazy Evaluation): 필요할 때만 값을 계산
이러한 원칙을 따르면 코드는 예측 가능하고 테스트하기 쉬워지며, 복잡한 로직을 더 작고 조합 가능한 단위로 나눌 수 있습니다.
2. 자바스크립트에서의 함수형 프로그래밍
자바스크립트는 함수가 일급 객체(first-class citizen)이기 때문에 함수형 프로그래밍을 적용하기에 매우 유연한 언어입니다. 실제로 JS는 map
, filter
, reduce
같은 배열 메서드를 통해 함수형 스타일을 일부 내장하고 있습니다.
그러나 다음과 같은 한계도 존재합니다
- 배열(Array)에만 적용 가능 —
Set
,Map
,Generator
등에는 직접적으로 적용 불가능 - 지연 평가가 기본값이 아님 —
map().filter()
는 즉시 실행됨 - 복잡한 체이닝 로직은 중간 변수를 사용해야 가독성을 유지할 수 있음
3. 설계 중점
이 프로젝트의 중심은 단순합니다.
"이터러블(iterable)을 기반으로 한 체이너블 함수형 유틸리티를 만든다."
설계 원칙은 다음과 같았습니다:
- 이터러블 기반:
Array
뿐만 아니라Set
,Map
,Generator
등 모든iterable
에 동작 - 지연 평가:
range()
,filter()
,map()
등이 실제로 쓰일 때까지 계산하지 않음 - 타입 안전: 타입스크립트 기반으로, 타입 추론이 가능한 설계
- 체이닝 중심:
Fx.of(data).map(...).filter(...).take(...)
형태의 파이프라인 지원 - 불변성 유지: 연산 후 원본 변경 없음. 항상 새로운 Fx 인스턴스 반환
4. 로직이 아니라 흐름
이 프로젝트에서 가장 중요하게 생각한 점은 로직보다 흐름에 집중하자는 것이었습니다.
기존 코드: 메모리 폭발
- 100만 개를 먼저 전부 생성
- 그 중 일부만 사용 ➝ 불필요한 계산 낭비
const topSquares = Array.from({ length: 1_000_000 }, (_, i) => i)
.map(x => x * x)
.filter(x => x % 2 === 0)
.slice(0, 10);
지연 평가 방식으로 처리하게 되면 아래와 같이 바꿀 수 있습니다
const topSquares = Fx.of(range(1_000_000))
.map(x => x * x) // 무한히 제곱을 생성
.filter(x => x % 2 === 0) // 짝수 제곱만 통과
.take(10) // 상위 10개만 평가
.toArray();
console.log(topSquares);
// 출력: [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]
range
함수는 yield
키워드를 이용하여 아래와 같이 구현할 수 있습니다.
export function range(end: number): Generator<number>;
export function range(start: number, end: number): Generator<number>;
export function range(start: number, end: number, step: number): Generator<number>;
export function* range(start: number, end?: number, step: number = 1): Generator<number> {
if (end === undefined) {
end = start;
start = 0;
}
if (step < 0) {
for (let i = start; i > end; i = i + step) yield i;
}
for (let i = start; i < end; i = i + step) yield i;
}
'프로그래밍 공부 > javascript & typescript' 카테고리의 다른 글
[Functional Programming] 부록: 테스트 커버리지와 GitHub Workflows (0) | 2025.04.20 |
---|---|
[Functional Programming] 1장: 기본 배열 함수 구현하기 (0) | 2025.04.19 |
[NextJS] nodemailer 이용하여 email 보내기 (2) | 2023.10.22 |
[Javascript & Typescript] Textarea의 값을 엔터키로 제출할 때 엔터가 입력되는 현상 해결 (0) | 2023.04.13 |
Content Security Policy (CSP) 컨텐츠 보안 정책 - eval() (0) | 2023.03.18 |