이 글에 앞서

모든 악의 근원 : 불완전성의 원리

- 비 구조적 언어와 예견된 위기


소프트웨어 기술에 대한 이야기를 본격적으로 하기에 앞서, 소프트웨어 - 불완전성 - 인간의 인지 능력에 대한 관계를 명확히 해 둘 필요가 있다. 특히 이 글에서는 인간의 인지 능력의 특징을 살펴 보도록 하겠다. 인간의 인지 능력이 어떤 것인지 알아야만 왜 소프트웨어 기술이 현재와 같은 방향으로 발전하게 되었는지를 이해할 수 있다.(내가 누구이며 왜 내가 그것을 알아야 하는지를 아는 것을 메타 인지라고 한다. 소프트웨어 기술이 왜 필요한지를 아는 메타 인지가 있어야 기술을 배우고 익히는데 주저함이 없어지고 과감하게 도전하게 되는 것이다)

우선 불완전성의 원리와 소프트웨어의 관계에 대해서는 모든 악의 근원 : 불완전성의 원리에서 이야기 했다. 그리고 초창기 소프트웨어 기술이 어떤 문제로 불완전성을 관리하지 못했는지를 비 구조적 언어와 예견된 위기에서 이야기 했다.

불완전성과 인간

만약에 소프트웨어에 불완전성이 없었다면 어떤 세상이 되어 있었을까? 즉, "완전성의 원리"라는 것이 증명이 되었다고 가정해 보자. "완전성의 원리"는 어떤 공리계가 무모순이고, 그 공리계는 자기 자신의 무모순에 대한 정리를 포함할 수 있다고 가정해 보자. 좀 더 쉽게 이야기 하자면, 어떤 알고리즘이 있고, 이 알고리즘이 오류가 없을 때 이 알고리즘은 자기 스스로 오류가 없음을 증명할 수 있다고 하자. 좀 더 과감하게 가정해 보면 모든 알고리즘이 스스로 오류가 있는지 없는지를 검사할 수 있다고 해보자. 

만약 그런 세상이었다면 이제 모든 수학자들이 알고리즘 연구에 매달리게 되었을 것이다. "알고리즘 스스로 자신의 알고리즘이 완벽함을 증명하는 알고리즘"은 당연히 먼저 만들어지게 되었을 것이다. 그 다음에는 "스스로의 완전함을 증명할 수 있으면서 다른 알고리즘의 완전함도 증명할 수 있는 알고리즘"이 만들어졌을 것이다. 그리고 이게 발전하면 "모든 다른 알고리즘의 완전함을 증명할 수 있는 알고리즘"이 만들어지게 될 것이다. 

이것은 상상하기 힘들 정도로 엄청난 알고리즘임에 틀림 없다. 인간이 어떤 소프트웨어를 작성하더라도 이 알고리즘은 소프트웨어에 오류가 있음을 알아서 찾아 준다. 좀 더 지나면 찾는 것에서 끝나지 않고 사람이 의도한 바를 알고 알아서 오류가 없도록 만들어 주기도 할 것이다. 이제 사람이 소프트웨어를 개발한다는 것은 이 알고리즘에게 자신이 개발하려는 것이 무엇인지 그 의도를 알려 줄 수만 있으면 된다. 이런 세계에서라면 소프트웨어 개발자는 전 세계에 몇 명 되지 않을 것이다.

몇가지 좀 더 상상력을 발휘해 볼 수도 있지만 애초에 가정에서 시작한 것이니 이쯤에서 접어 두겠다. 그럼 이제 현실은 어떠한가? 모든 소프트웨어는 불완전성의 원리에 의해 지배 된다. 소프트웨어 스스로는 자신의 동작에 오류가 없음을 증명할 수 없다. 증명은 불완전성의 원리에 의해 불가능하다. 오직 잘 관리할 수만 있을 뿐이다. 그러면 무엇이 그것을 관리할 수 있을까?

답은 "인간의 두뇌" 밖에 없다. 다른 소프트웨어는 자기 스스로도 완전함을 증명할 수 없다. 따라서 당연히 다른 소프트웨어를 관리할 수 없다. 결국은 소프트웨어를 만드는 사람이 소프트웨어를 관리할 수 밖에 없다. 소프트웨어의 문제는 인간의 문제인 것이다.

