독서/📚

[도서 리뷰] 멀티프로그래밍 패러다임

leejinwoo1126 2025. 5. 26. 16:48
반응형

한빛미디어 서평단 <나는리뷰어다> 활동을 위해서 책을 협찬받아 작성된 서평입니다

 

 

📚 책을 읽게 된 계기

 실무에서는 대부분 주어진 기한 내에 기능을 구현하는 것에 초점을 맞추다 보니, 다양한 패러다임이나 이론들이 실제로 얼마나 실용적인지 의문을 품게 될 때가 많았습니다. 명령형, 객체 지향, 함수형 프로그래밍을 따로 익히면서 어떤 상황에서 어떤 방식을 적절히 적용해야 하는지 판단하기 어려웠고, 그만큼 업무에서 잘 활용하지 못한 게 사실이었습니다.

 

 그러던 중 IT 업계 지식 공유자분이 『멀티패러다임 프로그래밍』 도서 출간에 주목하고 있다는 소식을 접하게 되었고, 리뷰를 읽으면서 흥미가 생겼습니다. 현재는 백엔드 개발에 좀 더 비중을 두고 있지만, 프론트엔드도 지속적인 관심을 두고 있는 만큼, 시야를 확장하고 기술적 깊이를 더하기 위해 이 책을 읽게 되었습니다

 

google play books 전자책 표지

 

👀 리뷰

1. 이터레이터와 지연 실행

처음부터 흥미로웠던 부분은 이터레이터와 지연 실행이였습니다. map, filter와 같은 고차 함수가 일반 배열 메서드에서 즉시 실행되는 것과 달리, 제네레이터 함수에서는 필요할 때마다 값을 생성하는 구조라서 상황에 따라 성능 이점이 있다는 것을 알게 되었습니다.

 

책에서는 Array.prototype.reverse()와 직접 구현한 제네레이터 함수를 비교했는데, 프로토타입 함수는 즉시 배열 전체를 복사하고 순서를 뒤집는 반면, 이터레이터로 구현한 reverse() 함수는 호출하는 시점에서 역순으로 값을 지연 실행하게 됩니다. 또한, 이터레이터의 경우 원본 배열을 읽기만 하는데다, 호출할 때마다 요소를 하나씩 읽게 되어 데이터 양이 많을수록 상대적으로 메모리 사용량이나 계산 비용 측면에서 더 유리하다는 점을 알 수 있었습니다.

 

코드 1-5. Iterator를 반환하는 reverse 함수

function reverse<T>(arrayLike: ArrayLike<T>): Iterator<T> {
    let idx = arrayLike.length;
    return {
        next() {
            if(idx === 0) {
                return { value: undefined, done: true};
            } else {
                return { value: arrayLike[--idx], done: false};
            }
        }
    }
}

{
    const array = ['a', 'b'];
    const reversed = reverse(array);
    console.log(array); // ['a', 'b'], 원본 배열 그대로 

    console.log(reversed.next().value); // b
    console.log(reversed.next().value); // a
    console.log(reversed.next().value); // undefined
}

 

 

코드 1-6. 지연 평가가 더 효율적인 상황

const array = ['a', 'b', 'c', 'd', 'e', 'f'];
array.reverse(); // 원본 배열 객체가 변경됨
console.log(array); // ['f', 'e', 'd', 'c', 'b', 'a']
console.log(array[0], array[1]); // f e


const array2= ['a', 'b', 'c', 'd', 'e', 'f'];
const reversed = reverse(array2);
console.log(array2); // ['a', 'b', 'c', 'd', 'e', 'f'], 원본 배열은 그대로
console.log(reversed.next().value); // f
console.log(reversed.next().value); // e

 

 

코드 1-14. 제네레이터로 작성한 reverse 함수

function* reverse<T>(arrayLike: ArrayLike<T>): IterableIterator<T> {
    let idx = arrayLike.length;
    
    while(idx) {
        yield arrayLike[--idx];
    }
}

const array = ['a', 'b', 'c', 'd', 'e', 'f'];
const reversed = reverse(array);

console.log(reversed.next().value); // f
console.log(reversed.next().value); // e
console.log(reversed.next().value); // d
console.log(array); // ['a', 'b', 'c', 'd', 'e', 'f']

참고. MDN | Generator

 

 

2. 이터러블, 이터레이터, 그리고 프로토콜

