[책] 알고리즘 산책: 수학에서 제네릭 프로그래밍까지 - 추상화를 어떻게 해야 하는지에 대한 수학적 설명
수학을 왜 공부해야 하는가? 수학자들은 세상의 진리를 탐구하기 위해서라고 할 것이고, 변태들은 단순히 재밌어서라고 할 것이다. 하지만 나는 진리 탐구에는 관심이 없다. 그리고 수학이 흥미롭다고 생각하지, 재밌다고 생각하지도 않는다. 그럼에도 수학을 공부하는 이유는 수학이 사고 훈련에 도움되기 때문이다.
LLM에게 코드 작성을 시키면 정말 못하는 것이 하나 있다. 바로 추상화다. 다른 부분은 몰라도 추상화만큼은 정말이지 신입 개발자 수준의 모습을 보여준다. LLM이나 신입 개발자들이 하는 추상화는 그저 비슷한 코드 몇 개를 하나의 함수로 합치는 수준에 불과하다.
추상화란 무엇일까? 추상화는 여러 개의 구체적인 대상에서 본질적으로 필요한 성질만 남기고, 나머지 차이를 버려 하나의 일반적인 개념으로 표현하는 것이다. 그렇다면 추상화를 잘하려면 어떻게 해야 할까?
추상화의 첫 번째 단계는 추상화할 대상과 범위를 정하는 것이다. 그리고 그 대상의 필수적인 성질이 무엇인지 깊이 생각해야 한다. 어떤 성질은 주어진 대상의 본질적인 것이고, 어떤 성질은 그 본질적인 성질을 만족하기만 하면 부수적으로 따라오는 것이다. 반대로 지금 눈앞에 있는 코드에 공통점이 있더라도 그것이 우연히 공유하고 있을 뿐이고, 실제로는 추상화 범위에 포함해서는 안 되는 경우도 있다. 좋은 추상화는 이 경계를 잘 찾는 일이다. 이런 구분이 없이 일단 코드가 같기 때문에 같은 클래스나 함수로 추상화하는 경우를 보면 나는 이렇게 묻는다. 이 구조가 정말로 필연적인가?
그리고 이것이 수학에서 사고하는 방식이다. 아니, 수학이라고 하면 범위가 너무 넓을 수도 있다. 최소한 대수학에서는 그렇다. 정수, 행렬, 다항식은 겉보기에는 전혀 다른 대상이다. 하지만 대수학에서는 그 대상 자체가 아니라 그 위에 어떤 연산이 정의되고, 그 연산이 어떤 법칙을 만족하는지를 본다. 그렇게 군, 반군, 모노이드, 환, 체 같은 대수 구조가 만들어진다.
이 책은 역사 속 대수학이 발전한 과정을 따라간다. 고대 그리스 수학에서 출발한 정수론의 발전 과정을 짚어보며, 개별적인 수나 계산법을 넘어 공통된 구조와 법칙을 뽑아내는 대수학적 사고방식에 도달한다. 여기서 중요한 점은 군, 모노이드, 환, 체 같은 개념들이 먼저 존재했던 것이 아니라는 것이다. 해결하고자 하는 구체적인 문제가 있었고 그 해법을 일반화하는 과정에서 발견되었다는 사실이다. 이 책은 군, 반군, 모노이드 등을 다루며 제네릭 알고리즘을 유도하고, 그것이 환, 반환, 유클리드 영역 같은 다양한 대수 구조로 확장되는 과정을 보여준다.
그리고 여기서 프로그래밍으로 넘어간다. 제네릭 프로그래밍도 본질적으로 같은 일을 한다. 단순히 int 타입에서 돌아가는 어떤 알고리즘을 T 타입에 돌아가게 하는 것이 제네릭 프로그래밍이 아니다. 제네릭 프로그래밍은 그 알고리즘이 실제로 요구하는 연산이 무엇인지 찾고, 그 연산이 어떤 법칙을 만족하는지 고민하여, 그보>다 더 강한 조건은 제거하는 과정이다. 즉, 타입 대신 알고리즘이 성립하기 위해 요구되는 최소한의 구조를 찾는 일이다.
이것은 대수학이 발전해 온 과정과 매우 닮았다. 구체적인 여러 대상을 보고 공통점을 찾는 것만으로는 부족하다. 필수적인 특성과, 현재의 구체적인 구현 상황에서 우연히 공유되는 특성을 구분해야 한다. 요구 조건이 너무 많으면 재사용성이 제한되고, 반대로 요구 조건이 너무 적으면 알고리즘 자체를 정의할 수 없게 된다. 그런 점에서 이 책은 "프로그래머가 왜 수학을 공부해야 하는가?"라는 질문에 답하기 위해 필요했던 바를 완벽하게 제공한다.
굳이 꼽자면 딱 하나 마음에 안 드는 부분이 있다. 마지막 실전 응용을 RSA 암호 알고리즘으로 구성한다는 것이다. 사실 책의 앞부분에서 GCD가 처음 예시로 등장했을 때부터 혹시나 했는데 역시나였다. 물론 RSA가 들어간 이유 자체는 충분히 이해할 수 있다. 앞에서 다룬 소수, 최대공약수, 베주 항등식, 모듈러 산술, 페르마의 작은 정리, 오일러의 정리 같은 정수론의 핵심 개념들을 하나로 종합해 보여주기에는 RSA만한 예제가 없기 때문이다.
하지만 이 책의 제목은 '수학과 제네릭 프로그래밍'이 아닌 '수학에서 제네릭 프로그래밍까지'다. 이 책이 다른 수학 책이나 프로그래밍 책에 비해 흥미로웠던 부분은 구체적인 수학 문제에서 출발해 공통된 구조를 발견하고, 그 추상화 방식을 제네릭 프로그래밍으로 가져오는 과정이었다. 그런데 마지막 예제인 RSA에서는 이 흐름이 끊기고 다시 정수론의 구체적인 응용으로 돌아간다.
그래서 이 책의 마무리는 사족처럼 느껴진다. 책을 읽으며 구체적인 대상에서 공통된 연산을 뽑고 그 연산의 필요한 법칙을 통해 추상적인 구조를 만드는 흐름을 배웠다. 그리고 그 사고방식이 프로그래밍으로 옮겨져, 구체적인 타입이 아니라 알고리즘에 필요한 최소한의 요구조건을 찾는 제네릭 프로그래밍으로 이어지는 과정도 보았다. 그런데 마지막 흐름에서 다시 정수론의 응용으로 돌아간다. 이 책의 제목에서 내가 기대한 마무리와는 약간 다르다. RSA가 책의 마지막 내용으로 적절한 챕터였을까? 사족이 아니었을까? 차라리 부록에 넣으면 어땠을까? 그런 생각이 든다.
이런 사족에도 불구하고 이 책의 장점은 명확하다. 제네릭 프로그래밍에서 추상화를 어떻게 다루어야 하는지 이 정도로 깊이 있게 설명한 책은 찾기 힘들다. 코드 예제를 C++로 사용하지만 이것은 딱히 단점이 되지 않는다. C++도 그저 코드를 표현하기 위한 도구일 뿐, 책이 설명하고자 하는 것은 제네릭 프로그래밍 그 자체이기 때문이다. 기본적인 C++ 문법 지식만 있어도 코드를 이해하는 데 무리는 없을 것이다. 오히려 진입 장벽이 있다면 C++보다는 수학 쪽이다. 수학적 개념과 증명이 적지 않게 등장하는 것이 걸림돌이 될 수 있다. 본문에 수학적 개념과 증명이 적지 않게 등장하기 때문에, 대학교 수준의 수학을 배운 경험이 없다면 꽤 읽기 어려울 수 있다. 그럼에도 불구하고 좋은 추상화가 무엇인지 이해하고 싶은 개발자라면 한 번쯤은 읽어보기를 추천한다. 아니, 추천하는 정도가 아니라 한 번은 읽어봐야 하는 책이라고 생각한다.

댓글
댓글 쓰기