자 여기서 결론을 먼저 이야기 해 보도록 하겠다. 모든 소프트웨어 기술, 정확히 말해서 소프트웨어를 만드는 기술은 (소프트웨어를 위해서 만들어진 것이 아니라) 인간을 위해 만들어진 것이다.

기술까지 가지 않고도 이야기 할 수 있는 부분은 많다. int count를 int a라고 쓰지 않는 이유는 무엇인가? int count든 int a든 컴퓨터에게는 어떤 차이도 없다. 둘 중 무엇을 쓰든지 컴퓨터에는 어떤 문제도 발생하지 않는다. 문제는 인간에게 생긴다. 인간은 count 대신 a를 사용하는 것과 같은 짓을 10번만 해도 코드를 이해하기 힘들어진다. 코드를 이해하기 힘들어지면 소프트웨어를 개발하는 과정도 힘들어진다. 그리고 소프트웨어는 스스로 완전함을 증명할 수 없다. 그러면 소프트웨어에는 문제가 생긴다. 간단한 예시였지만 불완전성과 인간의 관계가 어떤 것인지 이해할 수 있었을 것이다.


인간의 인지 능력

소프트웨어의 불완전성을 관리하는 것은 인간의 몫이다. 그 중에서도 특히 인간의 두뇌이고, 이 중에서도 소프트웨어를 다루는 능력이 될 것이다. 우선 인간의 두뇌 능력을 포괄적으로 인지 능력이라고 부르도록 하자. 인지 능력이란 무엇인가를 이해하는 능력이다. 두뇌가 뛰어난 사람들은 더 많은 것을 이해할 수 있다. 그러나 두뇌가 뛰어나다고 해서 모든 것에 대한 이해가 다른 사람들보다 뛰어난 것은 아니다. 두뇌의 능력은 사람에 따라 다르고 경험에 따라 다르다. 그래서 개개인의 특성을 이야기하는 것은 별 의미가 없다. 우리에게 필요한 것은 소프트웨어를 관리할 수 있는 두뇌의 능력을 이해하는 것이다.

인지 능력 중 필요한 것을 나열하는 것으로 시작해 볼 수도 있다. 하지만 인간의 두뇌는 당연하고 일상적인 것을 받아 들일 때에는 그 중요성을 잊어버리는 경향이 있다. 그래서 두뇌의 능력에 맞게 오히려 인지를 방해해서 이해하기 어렵게 하는 것들이 무엇인지 이야기 해보자.


사실이 아닌 것

개인적인 경험일 뿐이지는 모르겠지만 다른 사람과 이야기를 하다 보면 너무나도 당연한 사실을 아니라고 우기는 사람들이 있다. 그래서 사실이 아닌 것을 사실이라고 말하는 사람에게 그 이유를 물어 보게 된다. 왜 믿는지를 이야기하는 과정에서 잘못된 가정이나 왜곡된 사실이 있는지를 알아보기 위해서다. 그래서 이야기를 듣고 나서는 일단 그렇게 믿게 된 이유를 알게 되긴 했다. 그 자리에서 그것을 알고 나서 얼마 지난 후에 그 사람의 이야기를 다시 조립해보면 다시 이해가 가지 않는다. 그리고 그 사람과의 대화 내용이 제대로 기억이 나질 않는다.

공감이 가는 이야기인가? 사람은 사실이 아닌 것을 기억하기 힘들어 한다. 사람의 기억은 (적어도 그 개인에게는) 너무나 자명한 사실이다. 사실이라는 것은 생존을 이롭게 만든다. 송이 버섯을 먹을 수 있다는 것은 사실이다. 그것을 기억하고 있으면 생존에 더 이롭다. 그래서 사람들은 생존에 이로운 것을 사실로 기억하고, 다른 것을 기억해야 할 때 이미 가지고 있는 기억과 연관지어 다른 것들을 계속 생각하게 된다. 그래서 사실인 것은 기억하기가 쉽고 사실이 아닌 것은 기억하기가 어렵다.

이것이 소프트웨어와 무슨 연관이 있을까? 아래 코드를 보자.

class Dog{

