한빛미디어 서평단 <나는리뷰어다> 활동을 위해서 책을 협찬 받아 작성된 서평입니다.
😲 함수형 프로그래밍에 대한 인식을 바꾼 책
평소에 함수형 프로그래밍을 적극적으로 사용하는 편은 아니다. 보통 Java Collection 클래스에 대한 후처리가 필요할 때 사용하는데, 예를 들어, 응답으로 객체 배열이 들어왔을 때, 유효한 객체만 응답으로 받기 위해 filter를 사용하거나, 일괄적인 변경이 필요할 때 map을 사용하는 수준이다. 이번에 책을 읽고 난 뒤에는 생각이 바뀌었다.
우선은 비동기 처리의 필요성이다. 많은 시간이 소요되는 어떤 작업이 있다. 이 작업은 CPU 집약적이지 않고, 네트워크 통신 또는 파일 처리 등의 이유로 지연이 생기기 때문에 느리다. 이런 작업들은 동기적으로 진행하면 비효율적이다. 마치 음식점에서 여러 음식을 주문할 때, 한 음식이 조리되어 식탁에 올려졌을 때 비로소 다음 음식을 주문하는 것과 동일하다. 처음에 한 번에 주문하고 나오는대로 맛있게 먹으면 될텐데 말이다.
async function orderFoodAsync() {
console.log("음식점 도착 - 한번에 모든 음식 주문");
// 모든 음식을 동시에 주문
const steakPromise = cookFood("스테이크");
const pastaPromise = cookFood("파스타");
const saladPromise = cookFood("샐러드");
steakPromise.then((food) => console.log("잘라먹기:", food));
pastaPromise.then((food) => console.log("말아먹기:", food));
saladPromise.then((food) => console.log("찍어먹기:", food));
await Promise.all([steakPromise, pastaPromise, saladPromise]);
}
그 다음은 일급 함수의 역할이다. 일급 함수는 함수를 인자로 전달받을 수 있기 때문에, 매우 유연한 처리가 가능하다. 예를 들어, 음식점에서는 주문 받은 메뉴에 따라 조리법과 플레이팅 방식이 다르다. 하지만 요리를 조리하고, 접시에 올려서, 서빙한다는 순서는 동일하다. 일급 함수는 메뉴에 따라 다른 동작을 수행하는 함수를 전달받고, 일정한 순서에 따라 서빙까지의 절차를 일관되게 표현할 수 있다.
// 일급 함수를 활용한 일관된 서비스 로직
function order(dishName, ingredients, cookingFunction, platingFunction) {
console.log(`--- ${dishName} 요리 시작 ---`);
const cookedDish = cookingFunction(ingredients); // 1. 조리
const platedDish = platingFunction(cookedDish); // 2. 플레이팅
return platedDish; // 3. 서빙
}
그 다음은 순수 함수다. 순수 함수는 비동기 처리에 유용하게 활용할 수 있다. 순수 함수는 부수 효과(side-effect)를 발생시키지 않고, 동일한 입력에 항상 동일한 결과를 반환한다. 이러한 특징으로 인해 thread-safe한 상태를 유지하고, 예측 가능성 뿐만 아니라 성능 최적화를 위해 활용된다. 마치 학교 게시판에 공모전 정보가 게시되었을 때, 내가 필요하다고 포스터를 가져가는게 아니라 카메라로 QR만 찍어서 지원 사이트에 방문하는 것과 같다.
마지막으로 함수형 프로그래밍은 이러한 요소들을 잘 버무린 비빔밥이라고 비유하고 싶다. 그 안에는 나열된 일련의 자료들을 효율적으로 처리하기 위한 고민들이 담겨져 있다. Iterator 패턴을 활용한 범용성, 지연 평가를 통한 메모리 효율성, 일급 함수를 활용한 확장성, 순수 함수를 활용한 병렬 처리 안정성까지. 그러니 저자가 IP, OOP, FP의 관계를 강조하는 것도 이해가 간다.
LLM의 도움을 받아 generator와 음식점 예시를 작성해보았다. Generator, 고차함수, 지연 실행, Promise와 비동기 처리 등 다양한 요소를 자연스럽게 반영하려 했지만, 코드가 어쩔 수 없이 굉장히 작위적이라는 건 부정할 수 없을 것 같다. 그래도 이런 개념을 활용해보았다는데 의의를 두고 싶다!
fp_practice
fp_practice. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
async function runRestaurant() {
const dishes = [
{
name: "스테이크",
ingredients: "등심",
cookFn: Recipes.cookSteak,
plateFn: Platings.fancy,
},
{
name: "스테이크",
ingredients: "등심",
cookFn: Recipes.cookSteak,
plateFn: Platings.fancy,
},
{
name: "파스타",
ingredients: "뇨끼",
cookFn: Recipes.cookPasta,
plateFn: Platings.fancy,
},
{
name: "샐러드",
ingredients: "채소",
cookFn: Recipes.cookSalad,
plateFn: Platings.simple,
},
];
console.log(`📊 총 주문 개수: ${dishes.length}개`);
// Generator를 통한 lazy evaluation과 iteration
const pipeline = cookingPipeline(dishes);
const cookingTasks = [];
// 각 조리 작업을 지연 평가로 수집
for (const cookingTask of pipeline) {
cookingTasks.push(cookingTask()); // 실제 실행은 여기서!
}
// 모든 조리 작업을 병렬로 실행
await Promise.all(cookingTasks);
console.log("📋 모든 음식이 서빙되었습니다. 맛있게 드세요!");
console.log("💰 계산을 진행하고 퇴장합니다");
return "서비스 완료";
}
🍯 심도 있는 고찰과 이론과 실무를 넘나드는 꿀팁
몇 가지 내용만 간단히 소개하자면, 먼저 비동기 에러 핸들링에 대한 팁이 도움이 됐다. 순수 함수는 부수 효과를 가지지 않는 것이 핵심이므로 발생한 에러는 상위 호출자에게 위임하는 것이 좋고, 에러 핸들링 코드는 네트워크 요청, 파일 입출력, 데이터베이스 쿼리와 같이 부수 효과 발생 코드 근처에 작성하는 것이 좋다. 리스트 프로세싱 패턴화도 도움이 되었다. 변형-누적 패턴(map-reduce), 중첩-변형 패턴(nested-map) 등 자연스럽게 사용하지만 이렇게 정리해보니 필요할 때 어떤 패턴을 사용해야할지 정리할 수 있어서 좋은 것 같다. 그리고 리스트 프로세싱 함수 유형별 개념 정리도 패턴화와 연계해서 매우 유용하게 사용 가능하다. 지연 중간 연산에는 map, filter, zip, 터미널 연산에는 find, every, some, reduce 등 특정한 패턴을 사용하고자 할 때, 함수의 목록을 바로 참고할 수 있다. 이 부분은 저자의 GitHub 소스의 예제를 참고하면 더 많은 것을 배울 수 있다.
GitHub - marpple/multi-paradigm-programming: Multi-Paradigm Programming: Combining Object-Oriented, Functional, and Imperative A
Multi-Paradigm Programming: Combining Object-Oriented, Functional, and Imperative Approaches for Software Development and Design - marpple/multi-paradigm-programming
github.com
⚠️ 프론트엔드 예시와 FxTs 라이브러리 사용
물론 백엔드 개발자도 참고할만한 내용이 있지만, 기본적으로 타입스크립트를 사용하는 프론트엔드 개발자가 얻어가는 것이 가장 많은 책이다. 그리고 일부 예시로 FxTs 라이브러리를 활용하는 경우가 나온다. 따라서, Native JavaScript만으로 모든 것을 설명하지는 않는 점을 알아두면 좋다.
마치며
솔직히 후반부는 내용이 잘 읽히지 않았다. 변명하자면 코드가 여러 곳에 산재되어 있어서, 나처럼 프론트엔드 경험이 부족하다면 진득하게 정독해야만 제대로 이해할 수 있을 것 같다. 그리고 책이 확실히 깊이가 있다. 읽으면 읽을수록 놓쳤던 내용들이 보여서 신기했다. 언젠가는 이 책에 있는 내용들을 모두 이해할 수 있는 날이 왔으면 좋겠다 🙂
'Insight > 서평' 카테고리의 다른 글
[서평] 자바 최적화 (7) | 2025.06.26 |
---|---|
[서평] 한 권으로 끝내는 만만한 자소서 (0) | 2025.04.28 |
[서평] 자바 코드의 품질을 높이는 100가지 방법 (4) | 2025.03.27 |
[서평] 전문가를 위한 리액트 (1) | 2025.02.23 |
[서평] 대규모 머신러닝 시스템 디자인 패턴 (1) | 2024.12.31 |