해설이 글은 무엇을 말하는가
1972년, 다익스트라는 컴퓨터 과학 최고의 영예인 ACM 튜링상을 받았다. 그가 수상 강연에서 한 일은 자기 분야를 칭송하는 것이 아니라, 그 분야에 칼을 겨누는 것이었다.
「겸손한 프로그래머」는 자전적 회고의 형식을 빌린 선언문이다. 다익스트라는 1952년 직업조차 존재하지 않던 시절의 첫 프로그래밍 일자리에서 시작해, 20분 만에 최단경로 알고리즘을 설계한 경험, 첫 ALGOL 60 컴파일러를 만들며 "영리한 트릭"의 재앙을 목격한 순간을 이야기한다. 그러나 이 회고는 하나의 주장으로 수렴한다. 프로그래밍의 진짜 문제는 기술이 아니라 인간 정신의 한계다.
이 강연이 놓인 시대적 맥락은 "소프트웨어 위기(software crisis)"였다. 하드웨어가 강력해질수록 야망이 커지고, 소프트웨어는 예산을 초과하고 일정을 넘기며 버그투성이 제품을 쏟아냈다. 당시의 통념은 더 많은 인력과 더 나은 관리로 이를 해결할 수 있다는 것이었다. 다익스트라는 단호히 거부한다. 문제는 본질적으로 지적이다 — 엄청나게 복잡한 논리적 구조물을 어떻게 설계하고 이해할 것인가의 문제이며, 더 나은 지적 도구와 더 나은 지적 훈련만이 답이다.
그 도구가 바로 추상화와 관심사의 분리다. 인간의 단기 기억은 불과 몇 개의 항목만 담는다. 그러므로 프로그램은 그 복잡성이 "지적으로 관리 가능한(intellectually manageable)" 수준에 머물도록 구조화되어야 한다. 그리고 우리는 테스트로 버그의 부재를 증명할 수 없으므로, 프로그램과 함께 그 정확성 증명을 구성해야 한다.
이 강연이 고전이 된 이유는, 그것이 단순한 기술 권고가 아니라 직업 윤리를 제시했기 때문이다. "겸손"이란 자기비하가 아니다. 그것은 인간 지성의 한계를 정직하게 인정하고, 그 한계에 맞춰 일하는 행위다. 컴퓨터가 무한한 정밀성을 요구하고 최소한의 불완전성도 용납하지 않기에, 겸손하지 않은 프로그래머는 어리석은 프로그래머다.
원문 · 01프로그래밍의 여명기
오늘 저녁, 제 앞에 펼쳐진 이 기회를 어떻게 활용할지 곰곰이 생각해 보았습니다. 저에게 기대되는 것이 무엇인지 스스로 물어보았습니다. 한편으로는 제가 학문적 배경을 가졌기에, 발견의 즐거움만 가르쳐야 하고 개인적인 의견은 삼가야 할지도 모르겠습니다. 다른 한편으로, 여러분 대부분이 산업계의 실무자들이기에, 저의 학문적 견해는 제가 종사하는 일의 현실과 동떨어진 것으로 치부되어 버릴 수도 있겠습니다. 하지만 저는 이 기회를 저만의 방식대로 활용하기로 결심했습니다. 다소 자전적인 성격의 이야기가 될 것이고, 솔직한 의견도 피력할 것입니다. 여러분이 흔쾌히 받아들이지 않으실 의견도 있을 것입니다. 그러한 의견들은 그저 하나의 의견일 뿐이지만, 저는 그것들에 대한 충분한 근거를 제시할 것이며, 성숙한 청중으로서 여러분이 스스로 판단하시리라 믿습니다.
지금부터 정확히 이십 년 전인 1952년 초봄, 저는 네덜란드의 수학 센터에서 첫 프로그래밍 직책을 맡았습니다. 당시 저는 암스테르담 대학의 이론 물리학과 학생이었습니다. 뒤돌아보면, 가장 놀라운 점 중 하나는 제가 느린 발전 속도를 얼마나 당연하게 받아들였는가 하는 것입니다. 1952년 당시 저는 미래에 프로그래밍을 직업으로 삼으리라는 것을 의식하지 못했고, 실제로 그러한 직업이 존재하지 않았기 때문입니다. 프로그래밍이 한 나라의 발전에 있어서 어느 날 중요한 역할을 할 수 있다는 발상을 조금이라도 지지하는 어떠한 물질적·이론적·역사적 증거도 전혀 없었습니다.
제가 1957년 봄에 결혼했을 때, 네덜란드에서는 결혼 예식의 필수 부분으로 직업을 밝혀야 했습니다. 그래서 저는 직업을 "프로그래머"라고 밝혔습니다. 그러나 이것은 암스테르담 당국에서 받아들여지지 않았는데, 그러한 직업은 존재하지 않는다는 이유에서였습니다. 결국 "이론 물리학자"가 되었는데, 이것이 흠잡을 데 없이 인정된 직업이었기 때문입니다. 수년이 흐른 뒤, 제 상관 A. 반 베인하르던에게 이 경험을 이야기했습니다. 그는 저보다 십 년 이상 연장자였고, 그가 제게 기대하는 것은 우리가 프로그래밍을 존경받는 학문으로 만드는 것을 평생의 사명으로 삼는 것이라고 설명해 주었습니다. 이러한 그의 소명 의식의 전환이 저에게 큰 영향을 미쳤습니다.
초기 컴퓨터들은 제각각 고유한 기계들로, 실험실 환경에서 연구 그룹에 의해 제작되었습니다. 기계가 한 시간 동안 고장 없이 작동하면 정말 운이 좋은 것이었습니다. 한편으로는 프로그램 작성에 매우 보수적인 기법만 사용되었고, 다른 한편으로는 프로그램 검증을 거의 하지 않았습니다. 프로그램을 테스트하는 것이 최선이라는 소박한 신념이 팽배했습니다. 물론 매우 제한된 테스트 절차가 충분하다고 당연시했는데, 그들은 아직 프로그램이 얼마나 미묘하게 잘못될 수 있는지에 대한 개념이 없었기 때문입니다.
저는 두 가지 형성적 경험을 했습니다. 첫째, 트랜지스터 기계를 위한 부동 소수점 해석기를 설계하고 구현했는데 — 그것이 제 첫 프로그램이었습니다 — 구현 테스트를 제대로 해 본 적은 없었습니다. 저는 그것을 탁자 위에서 검증했고, 나중에 테스트해 본 결과 올바른 것으로 증명되었습니다. 이 경험은 강한 인상을 남겼고, 저는 가능하면 실수를 피하는 법을 제대로 배워야겠다고 결심했습니다. 둘째, 네덜란드 계산 센터들의 공통 라이브러리를 위한 품질 관리 과업이 저에게 할당되었는데, 그 일은 다른 프로그래머들의 작품을 깊숙이 들여다볼 기회를 주었습니다 — 그것이 무엇보다도 저 자신의 표준을 높이고 취향을 형성하는 데 도움이 되었습니다.
그 직후, 우리 하드웨어 그룹이 ARMAC의 공개 발표를 계획하면서 기계가 뭔가 인상적인 일을 해야 했기에, 저는 최소 경로 문제를 위한 알고리즘을 설계했습니다. 아름다운 프로그램을 설계하고 구현하는 데 약 이십 분이 걸렸습니다. 솔직히 말하면, 저는 플로우차트를 그리지 않았고, 솔루션에 대한 완전한 확신을 가지고 있었으며, 해답의 발견 과정에서 사실상 ARMAC과 그 명령어 코드에 대해 전혀 생각하지 않았습니다.
원문 · 02자동 컴퓨터의 등장
제가 프로그래밍에 입문한 지 불과 몇 년 만에, 자동 컴퓨터의 상황이 급격하게 달라졌습니다. 우선, 기계의 신뢰성이 크게 향상되었습니다. 이제 사람들은 기계가 꽤 오랜 기간 동안 제대로 작동하기를 당연히 기대하게 되었고, 기계가 실패했을 때 그것은 뉴스거리였습니다. 신뢰성의 향상과 함께, 기계 아키텍처와 애플리케이션의 야망이 함께 성장했습니다. 저는 바로 이러한 발전들이 정확히 프로그래밍 문제(programming problem)에 대한 우리의 눈을 열어 주었다고 믿습니다.
수많은 새로운 애플리케이션들은 특이한 프로그래밍 기법들을 필요로 했는데, 그중에서도 운영 체제가 단연 으뜸이었습니다. 사람들이 "프로그래밍을 어떻게 해야 하는가?"라는 질문을 명시적으로 제기하면서, "이런저런 방식으로 프로그래밍 하지 않아야 한다"는 부정적인 규칙들이 생겨났습니다. 하지만 저는 단지 "하지 마라"는 식의 규칙에 전적으로 만족하지 않았습니다. 저는 왜 그러한 기법이 나쁜지에 대한 좋은 이유를 원했고, 그것은 우리 인간의 정신 구조에 대해 생각하게 만들었습니다.
기계가 신뢰할 수 있게 되고 프로그래밍 비용이 초점을 받게 되면서, 효율성이 절박하게 중요했습니다. 사람들이 서로에게 "영리한" 트릭을 사용해서 몇 마이크로초를 절약하는 방법을 자랑하던 시절이었습니다. 숙련된 프로그래머의 특성으로서 "영리한 트릭"이 칭찬받던 시절이었습니다. 하지만 이미 그때, 저는 가끔 영리한 트릭들이 재앙으로 이어지는 것을 목격했습니다. 첫 번째 ALGOL 60 컴파일러를 담당하던 때, 한 프로그래머가 어떤 변수를 오버로드하여 두 가지 다른 목적으로 사용했습니다. 이 영리한 트릭은 코드를 이해하기 어렵게 만들었고, 나중에 컴파일러를 수정할 때 엄청난 시간이 낭비되었습니다. 그 순간 저는 "프로그래밍에서 영리함은 어느 수준까지 허용되어야 하는가?"라고 처음으로 스스로에게 물었습니다.
문제의 규모에 대해 처음 깨달았을 때 저는 우울해졌습니다. 그러나 서서히, 그것이 불가능한 것이 아니라는 생각을 하게 되었습니다. 결국, 프로그램은 우리가 이해할 수 있도록 만들어져야 합니다. 그리고 우리가 그것을 이해할 수 있다면, 우리의 이해력으로 증명(proof)을 구성할 수 있을 만큼 충분히 이해할 수 있어야 합니다.
원문 · 03추상화의 발견
1960년대 초에 저는 "추상화(abstraction)"와 "관심사의 분리(separation of concerns)"의 중요성을 점점 더 깊이 자각하게 되었습니다. 저는 이것을 그 후 제 지적 발전의 핵심으로 간주해 왔습니다. 무엇보다도, 저는 프로그램이 이해 가능하도록 구성되어야 한다는 것을 배웠습니다. 이해 가능성의 필요성은 프로그램의 정확성에 대한 확신을 갖기 위한 전제 조건입니다. 둘째로, 저는 저 자신의 이해력의 극도로 제한된 크기를 점점 더 의식하게 되었습니다. 우리 모두는 그렇습니다. 그러므로 프로그램의 복잡성을 "지적으로 관리 가능한" 수준으로 유지하는 것이 필수적입니다.
저는 "추상화"를 도구로 생각하기 시작했습니다. 한 수준에서 프로그램에 대해 추론할 때, 저는 다른 수준의 세부 사항을 의식적으로 무시합니다. 이러한 목적으로, 수준들은 명확하게 분리되어야 합니다.
여기서 더 넓은 맥락을 소개하겠습니다. 컴퓨터는 자연이 우리에게 제공한 것이 아닙니다. 그것들은 인간의 창조물, 인간의 인공물입니다. 그것들을 설계하고, 구성하고, 프로그래밍하고, 사용하는 것은 (새로운 종류의) 인간 활동입니다. 인간 정신의 구조에 대한 분석 없이 컴퓨팅의 과학은 있을 수 없습니다. 컴퓨터의 인공적 특성과 그것이 인간에게 제기하는 새로운 종류의 도전의 결합은 "컴퓨팅 과학"을 고유하고 새로운 학문으로 만듭니다.
원문 · 04프로그래밍 언어의 역할
FORTRAN을 먼저 언급하겠습니다. 개발 시점에서 FORTRAN은 대단한 성취로 칭송받을 만했습니다. 그러나 오늘날의 눈으로 보면, 그것은 특별히 우아하지도, 안전하지도, 프로그래밍 방법론의 발전에 자극적이지도 않은 언어입니다. 그 광범위한 사용으로 인해 그것은 이제 프로그래밍 언어 설계의 발전에 가장 강력한 장애물 중 하나가 되었습니다. FORTRAN은 — 그 실질적인 영향력 때문에 — 프로그래머의 정신을 과거의 실수에 연쇄시키는 도구가 되었습니다.
LISP는 표현력의 측면에서 영감을 주는 많은 성질을 가지고 있습니다. 그러나 LISP에 대해 제가 불안한 점은 효율성에 대한 부주의한 태도입니다. ALGOL 60은 산업계에서 FORTRAN과 같은 상업적 성공을 거두지 못했지만, 저는 ALGOL 60을 컴퓨터 과학 역사에서 가장 중요한 언어 중 하나로 간주합니다. 최초로 재귀(recursion)를 도입한 고급 언어였고, 최초로 형식적으로 정의된 언어였습니다. BNF를 사용한 통사 정의는 그 자체로 하나의 돌파구였습니다.
PL/I는 IBM이 FORTRAN과 COBOL의 후계자로 설계한 언어로, 당시 알려진 거의 모든 프로그래밍 언어 기능을 하나의 언어에 담으려 했습니다. 저는 그것을 7000개의 버튼이 달린 비행기 조종석에 비유하곤 합니다. 조종사가 어떤 버튼을 눌러야 할지 모른 채 거기 앉아 있는 것입니다.
프로그래밍 언어는 도구입니다. 그것은 프로그래머가 생각을 표현하는 도구이자, 컴퓨터에게 무엇을 해야 하는지 알려주는 도구입니다. 복잡한 언어는 복잡한 생각을 조장하고, 단순한 언어는 단순한 생각을 조장합니다. 그러므로 저는 단순한 언어를 선호합니다. 언어는 우리가 어떤 생각을 할 수 있고 어떤 생각을 쉽게 표현할 수 있는지를 결정합니다.
원문 · 05지적 관리 가능성과 정확성
프로그램은 그 복잡성이 프로그래머의 이해력 범위 내에 있을 때 "지적으로 관리 가능"합니다. 프로그래밍이 어려운 이유 중 하나는 우리의 정신적 능력이 제한되어 있기 때문입니다. 우리는 동시에 많은 것을 추적하지 못합니다. "관심사의 분리"란 한 번에 한 가지 측면에만 집중하고, 다른 측면은 의식적으로 무시하는 것입니다. 프로그램의 논리적 정확성을 검증할 때, 우리는 일시적으로 효율성에 대해 생각하지 않습니다. 그리고 효율성을 최적화할 때, 우리는 정확성이 이미 확립되었다고 가정합니다.
프로그램이 올바르다는 것을 어떻게 확신할 수 있습니까? 전통적인 접근 방식은 테스트입니다. 그러나 테스트에는 근본적인 한계가 있습니다.
저는 다른 접근 방식을 제안합니다. 프로그램과 함께 그것의 정확성 증명(correctness proof)을 구성하는 것입니다. 이것은 프로그램을 작성한 후에 증명을 하는 것이 아니라, 프로그램의 개발과 함께 증명을 개발하는 것을 의미합니다. 이러한 접근의 장점은 프로그램의 구조 자체가 증명 가능하도록 설계된다는 것입니다.
프로그램을 관리 가능하게 만드는 핵심 기법은 계층적 구조(hierarchical structure)입니다. 가장 낮은 수준에는 하드웨어가 있고, 그 위에 운영 체제, 프로그래밍 언어, 애플리케이션이 있습니다. 각 수준은 그 아래 수준의 복잡성을 추상화합니다. 상위 수준의 정확성은 하위 수준이 그 명세에 따라 동작한다고 가정하고 증명할 수 있습니다. THE 다중프로그래밍 시스템의 설계에서 우리는 이 원칙을 적용했습니다.
1960년대 후반, "소프트웨어 위기"라는 용어가 만들어졌습니다. "소프트웨어 공학(Software Engineering)"이라는 용어가 이 맥락에서 등장했지만, 저는 약간의 불편함을 느낍니다. 그것이 단순히 좋은 관리 관행의 적용으로 문제를 해결할 수 있다는 인상을 줄 수 있기 때문입니다. 저는 문제가 그보다 더 깊다고 믿습니다. 우리가 직면한 문제는 본질적으로 지적인 문제입니다 — 엄청나게 복잡한 논리적 구조물을 어떻게 설계하고, 구현하고, 이해할 것인가의 문제입니다. 이것은 더 많은 인력이나 더 좋은 관리로 해결될 수 없습니다.
원문 · 06교육과 겸손
만약 프로그래밍이 본질적으로 지적 활동이라면, 우리는 학생들에게 단순히 현재의 프로그래밍 언어와 도구의 사용법을 가르쳐서는 안 됩니다. 대신, 우리는 그들에게 사고하는 방법을 가르쳐야 합니다 — 추상화하는 방법, 문제를 분해하는 방법, 정확성에 대해 추론하는 방법을. 일단 나쁜 습관이 형성되면 나중에 고치는 것은 매우 어렵습니다. 그러므로 첫 번째 프로그래밍 과정은 매우 신중하게 설계되어야 합니다.
왜 겸손이 필요합니까? 첫째, 우리 인간의 지적 능력은 매우 제한되어 있습니다. 우리의 단기 기억은 불과 몇 개의 항목만 담을 수 있습니다. 둘째, 프로그래밍 문제의 복잡성은 엄청납니다. 중간 규모의 프로그램조차도 수천 개의 상호 작용하는 부분으로 구성되며, 가능한 실행 경로의 수는 천문학적입니다. 셋째, 우리의 지식은 불완전합니다. 우리가 지금 올바르다고 생각하는 것이 몇 년 후에는 잘못된 것으로 밝혀질 수 있습니다.
그러므로 저는 프로그래머로서의 우리 자신에게 권합니다. 우리의 과업의 엄청난 어려움에 대한 완전한 인식을 가지고 접근합시다. 겸손하고 우아한 프로그래밍 언어를 선택합시다. 인간 정신의 본질적인 한계를 존중합시다. 그리고 우리의 프로그램을 지적으로 관리 가능한 범위 내에 유지합시다.
컴퓨터는 우리 시대의 가장 놀라운 도구 중 하나입니다. 그러나 그것은 또한 가장 위험한 도구 중 하나이기도 합니다. 컴퓨터가 무한한 정밀성을 요구하고 최소한의 불완전성도 용납하지 않기 때문입니다. 그러므로 저는 우리 모두가 겸손한 프로그래머가 되기를 촉구합니다. 우리의 한계를 인식하고, 복잡성을 존중하고, 정확성을 위해 노력합시다. 그리고 우리가 하는 일의 중요성을 결코 잊지 맙시다. 감사합니다.
모션핵심 개념 — 지적 관리 가능성
다익스트라의 중심 주장은 단순한 그림으로 요약할 수 있다. 인간의 머리는 작다. 프로그램의 복잡성은 거대하다. 평평한 — 구조 없는 — 프로그램에서는 모든 부분이 모든 부분과 얽혀, 이해해야 할 상호작용의 수가 폭발한다. 반면 계층적으로 추상화된 프로그램에서는 각 수준이 아래 수준의 세부를 숨겨, 한 번에 들여다봐야 할 것이 인간 정신의 범위 안에 머문다. 아래 모션은 두 세계를 나란히 보여준다.