    public final int LEGS = 2;

    public final int HEAD = 25;

    public final String name = "cat";

}

class Cat{

    public final int LEGS = 4;

    public final int HEAD = 1;

    public final String name = "cat";

}

첫번째 클래스와 두번째 클래스 중에서 기억하기 쉬운 것은 어떤 것인가? 아마 Dog 클래스를 이해하려고 시도하면 머리 속에 혼란이 오기 시작할 것이다. 이 코드를 보고 기분이 나빠지기 시작했다면 미안하다. 하지만 이 코드로 의도하고자 했던 것은 이해할 수 있을 것이다. 우리는 소프트웨어를 작성하면서 소프트웨어가 완전히 논리적인 것이며, 그렇기 때문에 실제 세계와는 생각하는 방식이 완전히 다를 것이라고 추측하곤 한다. 하지만 이 예제에서 보듯이 소프트웨어를 만드는 일은 실제 세계에서 하는 일과 유사하다. 즉, 기억하고 있는 것과 새로 기억해야 할 것들을 연관지어서 생각하는 것이다. 단기적으로 Dog 클래스를 기억할 수는 있다. 하지만 그 기억은 오래가지 못할 것이다. 우리가 아는 실제 세상, 즉 사실과 다르기 때문이다. 사실과 다른 것은 생존에 도움이 되지 않는다. 따라서 머리 속에서 금방 지워진다.


개념의 매핑이 올바르지 않은 것

빨강 노랑 파랑 녹색 주황

위의 단어 중 맞는 매핑이 맞는 단어는?

답을 3초 이내에 맞춰냈다면 대단한 사람이라고 칭찬할 수 밖에 없다.


그러면 아래에 주어질 단어와 색깔의 매칭은 맞는가?

빨강 노랑 파랑 녹색 주황

성인이 이 매칭에 3초 이상 걸렸다면 약간 문제가 있는 것일 수도 있다. 이 문제는 내가 매우 좋아 하는 문제 중 하나이다. 위의 것은 맞는 건 단 하나 뿐인데 찾는데 시간이 걸리고, 아래는 모두 매칭 시켜 봐야 하는데도 시간이 덜 걸렸을까?

답은 매우 간단하다. 우리가 색깔에 대한 단어를 훈련할 때 그렇게 훈련했기 때문이다. 즉 빨강 색깔을 보여 주고 단어를 이야기 해주거나 빨강이라는 단어를 주고 색을 찾도록 훈련해 왔기 때문이다. 우리가 익히 아는 색깔이 아닌 다른 색깔을 주어주고 이름을 이야기 했을 때 다른 색에 비해 새로 배운 색깔을 찾아 내는데 어려움을 겪는 것도 같은 이유다. 이 매칭 문제는 훈련이 없이는 얻어지는 것이 아니다. 첫번째 문제가 어려운 이유는 단순하다. 저런 식으로 색깔과 단어를 매칭시키는 훈련을 한 적이 없기 때문이다.


비 구조적인 것

비 구조적이라는 말은 구조가 없다는 말이다. 간단히 말하면 여러 부분으로 분리될 수 없음을 의미 한다. 소프트웨어에서 보면 우리가 익히 알고 있는 함수나 객체, 패키지나 모듈 단위로 쪼갤 수 없음을 의미한다. 현대에 와서 이런 소프트웨어 구조는 상상도 할 수 없다. 그러기에는 현대 소프트웨어는 너무 크다.

그런데 잠깐 생각해보자. 왜 사람은 비 구조적인 것을 구조적인 것보다 어렵게 느끼는 것일까? 왜 비 구조적인 것은 소프트웨어 크기를 크게 만들 수 없고, 구조적인 것은 더 크게 만들 수 있는 것일까?

