개발자가 읽은 철학
기획을 실제로 동작하는 무언가를 만들기 위해 개발자는 컴퓨터 앞에 앉아 커피를 벗 삼아 코드를 작성한다. 그 무언가는 DB가 될 수도, 서버가 될 수도, 웹이 될 수도, 앱이 될 수도... 기획의 목적에 따라 작업내용이 결정된다. 우리 개발자들은 보통 인간이 보기 편한 언어인 고차원적인 개발언어를 사용한다. 논리 구현에 절차적인 프로세스 흐름은 기본이고, 이를 더 사람이 이해하기 편리하게 하도록 객체지향 개념을 추가한다. 이후에는 코드 구조를 고민하면서 SOLID 5원칙과 같은 설계원칙을 따른다. 이는 직관적이며 유지보수가 편리한 방법을 찾기 위한 방식이다. 같은 이유로 개발자들은 코드리팩터링과 클린코드를 중요시한다. 누가 묻지 않아도 자연스럽게 해온 노력이며, 더 단순하게 만들기 위해 고민하는 개발자를 좋은 개발자로 생각한다.
오컴의 면도날은 "다른 모든 구성요소가 동일할 때, 가장 단순한 설명이 최선"이라는 철학용어다. 14세기 영국의 논리학자인 오컴이 빈번히 사용하여 그의 이름이 붙여졌다고 한다. 그의 저서에는 "필요 없이 복잡하게 만들지 말 것 Pluralitas non est ponenda sine neccesitate"이라고 표현했다고 한다. 이는 다시 말해 결론이 최소한으로 간결하고 직관적이며 위배 사례가 없는 설명이 우대받는다는 의미다.
수학용어인 소수를 설명하기 위한 증명으로 '유클리드의 증명'과 '리만제타함수를 이용한 오일러의 증명'이 있다. 두 증명 모두 소수를 설명할 수 있지만 '유클리드의 증명'이 더 간료하기에 오늘날 우리는 '유클리드의 증명'을 최우선적으로 학습자에게 가르친다. 이는 오컴의 면도날이 적절히 사용된 예시이다.
이는 무작정 단순하기 때문이 아니라,
결론이 간결할수록 다양한 환경에 대입하면서 결과를 예측하고,
필요하다면 실험을 설계하면서 학문을 발전시켜 나가기 좋기 때문이다.
CS에 대한 지식이 없더라도 컴퓨터가 0과 1로 이루어진 논리회로를 통해 동작한다는 것을 알고 있을 것이다. 그래서 많은 미디어에서 가상세계와 같은 디지털 환경을 표현할 때 무수한 0과 1이 쏟아지는 이미지를 차용한다. 그러나 개발자는 이런 0과 1로 코딩하지 않는다. 그럼에도 불구하고 0과 1이 쏟아지는 이미지는 맞는 표현이다.
기계어와 어셈블리어가 저차원언어(Low-level programming Language)를 대표한다. 기계어는 특별한 변환 과정 없이 컴퓨터가 직접 처리할 수 있는 유일한 언어이고, 어셈블리어는 기계어를 어느정도 추상화한 언어다. 이외에 많은 사람들이 알고 있는 C, C#, Java, Python, Javascript, R 과 같은 언어들은 레벨의 차이가 존재할 수 있지만 모두 고차원언어(High-level programming language)로 불리운다. 무슨 차이가 있는지는 직접 코드를 보는 편이 빠르다.
<피보나치 수열을 표현한 기계어(저차원언어)>
8B542408 83FA0077 06B80000 0000C383
FA027706 B8010000 00C353BB 01000000
C9010000 008D0419 83FA0376 078BD98B
B84AEBF1 5BC3
<피보나치 수열을 표현한 C언어(고차원언어)>
unsigned int fib(unsigned int n) {
if (n <= 0)
return 0;
else if (n <= 2)
return 1;
else {
unsigned int a,b,c;
a = 1;
b = 1;
while (1) {
c = a + b;
if (n <= 3) return c;
a = b;
b = c;
n--;
}
}
}
위 두 코드 모두 피보나치 수열을 표현한 코드다. 필자는 어셈블리어도 모르며 기계어는 더 어떤 의미인지 모른다. 다만, 고차원언어인 C언어는 별도로 공부하지 않은 언어이지만 어느정도 코드 흐름이 어떻게 될 것인지에 대해 유추가 가능하다. 저차원언어와 고차원언어 모두 동일한 기능을 수행할 수 있다. 인간이 느끼기에 저차원언어는 어렵고 복잡하며, 고차원언어는 상대적으로 간단하다. '유클리드 증명'과 '리만제타함수를 이용한 오일러의 증명'에서 '유클리드 증명'을 선택한 것과 동일한 논리이지 않은가.
그런데 재밌는 점이 저차원언어를 무시하고 고차원언어만 이해하고 사용하다가 뜻하지 않은 오류를 만나기도 한다. 자바스크립트의 플로팅 이슈가 그 일례다. 자바스크립트에서 소수점 연산을 하면 종종 정확한 소수값이 아닌 0.3000000000004와 같은 정확하지 않은 값의 결과를 확인할 수 있다. 이는 컴퓨터 메모리에는 한계가 있어서 무한 소수를 다 담지 못하고 중간에 잘라서 유한 소수로 저장하기에 발생하는 이슈이다. 인간이 보기 편리한 고차원언어는 컴파일러 혹은 인터프리터를 통해 기계어로 변환되어 동작한다는 점을 기억해야 한다. 그렇기에 기능수행의 정확성과 속도가 중요한 경우 고차원언어가 아닌 저차원언어를 사용한다.
위에서 설명한 고차원언어들은 절차지향언어와 객체지향언어로 분류할 수 있다. 그런데 많은 이들이 두 개념을 설명할 때 '절차지향은 순서! 객체지향은 객체!' 라고 뭉뚱그려 알려주는 경우가 있다. 하지만 C#, Java 같은 언어를 사용해본 경험이 있다면 객체지향언어가 절대로 절차를 무시하고 동작하지 않는 다는 점을 알 것이다. 이를 좀더 정확히 설명한다면 "절차지향은 논리순서를 먼저 만든 후에 자료구조를 설계하는 방식이고, 객체지향은 자료구조를 통해 모듈을 먼저 설계한 후에 논리순서를 만든다" 라고 하는 것이 비교적 정확하다. 절차지향 언어는 객체지향언어에 비해 기계어의 흐름과 닮아 있어서 속도가 더 빠르다. 그렇다면 개발자가 프로그래밍 언어를 선택하는 이유는 절차지향이냐 객체지향이냐이기 때문일까?
개발자는 존재하지 않는 것을 만드는 것처럼 보이지만, 대부분 현실에 존재하는 것으로 빗대어 소프트웨어 상에서 구현하는 경우가 많다. 적어도 내가 일하면서 경험한 프로젝트는 전부 현실의 것으로 비유할 수 있는 것들이었다. 이때에 객체지향 프로그래밍이 선사하는 캡슐화, 추상화, 상속, 다형성과 같은 개념들은 코드를 훨씩 직관적으로 만들어준다. 그렇기에 모델링이 쉬우며, 유지보수가 편리하다는 이점을 가진다. 절차지향이 아닌 객체지향을 선택하는 이유는 역시 코드가 더 인간에게 편리하게 작성되기 때문인 것이다. 그러나 절차지향과 객체지향에 대한 설명은 사실 오컴의 면도날에 비유하기에 부적절하다.
오늘날 실제로 가장 많이 사용하는 언어는 Python인데, 이유는 절차지향인지 객체지향인지 때문이 아니다. Python이 제공하는 AI 라이브러리 생태계가 매우 강력하기 때문이다. 또한 분야별 및 국가별로 주로 사용하는 언어도 다르다. 한국에서 주류 웹프로그래밍은 Java를 사용하지만 함수형 코딩이 가능한 Kotlin으로 전환하려는 추세이기도 하며, 애초에 외국은 Java가 대세언어가 아니다. 따라서, 다른 모든 구성요소가 동일하지 않기 때문에 프로그래밍 언어를 선택하는 이유로 절차지향에 비해 객체지향이 더 직관적이기 때문이라는 설명은 오컴의 면도날 이론에 위배된다.
위에서 언급한 바와 같이 프로그래밍 언어를 선택하는 이유로 절차지향에 비해 객체지향이 더 직관적이기 때문이라는 설명은 오컴의 면도날 이론에 위배된다. 다만 객체지향언어가 선사하는 모델링과 유지보수의 장점이 코드를 직관적으로 한다는 것은 분명한 사실이다. 객체지향은 여기서 더나아가 다섯가지 디자인 패턴을 원칙으로서 제공한다. 이 SOLID 원칙을 알고, 다양한 디자인패턴에 대해 이해하고 있는 편이 개발자의 능력에 분명한 차이를 만든다. 좋은 소프트웨어 설계를 위해서는 결합도는 낮추고 응집도는 높여야한다는 것이 핵심이다. 이는 분명 동일한 언어를 사용하면서도 프로그램의 퀄리티의 차이를 만드는 명확한 원인이 된다. 분명 더 좋은 소프트웨어는 직관적인 설계가 완료된 프로그램이며, 더 좋은 개발자는 이러한 지식을 배우고 적용하기 위해 노력하는 개발자다.
오컴의 면도날 개념은 개발 분야에 많은 부분에 적용될 수 있다. 언급하지 않았지만 UI/UX, 디자인, 설계 등. 더 간단한 것을 추구하려는 노력은 개발 분야의 대부분에 적용할 수 있다. 다만 필자가 오컴의 면도날을 보자마자 생각난 것은 개발자들의 숙명, 클린코드와 리팩터링이었다. 프로젝트를 시작하기 전에 동료들과 컨벤션을 맞추고, 어느 수준까지 추상화할지를 정하고, 어떤 프레임워크를 사용할지 정하고, 어떻게 배포전략을 가져갈 것인지, 개발전략으로 TDD를 쓸지말지 등 다양한 결정을 한다. 이 모든 것이 클린코드를 만들기 위한 노력이다. 프로젝트를 시작한 이후에도 더 직관적인 코드를 만들기 위한 리팩터링이 이어진다. 이는 마치 개발자가 오컴의 면도날을 들고 더 깔끔한 모습, 더 쉬운 코드를 만들기 위해 열의를 불태우며 면도하는 모습이 그려지지 않는가