새롭게 알게 된 개념 중 가장 중요하다고 생각한 건 이터러블 프로토콜(Iterable Protocol)이었습니다. 프로토콜이란 단어를 주로 네트워크 통신에서의 규약 정도로만 이해하고 있었는데, 자바스크립트에서는 객체 간의 유연한 확장성과 협업을 위한 약속으로 쓰이고 있다는 점이 신선하게 다가왔습니다. 

 

자바스크립트에서 어떤 객체가 [Symbol.iterator]() { return { next() { ... }}}라는 이터레이터를 반환하면, 해당 객체는 이터러블로 간주됩니다. 그리고 이터러블 객체를 순회하기 위해 반환되는 이터레이터next() 메서드를 가지고 있으며, { value, done } 형식의 결과를 반환해야 합니다. 이처럼 인터페이스(약속)를 지키기만 하면, 어떤 객체든 for...of, 전개 연산자, 구조 분해 할당과 같은 다양한 고급 기능과 함께 사용할 수 있습니다. 덕분에 범용적으로 다룰 수 있는 유연하고 일관된 순회 구조를 만들어낼 수 있다는 걸 책을 통해 배웠습니다.

 

 

코드 1-18. Iterator<T>, Iterable<T>, IterableIterator<T>

interface IteratorYieldResult<T> {
	done?: false;
	value: T;
}

interface IteratorRetunResult {
	done: true;
	value: undefined;
}

interface Iterator<T> {
	next(): IteratorYieldResult<T> | IteratorRetunResult;
}

interface Iterable<T> {
	[Symbol.iterator](): Iterator<T>;
}

interface IterableIterator<T> extends Iterator<T> {
	[Symbol.iterator](): IterableIterator<T>; 
}
  • Iterator :  {value, done} 객체를 반환하는 next() 메서드를 가진 값
  • Iterable : 이터레이터를 반환하는 [Symbol.iterator]() 메서드를 가진 값
  • IterableIterator : 이터레이터이면서 이터러블인 값
  • 인터러블 프로토콜 : 이터러블을 for...of문, 전개 연산자 등과 함께 동작하도록 한 규약

 

예전에 NodeList 객체를 forEach, map, filter 함수 체이닝하여 사용하는 것이 당연한 줄만 알았는데, 이 역시도 NodeList가 이터러블 프로토콜을 따르기 때문에 가능한 일이었다는 걸 처음 알게 되었습니다. 책에서는 Array, Map, Set, NodeList가 서로 전혀 다른 종류의 객체임에도 불구하고 동일한 방식으로 순회 가능했던 이유를 프로토콜 개념을 통해 설명해줍니다. 그동안 무심코 사용했던 문법들이 사실은 공통 규격을 따르기 때문에 가능한 것이었다는 걸 알게 되어, 언어에 대한 이해도가 깊어지는 느낌을 받았습니다.

  • NodeList : 브라우저 구현 객체
  • Array, Map, Set : 자바스크립트 표준 라이브러리 객체

 

코드 1-45 

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    const nodeList = document.querySelectorAll('li');

    for(const node of nodeList) {
        console.log(node.textContent);   
    }
</script> 

---
콘솔 출력
1
2
3
4
5

 

 

코드 1-46

forEach(console.log, 
    filter(x => x % 2 === 1, 
        map(node => parseInt(node.textContent), document.querySelectorAll('li'))
    )
);
  • NodeList가 이터러블 프로토콜을 지키고 있기 때문에 앞에서 만든 이터러블 함수와 함께 사용할 수 있습니다.
  • 텍스트 값이 홀수인 경우에만 콘솔 출력됩니다.

 

3. 명령형, 객체 지향형, 함수형 프로그래밍, 그리고 멀티 패러다임

forEach, map, filter, reduce 함수를 이터러블 프로토콜과 제네레이터 함수를 활용해 직접 구현하는 과정을 따라해보면서, 동작 원리를 자연스럽게 이해할 수 있었습니다. 이때 한 가지 패러다임에만 국한되지 않고, 명령형, 객체 지향, 함수형 프로그래밍을 적절히 혼합하여 구현하는 과정이 굉장히 인상적이었고, 기존에 가지고 있던 패러다임에 대한 인식을 바꾸는 계기가 되었습니다. 특히, 객체 지향 구조(클래스)와 함수형 체이닝이 자연스럽게 결합된 구현 방식은 저에게 많은 영감을 주기에 충분했습니다.

 