일단 구조적인 것은 부분으로 분리가 가능하다. 부분으로 나누고 나면 관리하기가 편해지는데 이는 로마에서 말하는 "분할하여 통치하라"는 격언과 딱 들어 맞는다. 사람은 잡다한 것들의 덩어리 보다는 명확히 규정되어 있는 것들의 부분 부분을 이해하는 데 훨씬 뛰어나다. 이것이 어느 정도나 그러냐 하면, 실제 세계는 매우 다양한 양상들의 뒤섞임을 통해 만들어지는데도 불구하고 자꾸 분리하려고 할 정도다. 미시적으로 보면 동물과 식물은 명확히 구분되지 않는다. 미생물의 영역에서 보면 동물인지 식물인지를 구분하기 힘든 생물들이 존재하기 때문이다. 하지만 우리는 생물이 동물과 식물로 구분되어 있다고 규정 짓는다. 이 편이 이해하기 훨씬 쉽기 때문이다. 동물과 식물을 나누면 동물의 특성과 식물의 특성을 규정 짓기가 쉬워지고, 그러고 나면 동물과 식물을 더 이해하기 쉬워진다. 그리고 나면 자연스레 그 경계 영역에 있는 생물의 특성도 이해하기가 쉬워진다.(이러한 방식이 문제가 되는 곳도 있다. 바로 인간 스스로를 이렇게 구분 짓는 경우다. 그 문제도 중요하긴 하지만 주제 밖이므로 일단 논리적인 것에 집중하자.)

분할은 인간의 인지 능력의 기본 특성이다. 지식은 분리를 통해 시작된다. 소프트웨어를 배울 때 처음에는 무작정 다 같은 소프트웨어인 줄 알고 시작하지만 금새 여러 언어가 있음을 알게 된다. 하나의 언어를 배우고 나면 다른 언어와의 차이를 모르지만 여러 언어를 배우다 보면 왜 언어 마다 다른 특성이 있는지 어느 정도 이해하게 된다. 문법을 익히고 나면 구조 설계라는 것이 있다는 것을 알게 되고, 코딩 룰이나 디자인 패턴과 같이 좋은 설계나 구현의 방식들이 있음을 알게 된다. 이런 형태로 지식은 계속 분화하면서 이해는 계속 깊어지게 된다.

그런데 만일 소프트웨어가 구조가 없다면 어떻게 될까? 그 소프트웨어를 접한 사람은 그것을 이해하기 위해 알고 있는 지식들을 총 동원하게 될 것이다. 장담하건데 적어도 전체 코드의 일부를 분할해서 그 블럭에 이름을 붙이려고 시도할 것이다. 설령 goto 문으로 이리저리 얽혀 있는 코드라고 해도 그렇게 이름을 붙여서 한 덩어리로 된 소프트웨어를 어떻게든 분할해 놓는 편이 이해하기가 훨씬 쉽기 때문이다.


너무 큰 것

너무 큰 소프트웨어는 인지 능력에 방해가 된다. 인간의 두뇌는 명백히 제한적인 리소스만 가지고 있기 때문이다. 소프트웨어의 위기는 소프트웨어가 커지면서 시작되었다. 그리고 그러한 노력이 구조적 언어와 객체지향 언어를 만들어 냈다. 하지만 언제나 이런 통찰력 있는 시도들을 모든 사람이 따르는 것은 아니다. 여전히 수천줄짜리 함수를 누군가는 만들어 내고 있고, 그 많은 소스들을 한 개의 파일에 담으려고 노력하고 있다. 마치 내일은 없는 것처럼.


비 가시적인 것

눈으로 볼 수 없는 것은 이해하기도 어렵다. 잘 그려진 플로우 차트나 시퀀스 다이어그램 만으로도 복잡한 코드를 손쉽게 이해해 본 경험이 있을 것이다. 코드는 스스로 그림을 그려주지 않기 때문에 코드만을 가지고는 소프트웨어의 동작을 이해하기 어려울 수 있다. 이것이 정적인 뷰와 동적인 뷰가 따로 존재하는 이유이다.


표현하기 어려운 것

모든 소프트웨어 개발자들은 이름을 짓는데 어려움을 느낀다. 어떤 통계에서는 소프트웨어 개발자로서 가장 어려운 것이 무엇인지 조사했더니 절반 가까이가 이름 짓는 것이라고 말했다고 한다. 이름을 짓는 것이 어려운 이유는 자신이 만들어 낸 것을 현실 세계에서 찾아 낼 수 있는 개념이 아니거나, 이미 유사한 개념들을 다른 곳에 많이 써버렸기 때문이다. 그래서 현실 세계에서의 개념이 빈약한 사람들은 프로그래밍을 잘 할 수 없다. 이름을 지을 수 없기 때문이다. 이름을 잘 지을 수 있는 것도 능력이다.

이름이 없는 것을 이야기 하는 일을 이야기 해보자.

"지금으로부터 바로 이전 해가 뜬 시점에서 얼마 지나지 않은 그 때에 색깔이 얼룩이거나 단색이면서 네 개의 지면에 붙은 관절이 있는 부위로 걷고 머리 쪽에 밥을 먹는 구멍으로는 "야옹"이라는 소리를 내는 짐승을 보았다."

"아침에 고양이를 보았다"

같은 이야기를 하려 해도 너무 힘들다. 이름이 없다는 것은 이만큼 힘들다. 이름이 없어도 표현하려고 하다보면 말은 길어지고 의미 전달은 힘들어진다. 그 마저도 표현력이 뛰어나고 이해력이 뛰어난 두 사람이 만났다면 모를까 이름 없이 소프트웨어의 구현을 이야기 한다는 건 너무 힘든 일이다. 


너무 긴 시간

시간은 기억의 적이다. 모든 기억은 시간이 지날수록 희미해진다. 아무리 날카로운 지성이라고 해도 긴 시간 앞에서는 무기력 할 수 밖에 없다. 시간이 지나면서 프로그래밍 실력이 늘어났다고 해서 예전에 만든 코드에 대한 기억력이 더 높아지지는 않는다. 따라서 인지 능력을 최대한으로 발휘하기 위해서는 가능한 짧은 시간 내에 필요한 모든 일을 처리해야 한다.

어떤 일을 인지하는데 시간이 문제가 되는 또 다른 이유는 시간이 길어지면 길어질수록 집중력은 떨어지고, 다른 일들이 중간에 발생할 여지가 커진다는 점이다. 그러면 다시 이전의 이해로 돌아가는데 시간이 걸린다. 이것은 효율적으로 인지 능력을 사용하는 방법이 아니다.


그리고 소프트웨어

이처럼 인지능력에 해가 되는 것들이 무엇인지 알게 되었으니 이를 소프트웨어와 연관 지어 보도록 하자. 소프트웨어는 불완전성을 가지고 있다. 따라서 소프트웨어를 개발할 때 불완전성을 관리할 수 있는 도구는 오직 두뇌밖에 없다. 그리고 두뇌에는 한계가 있다. 이 한계 내에서 두뇌를 효율적으로 사용하려면 인지 능력을 최대한 발휘할 수 있는 형태로 소프트웨어를 관리해야 한다. 따라서 인지능력이 올바르게 발휘되도록 하려면 소프트웨어는 다음과 같은 특성을 가지고 있어야 한다.

최대한 실제 세계와 유사하도록 만들 것, 개념( = 메소드 혹은 변수, 클래스의 명칭)과 실제(구현)가 맞도록 할 것, 구조적일 것, 작은 단위로 나눠져 있을 것, 가시적인 도구를 사용할 것, 명확하게 표현할 것, 개발의 주기를 짧게 가져갈 것.

이와 같이 불완전성을 관리하는 도구로서의 두뇌를 최대한 활용할 수 있는 형태로 소프트웨어를 만들어야만 성공적인 소프트웨어를 만들 수 있다. 소프트웨어 분야에서는 이러한 특성을 이미 경험적으로 알고 이를 소프트웨어 기술 형태로 지속적으로 발전 시켜왔다. 어떤 때에는 선구자들의 통찰력을 통해, 어떤 때에는 그 통찰을 언어나 툴, 프레임워크에 담음으로써 기술적인 완성도를 높여 왔다. 소프트웨어는 거의 컴퓨터 초창기부터 있어 왔으나, 소프트웨어 기술을 기반으로 해서 세계 최고의 매출을 올리는 기업들이 속속 등장하는 이유도 이러한 소프트웨어 기술들이 만들어지고 많은 개발자들이 이를 적용하게 되었기 때문이다.

이 다음 글에서는 이렇게 인간의 인지 능력을 최대한 활용할 수 있도록 만들어 준 소프트웨어 기술들에 대해 하나씩 알아 보도록 하겠다.

Posted by 이세영2
,