이 책은 단순히 ‘패러다임을 나열하는 설명서’가 아니라, 각각의 개념이 코드에서 어떻게 현실적으로 섞이고, 시너지를 내는지를 체감하게 해줍니다. 프로그래밍 언어에 대한 이해를 넘어, 문제를 해결하는 사고의 도구로서 패러다임을 바라보는 관점을 배우게 된 점이 무엇보다 큰 수확이었습니다. 결국 책에서 말하듯 이미 일상적으로 여러 패러다임을 혼용하며 개발하고 있었고, 다만 그 원리를 잘 알지 못했던 것뿐이었다는 걸 깨닫는 계기가 되었습니다. 리뷰를 작성하는 시점에서는 중반부까지 읽었지만 비동기 프로그래밍, 리스트 프로세싱처럼 실무와 밀접한 주제들이 남아 있다는 점에서, 이 책을 끝까지 읽어 볼 가치가 있다고 생각합니다.

“현대 멀티패러다임 언어들이 제공하는 다양한 기능을 깊이 이해하고 전략적으로 활용하는 능력은 개발자에게 강력한 무기가 됩니다. 탄탄한 기본기를 바탕으로 다양한 문제에 접근할 때 개발자는 더욱 창의적인 응용력을 발휘하여 문제를 효과적으로 확장성 있게 해결할 수 있을 것입니다."
- 멀티패러다임 프로그래밍 (p263)

 

🙌  추천 대상

이 책은 기본적인 프로그래밍 경험이 있고, 다양한 설계 방식이나 코드 스타일에 대해 고민해 본 개발자에게 특히 추천하고 싶습니다. 단순한 문법 지식보다는, 언제 어떤 패러다임을 선택하고 어떻게 조합할 것인가에 관심이 생긴 분들에게 좋은 자극이 될 수 있습니다. 주니어에서 미들급 개발자로 성장하고자 하거나, 패러다임(예: 객체지향, 함수형)에 익숙한 상태에서 사고의 유연성을 키우고 싶은 분들이라면, 이 책을 통해 패러다임에 대한 시야를 확장할 수 있습니다.

 

저는 자바 백엔드 개발자이지만, JavaScript, TypeScript로 작성된 예제에 대해서는 언어적인 허들이 크지 않았습니다. 언어별 공식 문서가 잘 되어 있고, 문법보다는 이터러블 프로토콜 기반으로 개념과 구조, 패러다임 활용에 집중하는 책의 구성 덕분에 예제를 이해하고 따라가는 데 큰 어려움은 없었습니다. 익숙하게 사용해 온 표준 라이브러리에 대해 단순히 “이렇게 사용하면 된다”에서 벗어나 “왜 이렇게 동작하는가”, “언제 이런 패턴을 선택할 것인가”에 대해 더 깊이 생각해보고, 시야를 넓히는 좋은 계기가 될 것입니다.

 

 

⛳️  마치며

단순한 시스템이라면 절차지향, 명령형 프로그래밍만으로도 충분할 수 있습니다. 그러나 현실에서 요구사항의 변화는 막을 수 없는게 진리이며 시간이 지남에 따라 시스템의 복잡도는 점점 높아집니다. 이런 상황에서는 더 유연하고, 확장 가능하며, 유지보수가 쉬운 설계를 추구해야 한다고 생각하고, 이를 가능하게 하는 중요한 열쇠 중 하나가 멀티 패러다임적 사고💬라고 생각합니다.

 

회사와 개인이 성장하고자 하는 방향은 반드시 일치하지 않습니다. '개발자는 평생 공부해야 한다'는 흔할 말처럼 개발자에게 지속적인 자기계발은 선택이 아니라 숙명이라고 생각합니다. 저는 기능 구현 위주의 절차지향적 개발에 익숙해져 있었습니다. 자기 계발을 하면서도 패러다임은 실무와 거리가 먼 이론처럼 느껴지기도 했고, 그 중요성을 깊이 실감하지 못했던 것도 사실입니다. 그런데 📚『멀티패러다임 프로그래밍』 책을 접하면서, 그동안 막연하게 느껴졌던 패러다임 활용의 본질적인 고민에 대해 방향성을 잡을 수 있는 계기가 되었습니다.

 

혹시 지금, 🌊망망대해 속에서 🧭나침반 없이 항해하고 있는 듯한 느낌을 받고 있다면, 이 책이 험난한 여정에 작은 방향타가 되어줄 수 있을지도 모릅니다. 자신이 어디쯤 와 있는지 점검하고, 앞으로 어떤 사고 방식과 도구를 익혀야 할지 고민하고 있다면 한 번 읽어보시기를 추천합니다.👍

 

 

반응형