Docs | Src |   A-  |  A+   | -/-

Metadata

E.W. 다익스트라 아카이브: 겸손한 프로그래머(EWD 340)

겸손한(humble) 프로그래머(programmer)
지은이
에츠허르 W. 다익스트라

오랜 우연의 연속 끝에 나는 1952년 봄 첫날 공식적으로 프로그래밍이라는 직업에 들어섰다. 내가 추적해 본 한도에서는, 내 나라에서 그 일을 한 첫 번째 네덜란드인이었다. 돌이켜보면 가장 놀라운 점은, 적어도 내가 속한 세계에서는 프로그래밍이라는 직업이 너무도 느리게 등장했다는 사실이다. 지금 와서는 믿기 어려울 정도로 느렸다. 하지만 그 시절의 생생한 기억 두 가지가 있다. 그 느림을 의심할 여지 없이 보여주는 기억들이다. 나는 그것에 감사한다.

프로그래밍을 3년쯤 하고 난 뒤, 당시 암스테르담 수학센터에서 내 상사였던 A. 판 베인하르던(A. van Wijngaarden)과 대화를 나눈 적이 있다. 나는 살아 있는 한 그 대화에 대해 그에게 감사할 것이다. 문제는 내가 동시에 레이던 대학교에서 이론물리학을 공부하고 있었다는 점이었다. 두 일을 병행하기가 점점 더 어려워지자 결정을 내려야 했다. 프로그래밍을 그만두고 진짜로, 존중받는 이론물리학자가 되든지, 아니면 물리학 공부는 최소한의 노력으로 형식적으로만 마치고 ..... 그래, 무엇이 되느냐는 것이었다. 프로그래머? 하지만 그것이 존중받는 직업이었던가? 대체 프로그래밍이란 무엇인가? 그것을 지적으로 존중받는 학문 분야로 떠받칠 만한 탄탄한 지식 체계가 어디 있었는가? 그때 나는 하드웨어 동료들을 무척 부러워했던 기억이 선명하다. 그들은 자신의 전문성이 무엇이냐는 질문을 받으면 적어도 진공관, 증폭기 같은 것들을 다룰 줄 안다고 말할 수 있었다. 반면 나는 그 질문을 받으면 빈손으로 서 있는 기분이었다. 불안한 마음으로 판 베인하르던의 사무실 문을 두드리며 “잠깐 말씀드려도 될까요?” 하고 물었다. 몇 시간 뒤 그 방을 나왔을 때 나는 다른 사람이 되어 있었다. 그는 내 고민을 참을성 있게 들어 준 뒤, 그 시점까지는 프로그래밍이라는 분야에 별다른 학문적 체계가 없다는 점을 인정했다. 그러나 이어서 조용히 설명했다. 자동 컴퓨터는 앞으로도 계속 남을 것이며, 우리는 이제 막 시작점에 서 있고, 그렇다면 내가 앞으로 프로그래밍을 존중받는 학문으로 만드는 사람들 중 하나가 될 수도 있지 않겠느냐고. 이것은 내 인생의 전환점이었다. 나는 가능한 한 빨리 물리학 공부를 형식적으로 마무리했다. 이 이야기의 교훈 하나는 분명하다. 젊은이들에게 조언할 때는 매우 조심해야 한다. 때로는 그들이 정말 그 조언을 따르기 때문이다!

그로부터 2년 뒤인 1957년에 나는 결혼했다. 네덜란드의 혼인 절차에서는 직업을 기재해야 했고, 나는 내 직업을 프로그래머라고 적었다. 그러나 암스테르담 시 당국은 그런 직업은 존재하지 않는다는 이유로 그것을 받아들이지 않았다. 믿거나 말거나, 내 혼인증명서의 “직업” 항목에는 우스꽝스럽게도 “이론물리학자”라고 적혀 있다!

내 나라에서 내가 목격한 프로그래밍 직업의 등장이 얼마나 느렸는지는 이 정도로 해 두자. 그 뒤 나는 세계를 좀 더 많이 보았고, 전반적인 인상으로는 다른 나라들도 날짜가 조금씩 다를 뿐 성장 양상은 거의 같았다.

오늘의 상황을 더 잘 이해하기 위해, 그 옛날의 상황을 조금 더 자세히 포착해 보려 한다. 분석을 따라가다 보면, 프로그래밍 작업의 진정한 성격에 대한 많은 흔한 오해가 어떻게 그 먼 과거로 거슬러 올라가는지도 보게 될 것이다.

최초의 자동 전자식 컴퓨터들은 모두 유일무이한 단일 기계였다. 전부 실험실 특유의 흥분감이 감도는 환경 속에 놓여 있었다. 자동 컴퓨터라는 비전이 일단 생기고 나자, 그것을 실현하는 일은 당시 전자기술에 던져진 엄청난 도전이었다. 한 가지는 분명하다. 그런 환상적인 장비를 실제로 만들겠다고 나선 집단들의 용기를 우리는 부정할 수 없다. 그것들은 정말 환상적인 장비였다. 돌이켜보면, 그 초기 기계들이 어쨌든, 적어도 가끔은 작동했다는 사실 자체가 놀랍다. 압도적인 문제는 기계를 작동 가능한 상태로 만들고 또 유지하는 일이었다. 자동 계산의 물리적 측면에 대한 집착은 이 분야의 오래된 학회 이름에도 여전히 반영돼 있다. 예컨대 Association for Computing Machinery나 영국의 British Computer Society 같은 이름은 물리적 장비를 노골적으로 가리킨다.

그렇다면 불쌍한 프로그래머는 어땠을까? 솔직히 말하자면, 거의 눈에 띄지 않았다. 우선 초기 기계들은 너무 거대해서 거의 움직일 수도 없었고, 유지보수도 엄청나게 필요했다. 따라서 사람들이 기계를 사용하려 하는 장소가 그 기계가 개발된 바로 그 실험실과 같은 곳이 되는 것은 아주 자연스러웠다. 둘째, 그의 다소 보이지 않는 작업에는 화려함이 없었다. 방문객에게 기계를 보여줄 수는 있었지만, 그것은 몇 장의 코딩 용지보다 몇 차원은 더 극적이었다. 하지만 무엇보다 중요한 것은 프로그래머 자신이 자기 일을 매우 겸손하게 보았다는 점이다. 그의 작업은 전적으로 그 경이로운 기계의 존재로부터 의미를 얻었다. 그 기계가 유일무이했기 때문에, 자신의 프로그램도 지역적 의미밖에 없다는 사실을 그는 너무도 잘 알고 있었다. 게다가 이 기계의 수명이 제한적이라는 점도 너무나 분명했기에, 자신의 작업 중 오래 남을 가치가 있는 것이 거의 없으리라는 것도 알고 있었다. 여기에 더해 프로그래머의 태도에 깊은 영향을 준 또 하나의 사정이 있었다. 한편으로 그의 기계는 신뢰성이 낮았을 뿐 아니라 대체로 너무 느렸고 메모리는 너무 작았다. 말하자면 꽉 끼는 신발을 신고 있는 셈이었다. 그런데 다른 한편으로는, 대개 다소 괴상한 명령어 체계 덕분에 예상 밖의 기묘한 구성은 또 가능했다. 그 시절에는 적지 않은 영리한 프로그래머가, 자신의 장비가 가진 제약 속에 불가능해 보이는 것을 밀어 넣기 위해 교묘한 잔재주를 부리는 데서 엄청난 지적 만족을 얻었다.

프로그래밍에 대한 두 가지 견해는 바로 그 시절에 생겨났다. 지금 여기서 언급해 두고 나중에 다시 돌아오겠다. 하나는 정말 유능한 프로그래머라면 수수께끼를 좋아하는 성향이 있어야 하고 영리한 잔기술을 무척 좋아해야 한다는 견해였다. 다른 하나는 프로그래밍이란 결국 계산 과정의 효율을 이 방향이든 저 방향이든 최적화하는 일에 지나지 않는다는 견해였다.

후자의 견해는 실제로 당시 사용 가능한 장비가 몹시 꽉 끼는 신발과 같았다는 빈번한 현실에서 나온 것이다. 그 시절에는 더 강력한 기계가 나오기만 하면 프로그래밍은 더 이상 문제가 아닐 것이라는 순진한 기대를 흔히 볼 수 있었다. 기계를 한계까지 밀어붙이는 싸움이 더는 필요 없을 것이고, 프로그래밍이란 원래 그런 것 아니었느냐는 생각이었다. 그러나 그 뒤 수십 년 동안 일어난 일은 전혀 달랐다. 더 강력한 기계가 등장했다. 겨우 한 자릿수 배가 아니라, 몇 자릿수나 더 강력한 기계들이었다. 그런데 우리는 모든 프로그래밍 문제가 해결된 영원한 낙원에 도달한 것이 아니라, 목까지 차오른 소프트웨어 위기(software crisis) 속에 빠져 있음을 발견했다! 어째서였을까?

부차적 원인이 하나 있다. 한두 가지 측면에서 현대 기계는 옛 기계보다 본질적으로 다루기 더 어렵다. 첫째, 우리는 입출력 인터럽트를 갖게 되었다. 그것은 예측할 수도, 재현할 수도 없는 시점에 발생한다. 완전히 결정론적 자동기계인 척하던 옛 순차 기계와 비교하면, 이것은 극적인 변화였다. 이 기능이 만들어 낸 논리적 문제를 가볍게 말해서는 안 된다는 사실은 수많은 시스템 프로그래머의 흰머리가 증언한다. 둘째, 우리는 다층 저장장치를 갖춘 기계들을 갖게 되었다. 이것은 관리 전략의 문제를 우리 앞에 내놓았고, 그 주제에 관한 문헌이 아무리 많아도 여전히 꽤 잡히지 않는 문제로 남아 있다. 이것이 실제 기계 구조의 변화로 인해 추가된 복잡성이다.

그러나 나는 이것을 부차적 원인이라고 불렀다. 주된 원인은... 기계가 몇 자릿수나 더 강력해졌기 때문이다! 아주 노골적으로 말하자면 이렇다. 기계가 아예 없었을 때는 프로그래밍은 아무 문제도 아니었다. 약한 컴퓨터가 몇 대 생기자 프로그래밍은 가벼운 문제가 되었다. 이제는 거대한 컴퓨터를 갖게 되었고, 프로그래밍 역시 그에 걸맞게 거대한 문제가 되었다. 이런 의미에서 전자산업은 단 하나의 문제도 해결하지 않았다. 오히려 문제를 만들어 냈을 뿐이다. 자기들이 만든 제품을 사용하는 문제를 만들어 냈다. 달리 말하면, 사용 가능한 기계의 성능이 천 배 이상 높아지는 동안, 사회가 그 기계를 적용하려는 야심도 그에 비례해 커졌다. 그 목적과 수단 사이에서 폭발적으로 넓어진 긴장 영역 한가운데서 불쌍한 프로그래머는 자기 일을 해야 했다. 하드웨어의 성능 향상, 그리고 어쩌면 그보다 더 극적인 신뢰성 향상 덕분에, 몇 년 전만 해도 프로그래머가 감히 꿈꾸지도 못했던 해결책들이 실현 가능해졌다. 그런데 몇 년 뒤에는 그는 그런 것들을 반드시 꿈꿔야 했고, 더 나쁘게는 그 꿈을 현실로 만들어야 했다! 우리가 소프트웨어 위기에 빠진 것이 놀라운 일인가? 아니다. 전혀 놀랄 일이 아니다. 짐작하겠지만, 그것은 훨씬 전에 이미 예언되었다. 다만 소예언자들의 문제는, 그들이 옳았다는 사실을 진짜로 알게 되는 데 5년쯤 걸린다는 점이다.

그러다 1960년대 중반, 끔찍한 일이 벌어졌다. 이른바 3세대(third generation) 컴퓨터들이 등장한 것이다. 공식 문헌은 가격 대비 성능비가 주요 설계 목표 중 하나였다고 말한다. 하지만 “성능”을 기계 각 부품의 가동률로 정의해 버리면, 의심스러운 필요성밖에 없는 내부 집안일로 성능 목표의 대부분을 채우는 설계에 이르기 어렵지 않다. 또 “가격”을 하드웨어에 지불하는 가격으로만 정의해 버리면, 프로그래밍하기 끔찍하게 어려운 설계에 도달하기도 쉽다. 예를 들어 명령어 체계가 프로그래머나 시스템에게 이른 시점의 바인딩 결정을 강요하고, 그 결정들은 실제로 해결할 수 없는 충돌을 낳을 수도 있다. 그리고 이런 불쾌한 가능성은 상당 부분 현실이 된 듯하다.

이 기계들이 발표되고 기능 명세가 알려졌을 때, 우리 중 적지 않은 이들이 몹시 참담해졌으리라 생각한다. 적어도 나는 그랬다. 그런 기계들이 계산 공동체를 휩쓸 것이라고 예상하는 것은 지극히 합리적이었다. 그래서 그 설계가 가능한 한 건전해야 한다는 점은 더욱 중요했다. 그런데 그 설계에는 너무도 심각한 결함이 담겨 있었다. 나는 단 한 번의 타격으로 계산과학(computing science)의 진보가 적어도 10년은 늦춰졌다고 느꼈다. 그때가 내 직업 인생 전체에서 가장 암울한 한 주였다. 아마 지금 가장 슬픈 점은, 그토록 오랜 좌절스러운 경험을 거치고도 여전히 너무도 많은 사람이 기계는 원래 그렇게 생겨야 한다는 자연법칙이 있다고 진심으로 믿고 있다는 사실일 것이다. 그들은 의심을 잠재우기 위해 그런 기계가 얼마나 많이 팔렸는지를 본다. 그리고 그 사실에서, 결국 설계가 그렇게 나빴을 리 없다는 잘못된 안도감을 얻는다. 하지만 자세히 들여다보면 그런 방어 논리는 “담배를 피우는 사람이 워낙 많으니 흡연은 분명 건강에 좋다”는 주장만큼이나 설득력이 없다.

바로 이런 점 때문에 나는 계산 분야의 학술지가 새로 발표된 컴퓨터에 대한 서평(review)을, 과학 논문을 리뷰하듯 싣는 관행이 없다는 사실을 아쉽게 여긴다. 기계를 리뷰하는 일은 적어도 그만큼 중요할 것이다. 여기서 하나 고백할 것이 있다. 1960년대 초, 나는 실제로 그런 리뷰를 하나 써서 CACM에 투고할 생각이었다. 조언을 구하려고 원고를 보낸 몇몇 동료들도 모두 꼭 그렇게 하라고 권했다. 그런데 나는 감히 그러지 못했다. 나 자신이든 편집위원회든 감당해야 할 어려움이 너무 클 것 같았기 때문이다. 이 억제는 내 쪽의 비겁함이었고, 나는 시간이 갈수록 더 그것을 자책한다. 내가 예상했던 어려움은 널리 받아들여진 일반 기준이 없다는 데서 비롯되었다. 내가 적용하려 한 기준의 타당성은 확신했지만, 내 리뷰가 “개인적 취향의 문제”라는 이유로 거절되거나 묵살될까 두려웠다. 나는 지금도 그런 리뷰가 엄청나게 유익할 것이라고 생각한다. 그리고 그것이 실제로 받아들여져 등장하는 날을 간절히 기다린다. 그것이야말로 계산 공동체의 성숙을 확실히 보여 주는 징표일 테니 말이다.

내가 위에서 하드웨어 장면에 이토록 주의를 기울인 이유는, 어떤 계산 도구이든 가장 중요한 측면 중 하나가 그것을 사용하려는 사람들의 사고 습관에 미치는 영향이라고 느끼기 때문이다. 그리고 그 영향은 일반적으로 생각하는 것보다 훨씬 강하다고 믿을 만한 이유가 있다. 이제 관심을 소프트웨어 장면으로 돌려 보자.

여기서는 다양성이 너무 커서 몇몇 디딤돌만 짚고 넘어갈 수밖에 없다. 내 선택이 자의적이라는 사실을 나는 뼈아프게 알고 있다. 언급되지 않을 수많은 노력에 대한 내 평가를 거기서 추론하지 말아 주길 바란다.

처음에는 영국 케임브리지의 EDSAC이 있었다. 나는 그 기계의 설계와 사용 방식에서 처음부터 서브루틴 라이브러리(subroutine library)라는 개념이 중심 역할을 했다는 사실이 무척 인상적이라고 생각한다. 지금은 거의 25년이 지났고 계산 환경은 극적으로 변했다. 그러나 기본 소프트웨어(basic software)라는 개념은 여전히 우리와 함께 있고, 닫힌 서브루틴(closed subroutine)이라는 개념도 여전히 프로그래밍의 핵심 개념 중 하나다. 우리는 닫힌 서브루틴을 소프트웨어의 가장 위대한 발명 중 하나로 인정해야 한다. 그것은 세 세대의 컴퓨터를 견뎌 냈고 앞으로도 몇 세대는 더 살아남을 것이다. 왜냐하면 그것은 우리의 기본적인 추상화 패턴 중 하나를 구현하는 데 꼭 맞기 때문이다. 안타깝게도 3세대 컴퓨터 설계에서는 그 중요성이 과소평가되었다. 산술 장치에 명시적으로 이름 붙은 레지스터가 너무 많아서, 서브루틴 메커니즘에 큰 오버헤드가 생겼기 때문이다. 하지만 그조차도 서브루틴 개념을 죽이지는 못했다. 우리는 다만 그 변이가 유전되지 않기를 바랄 뿐이다.

소프트웨어 장면의 두 번째 주요 발전으로 내가 언급하고 싶은 것은 FORTRAN의 탄생이다. 당시 이것은 대담하기 이를 데 없는 프로젝트였다. 그 책임자들은 우리의 큰 존경을 받을 자격이 있다. 10년 남짓의 광범위한 사용 뒤에야 드러난 결점 때문에 그들을 비난하는 것은 절대 공정하지 않다. 10년 앞을 정확히 내다보는 집단은 정말 드물기 때문이다! 돌이켜보면 우리는 FORTRAN을 성공적인 코딩 기법으로 평가해야 한다. 하지만 개념 구상에 실질적으로 도움이 되는 수단은 거의 없었다. 그런데 지금은 그런 도움이 절실히 필요하므로, 이제는 그것을 시대에 뒤진 것으로 봐야 할 때가 왔다. FORTRAN이 존재했다는 사실을 우리가 빨리 잊을수록 더 좋다. 사고의 수단(vehicle of thought)으로서 그것은 더 이상 적절하지 않기 때문이다. 그것은 우리의 두뇌 자원을 낭비하고, 너무 위험하며, 그래서 사용 비용도 너무 비싸다. FORTRAN의 비극적인 운명은 그것이 널리 받아들여졌다는 데 있다. 그 때문에 수천, 수만의 프로그래머가 우리의 과거 실수에 정신적으로 사슬처럼 묶여 버렸다. 나는 매일 더 많은 동료 프로그래머가 호환성(compatibility)이라는 저주에서 벗어날 방법을 찾게 되기를 기도한다.

세 번째로 빼놓고 싶지 않은 프로젝트는 LISP이다. 이것은 완전히 다른 성격의, 매혹적인 시도였다. 아주 소수의 기본 원리 위에 세워졌음에도 놀라운 안정성을 보여 주었다. 게다가 LISP는 어떤 의미에서는 우리 컴퓨터 응용 가운데 가장 세련된 것들 상당수의 운반체가 되었다. LISP는 농담처럼 “컴퓨터를 오용하는 가장 지적인 방법”이라고 불려 왔다. 나는 이 표현을 대단한 찬사라고 생각한다. 거기에는 해방의 풍미가 온전히 담겨 있기 때문이다. 그것은 우리 동료 인간들 가운데 가장 재능 있는 몇몇이, 이전에는 불가능했던 생각을 할 수 있도록 도왔다.

네 번째로 언급할 프로젝트는 ALGOL 60이다. 오늘날까지도 FORTRAN 프로그래머들은 자신이 사용하는 프로그래밍 언어를 여전히 구체적 구현을 통해 이해하는 경향이 있다. 그래서 8진수와 16진수 덤프가 그토록 흔하다. 또 LISP의 정의도 여전히 그 언어가 무엇을 뜻하는지와 그 메커니즘이 어떻게 작동하는지를 뒤섞어 놓은 묘한 혼합물이다. 그런데 유명한 Report on the Algorithmic Language ALGOL 60은 추상화를 한 단계 더 멀리 밀고 나가, 구현 독립적인 방식으로 프로그래밍 언어를 정의하려는 진정한 노력의 결실이다. 어떤 사람은 이 점에서 저자들이 너무 성공적이어서, 과연 그것이 실제로 구현 가능한지에 대한 심각한 의문까지 만들어 냈다고 말할 수도 있다! 이 보고서는 오늘날 Backus-Naur-Form으로 꽤 잘 알려진 형식 기법 BNF의 힘을 찬란하게 보여 주었다. 동시에, 적어도 피터 나우어(Peter Naur)처럼 뛰어난 사람이 쓸 때는, 세심하게 다듬은 영어 문장이 얼마나 강력한지도 보여 주었다. 이처럼 짧은 문서 중에서 계산 공동체에 이만큼 깊은 영향을 미친 것은 극히 드물었다고 말해도 무방하다고 생각한다. 훗날 ALGOLALGOL 유사(ALGOL-like)라는 이름이 마치 보호받지 않는 상표처럼 너무나 쉽게 사용되어, 때로는 거의 관계도 없는 여러 후발 프로젝트에 그 영광의 일부를 빌려 주는 데 쓰였다는 사실은, 그 위상을 보여 주는 다소 충격적인 찬사다. 정의 장치로서 BNF의 강력함은 내가 이 언어의 약점 중 하나로 보는 점의 원인이기도 하다. 지나치게 정교하고, 그다지 체계적이지 않은 문법이 아주 적은 분량 속에 우겨 넣어질 수 있었기 때문이다. BNF처럼 강력한 장치를 사용했다면 Report on the Algorithmic Language ALGOL 60은 훨씬 더 짧았어야 했다. 게다가 나는 ALGOL 60의 매개변수 메커니즘에 대해서도 점점 더 회의적이 되고 있다. 그것은 프로그래머에게 조합상의 자유를 너무 많이 허용해서, 자신 있게 사용하려면 프로그래머에게 강한 규율이 필요하다. 구현 비용도 비싸고, 쓰기에도 위험해 보인다.

마지막으로, 유쾌한 주제는 아니지만 PL/1을 언급하지 않을 수 없다. 이 언어는 정의 문서의 크기와 복잡성이 무서울 정도다. PL/1을 사용하는 일은 조종석에 조작해야 할 버튼, 스위치, 손잡이가 7000개 달린 비행기를 모는 것과 같을 것이다. 프로그래밍 언어, 다시 말해 우리의 기본 도구가 그 엄청난 바로크적 복잡성 때문에 이미 우리 지적 통제를 벗어나 있는데, 그런 상황에서 점점 커지는 프로그램들을 어떻게 굳건히 우리의 지적 파악 속에 둘 수 있는지 나는 도무지 알 수 없다. PL/1이 사용자에게 끼칠 수 있는 영향을 비유해야 한다면, 내게 가장 가까이 떠오르는 것은 마약이다. 고급 프로그래밍 언어에 관한 한 심포지엄에서, 자신을 PL/1의 헌신적 사용자라고 소개한 한 사람이 PL/1을 옹호하는 강연을 한 것이 기억난다. 그런데 그는 1시간짜리 찬양 강연 안에서 무려 50개쯤의 새로운 “기능(feature)”을 더해 달라고 요청했다. 자기 문제의 주된 원인이 이미 “기능”이 너무 많다는 사실일 수도 있다는 점은 거의 의심하지 않은 채 말이다. 그 발표자는 중독의 우울한 증상을 모두 보여 주었다. 그는 더 이상 생각이 자라지 않는 정체 상태에 빠져, 오직 더 많은 것, 더 많은 것, 더 많은 것만 요구할 수 있었다.... FORTRAN이 유아기 질환이라 불린 적이 있다면, 위험한 종양처럼 자라는 특성을 지닌 완전한(full) PL/1은 치명적 질병으로 드러날 수도 있다.

과거 이야기는 이쯤 하자. 하지만 실수는 그 뒤에 거기서 배울 수 있을 때에만 의미가 있다. 사실 나는 우리가 이미 너무도 많은 것을 배웠기 때문에, 몇 년 안에 프로그래밍은 지금까지와는 엄청나게 다른 활동이 될 수 있다고 생각한다. 너무 다를 것이므로, 우리는 그 충격에 대비하는 편이 좋다. 가능한 미래 중 하나를 그려 보겠다. 얼핏 보면, 어쩌면 이미 가까운 미래의 프로그래밍에 대한 이 비전은 터무니없는 환상처럼 보일지도 모른다. 그래서 나는 이 비전이 매우 현실적인 가능성일 수 있다고 결론 내리게 하는 고려 사항도 덧붙이겠다.

그 비전이란 이렇다. 1970년대가 끝나기도 훨씬 전에, 우리는 지금 우리의 프로그래밍 능력을 극한까지 몰아붙이고 있는 종류의 시스템을, 지금 드는 인년(man-year) 비용의 몇 퍼센트만으로 설계하고 구현할 수 있게 될 것이다. 게다가 그런 시스템은 사실상 버그가 없을 것이다. 이 두 개선은 함께 간다. 후자의 측면에서 소프트웨어는 다른 많은 제품과 달라 보인다. 대체로 다른 제품은 품질이 높을수록 가격도 높아지기 마련이다. 하지만 정말 신뢰할 수 있는 소프트웨어를 원하는 사람들은, 애초에 대다수의 버그를 피할 방법을 찾아야 한다는 사실을 깨닫게 될 것이다. 그 결과 프로그래밍 과정은 더 저렴해진다. 더 효과적인 프로그래머를 원한다면, 그들이 디버깅에 시간을 낭비해서는 안 된다는 사실을 알게 될 것이다. 처음부터 버그를 넣지 말아야 한다. 다시 말해 두 목표는 같은 변화로 향한다.

이처럼 짧은 시간 안에 이런 급격한 변화가 일어난다면 그것은 혁명일 것이다. 미래에 대한 기대를 최근 과거의 매끄러운 외삽에 두는 사람들, 곧 사회적·문화적 관성에 관한 어떤 불문율에 기대는 사람들에게는, 이 급격한 변화가 실제로 일어날 가능성이 무시해도 좋을 만큼 작아 보일 것이다. 하지만 때로 혁명은 실제로 일어난다는 사실을 우리 모두 안다! 그렇다면 이번 혁명의 가능성은 어떨까?

충족되어야 할 큰 조건이 세 가지 있는 듯하다. 첫째, 세상 전체가 변화의 필요성을 인정해야 한다. 둘째, 그 경제적 필요가 충분히 강해야 한다. 셋째, 그 변화가 기술적으로 가능해야 한다. 이 세 조건을 위의 순서대로 논의해 보겠다.

소프트웨어 신뢰성을 더 높여야 할 필요성의 인식에 관해서는, 이제 더 이상 이견이 없을 것으로 본다. 불과 몇 년 전만 해도 사정은 달랐다. 소프트웨어 위기(software crisis)를 말하는 것은 신성모독처럼 여겨졌다. 전환점은 1968년 10월 가르미슈(Garmisch)에서 열린 소프트웨어 공학(Software Engineering) 회의였다. 그 회의는 소프트웨어 위기가 처음으로 공개적으로 인정되면서 큰 반향을 일으켰다. 지금은 일반적으로, 크고 복잡한 어떤 시스템이든 설계는 매우 어려운 일이 될 것이라는 점이 인정된다. 그런 사업을 책임지는 사람들을 만나 보면, 그들은 하나같이 신뢰성 문제를 몹시 걱정하고 있고, 그럴 만하다. 요컨대 첫 번째 조건은 충족된 듯하다.

이제 경제적 필요를 보자. 오늘날에는 1960년대에 프로그래밍이 과보수된 직업이었다는 견해를 종종 접한다. 그리고 앞으로 몇 년 동안 프로그래머 급여는 내려갈 것이라고 기대하기도 한다. 보통 이런 견해는 경기 침체와 관련해 표현된다. 하지만 그것은 어쩌면 전혀 다른, 그리고 꽤 건강한 어떤 현상의 징후일 수도 있다. 지난 10년 동안의 프로그래머들이 사실은 마땅히 했어야 할 만큼 좋은 일을 하지 못했다는 뜻일 수도 있다는 말이다. 사회는 프로그래머와 그 산출물의 성과에 점점 불만을 품고 있다. 하지만 훨씬 더 큰 비중을 지닌 다른 요인도 있다. 현재는 특정 시스템에 대해 소프트웨어 개발 비용이 필요한 하드웨어 가격과 비슷한 규모인 것이 아주 흔하고, 사회도 대체로 그것을 받아들인다. 그런데 하드웨어 제조사들은 다음 10년 동안 하드웨어 가격이 10분의 1 수준으로 떨어질 것이라고 말한다. 만약 소프트웨어 개발이 지금처럼 서툴고 비싼 과정으로 계속 남는다면, 사물의 균형은 완전히 무너질 것이다. 사회가 이것을 받아들일 것이라 기대할 수는 없다. 따라서 우리는 지금보다 한 자릿수 배는 더 효과적으로 프로그래밍하는 법을 반드시 배워야 한다. 달리 말하면, 기계가 예산에서 가장 큰 항목이던 시절에는 프로그래밍 직업이 서툰 기법으로도 어물쩍 넘어갈 수 있었지만, 그 우산은 곧 접힐 것이다. 요컨대 두 번째 조건도 충족된 듯하다.

이제 세 번째 조건이다. 기술적으로 가능한가? 나는 가능할지도 모른다고 생각하며, 그 의견을 뒷받침하는 여섯 가지 논거를 제시하겠다.

프로그램 구조를 연구한 결과, 같은 작업을 수행하고 같은 수학적 내용을 담고 있는 대안적 프로그램들조차도 지적으로 다루기 쉬운 정도에서는 엄청난 차이를 보인다는 사실이 드러났다. 몇 가지 규칙이 발견되었는데, 이것을 어기면 프로그램의 지적 관리 가능성(intellectual manageability)이 심각하게 훼손되거나 완전히 파괴된다. 이 규칙은 두 부류다. 첫 번째 부류는 적절히 선택된 프로그래밍 언어를 통해 기계적으로 쉽게 강제할 수 있다. 예를 들어 goto 문을 배제하거나 출력 매개변수가 둘 이상인 프로시저를 금지하는 식이다. 두 번째 부류에 대해서는, 적어도 나는 —내 능력 부족 때문일 수도 있겠지만— 그것을 기계적으로 강제할 방법을 보지 못한다. 어떤 자동 정리 증명기 같은 것이 필요해 보이는데, 나는 그런 것의 존재를 입증할 수 없다. 따라서 당분간, 그리고 어쩌면 영원히, 두 번째 부류의 규칙은 프로그래머에게 요구되는 규율의 요소로 나타난다. 내가 염두에 둔 몇몇 규칙은 너무도 분명해서 가르칠 수 있고, 주어진 프로그램이 그것을 어겼는지 아닌지를 두고 논쟁할 필요조차 없다. 예를 들면, 어떤 반복문도 종료 증명을 제시하지 않은 채 써서는 안 되며, 반복 가능한 문장의 실행이 깨뜨리지 않을 관계가 무엇인지 불변식으로 명시하지 않은 채 써서도 안 된다는 요구다.

이제 나는 우리가 지적으로 관리 가능한 프로그램의 설계와 구현에만 자신을 제한하자고 제안한다. 누군가 이 제한이 너무 심해서 우리가 감당할 수 없을까 걱정한다면 안심시킬 수 있다. 지적으로 관리 가능한 프로그램의 부류는 여전히 충분히 풍부해서, 알고리즘적으로 해결 가능한 어떤 문제에 대해서도 매우 현실적인 프로그램들을 많이 포함한다. 우리는 잊지 말아야 한다. 우리의 일이 프로그램을 만드는 것 자체는 아니다. 우리의 일은 원하는 동작을 보이는 계산의 부류를 설계하는 것이다. 우리를 지적으로 관리 가능한 프로그램으로 제한하자는 이 제안은, 내가 예고한 여섯 가지 논거 가운데 처음 두 가지의 토대다.

첫 번째 논거는 이렇다. 프로그래머가 지적으로 관리 가능한 프로그램만 고려하면 되므로, 그가 선택해야 하는 대안들은 다루기가 훨씬, 훨씬 쉬워진다.

두 번째 논거는 이렇다. 지적으로 관리 가능한 프로그램의 부분집합으로 우리 자신을 제한하기로 결정하는 순간, 우리는 검토해야 할 해 공간(solution space)을 단번에, 영구적으로 대폭 줄인 것이다. 그리고 이것은 첫 번째 논거와는 다른 논거다.

세 번째 논거는 프로그램 정확성 문제에 대한 구성적 접근(constructive approach)에 바탕을 둔다. 오늘날 흔한 기법은 먼저 프로그램을 만들고 그다음 테스트하는 것이다. 그러나 프로그램 테스트는 버그가 존재함을 보여 주는 데는 매우 효과적인 방법일 수 있어도, 버그가 없음을 보여 주는 데는 절망적으로 부적절하다. 프로그램에 대한 신뢰 수준을 의미 있게 높이는 유일한 효과적 방법은 그 정확성에 대한 설득력 있는 증명을 제시하는 것이다. 그러나 먼저 프로그램을 만들고 나중에 정확성을 증명해서는 안 된다. 그렇게 하면 증명 제공이라는 요구가 이미 힘든 프로그래머의 짐만 더 늘리기 때문이다. 오히려 정확성 증명과 프로그램은 손을 맞잡고 함께 자라야 한다. 세 번째 논거는 본질적으로 다음 관찰에 기반한다. 먼저 스스로에게 설득력 있는 증명의 구조가 무엇일지를 묻고, 그것을 찾은 다음 그 증명이 요구하는 조건을 만족하는 프로그램을 구성하면, 정확성에 대한 이런 관심이 매우 효과적인 발견적 안내가 된다는 것이다. 정의상 이 접근은 우리가 지적으로 관리 가능한 프로그램으로 자신을 제한할 때에만 적용 가능하다. 하지만 그 안에서 만족스러운 프로그램 하나를 찾아내는 효과적인 수단을 제공한다.

네 번째 논거는 프로그램 설계에 필요한 지적 노력의 양이 프로그램 길이에 어떻게 의존하는가와 관련이 있다. 지적 노력은 프로그램 길이의 제곱에 비례해 증가한다는 일종의 자연법칙이 있다고들 말해 왔다. 하지만 다행히도 아무도 그 법칙을 증명하지 못했다. 사실일 필요가 없기 때문이다. 우리는 모두 알고 있다. 유한한 한 덩어리의 추론이 무수한 경우를 포괄할 수 있게 해 주는 유일한 정신적 도구는 추상화(abstraction)라 불린다. 그러므로 추상화 능력을 효과적으로 활용하는 일은 유능한 프로그래머의 가장 핵심적인 활동 중 하나로 보아야 한다. 이와 관련해 지적해 둘 만한 점은, 추상화의 목적이 모호해지는 것이 아니라는 사실이다. 목적은 절대적으로 정확할 수 있는 새로운 의미 수준을 만드는 데 있다. 물론 나는 우리의 추상화 메커니즘이 충분히 효과적이지 못하도록 막는 근본 원인을 찾아보려 애썼다. 그러나 아무리 애써도 그런 원인을 찾지 못했다. 그래서 나는 —지금까지 경험으로 반박되지 않은— 다음 가정으로 기울고 있다. 즉, 우리의 추상화 능력을 적절히 활용하면, 프로그램을 구상하거나 이해하는 데 필요한 지적 노력은 프로그램 길이에 비례하는 정도 이상으로 늘지 않아도 된다는 가정이다. 그런데 이 연구의 부산물은 훨씬 더 큰 실천적 의미를 가질 수도 있고, 실제로 내 네 번째 논거의 기반이 된다. 그 부산물은 프로그램을 구성하는 전 과정에서 핵심 역할을 하는 여러 추상화 패턴(patterns of abstraction)을 식별해 낸 것이다. 이제는 이런 추상화 패턴 각각에 대해 강의 하나를 할 수 있을 정도로 많이 알려져 있다. 이런 패턴에 대한 친숙함과 의식적인 지식이 무엇을 뜻하는지 깨달은 순간은, 만약 이것이 15년 전에 이미 상식이었다면, 예를 들어 BNF에서 구문 지향 컴파일러(syntax-directed compilers)로 가는 단계가 몇 년이 아니라 몇 분이면 되었을 것이라는 사실을 알게 되었을 때였다. 따라서 나는 중요한 추상화 패턴에 대한 우리의 최근 지식을 네 번째 논거로 제시한다.

이제 다섯 번째 논거로 가자. 이것은 우리가 사용하려는 도구가 우리의 사고 습관에 미치는 영향과 관련이 있다. 나는 아마 르네상스에 뿌리를 둔 문화적 전통을 본다. 이 전통은 그러한 영향을 무시하고, 인간 정신을 자신이 만든 인공물의 최고이자 자율적인 주인으로 여긴다. 그러나 나 자신과 동료 인간들의 사고 습관을 분석하기 시작하면, 좋든 싫든 전혀 다른 결론에 도달하게 된다. 즉, 우리가 사용하려는 도구와 생각을 표현하거나 기록하는 데 사용하는 언어 또는 표기법이야말로, 우리가 애초에 무엇을 생각하거나 표현할 수 있는지를 결정하는 주된 요인이라는 결론이다! 프로그래밍 언어가 사용자들의 사고 습관에 미치는 영향을 분석하고, 이제는 두뇌 자원이 단연코 우리의 가장 희소한 자원이라는 사실을 인식하면, 서로 다른 프로그래밍 언어의 상대적 장점을 비교하는 새로운 잣대들이 생긴다. 유능한 프로그래머는 자기 두개골의 크기가 엄격하게 제한돼 있다는 사실을 완전히 알고 있다. 따라서 그는 프로그래밍 작업에 최대한 겸손하게 접근하며, 무엇보다 영리한 잔재주를 전염병 피하듯 멀리한다. 어느 유명한 대화형 프로그래밍 언어의 경우, 한 프로그래밍 공동체에 그 언어용 단말기가 갖춰지는 순간 특정 현상이 일어나며, 심지어 그것에는 이미 굳어진 이름까지 있다고 여러 경로로 들었다. 그것은 “원라이너(one-liners)”라고 불린다. 이것은 두 형태로 나타난다. 한 프로그래머가 한 줄짜리 프로그램을 다른 프로그래머의 책상 위에 올려놓고, 자랑스럽게 그것이 무엇을 하는지 말한 뒤 “이걸 더 적은 기호로 코딩할 수 있겠나?”라고 묻는다. 마치 그것이 개념적으로 무슨 의미라도 있는 것처럼! 아니면 그냥 “이게 뭐 하는 건지 맞혀 보라”고 묻기도 한다. 이 관찰로부터 우리는 결론 내려야 한다. 이 언어는 도구로서 영리한 잔재주를 대놓고 부추기는 초대장이다. 이것이 바로 그 언어가 일부 사람들에게 매력적인 이유일 수 있다. 곧, 자신이 얼마나 영리한지 보여 주고 싶어 하는 사람들에게 말이다. 하지만 유감스럽게도, 나는 이것을 프로그래밍 언어에 대해 할 수 있는 가장 치명적인 비난 중 하나로 간주할 수밖에 없다. 우리가 최근 과거로부터 배워야 했던 또 하나의 교훈은, “더 풍부한(richer)” 또는 “더 강력한(more powerful)” 프로그래밍 언어를 개발하려 했던 것이 실수였다는 점이다. 이런 바로크적 괴물들, 온갖 기벽이 한데 뭉친 잡탕들은 기계적으로도 정신적으로도 정말 관리할 수 없기 때문이다. 나는 매우 체계적이고 아주 겸손한(modest) 프로그래밍 언어에 큰 미래가 있다고 본다. 내가 “겸손한”이라고 할 때는, 예를 들어 ALGOL 60의 for 절(for clause)뿐 아니라 FORTRAN의 DO 루프(DO loop)조차 너무 바로크적이라는 이유로 폐기될 수도 있다는 뜻이다. 나는 정말 경험 많은 자원자들과 작은 프로그래밍 실험을 해 본 적이 있다. 그런데 꽤 의도치 않았고 예상도 못 한 일이 나타났다. 내 자원자들 가운데 누구도 그 명백하고도 가장 우아한 해법을 찾지 못했다. 자세히 분석해 보니 공통 원인이 있었다. 그들의 반복(repetition) 개념이 증가하는 제어 변수라는 생각과 너무 단단히 결합되어 있었기 때문에, 명백한 해법을 보는 데 정신적으로 막혀 있었던 것이다. 그들이 내놓은 해법은 덜 효율적이었고, 불필요하게 이해하기 어려웠으며, 찾아내는 데도 매우 오랜 시간이 걸렸다. 그것은 나에게 계시적이었지만 동시에 충격적인 경험이었다. 마지막으로, 한 가지 점에서 우리는 내일의 프로그래밍 언어가 지금까지 익숙했던 것과 크게 달라지기를 바란다. 지금까지보다 훨씬 더 많이, 우리가 적어 내려가는 것의 구조 속에, 우리가 설계하는 것의 복잡성을 개념적으로 다루기 위해 필요한 모든 추상화를 반영하도록 우리를 초대해야 한다는 점이다. 미래 도구의 더 큰 적합성에 대한 이야기는 이쯤 하자. 이것이 다섯 번째 논거의 토대였다.

곁다리로 한마디 경고를 덧붙이고 싶다. 프로그래밍 작업의 어려움을 현재 도구의 부적절함과 싸우는 데서만 찾는 사람들에게 하는 말이다. 그들은 우리의 도구가 훨씬 더 적절해지면 프로그래밍은 더 이상 문제가 아니라고 결론 내릴 수 있다. 그러나 프로그래밍은 여전히 매우 어려운 일로 남을 것이다. 상황적 번거로움에서 벗어나는 순간, 우리는 지금은 우리의 프로그래밍 능력을 한참 넘어서는 문제들에 자유롭게 도전하게 될 것이기 때문이다.

여섯 번째 논거는 마음대로 시비를 걸어도 좋다. 그것을 뒷받침할 실험적 증거를 모으기가 쉽지 않기 때문이다. 그렇다고 해서 내가 그 타당성을 믿지 않게 되지는 않을 것이다. 지금까지 나는 계층(hierarchy)이라는 단어를 언급하지 않았다. 하지만 잘 분해된 해법을 담은 모든 시스템에서 이것이 핵심 개념이라고 말해도 무방하다. 나는 한 걸음 더 나아가 이것을 신앙 조항처럼 말할 수도 있다. 즉, 우리가 정말 만족스럽게 해결할 수 있는 문제는 결국 잘 분해된 해법을 허용하는 문제뿐이라는 것이다. 얼핏 보면 인간의 한계에 대한 이런 견해는 꽤 우울하게 들릴지도 모른다. 하지만 나는 전혀 그렇게 느끼지 않는다. 오히려 그 반대다! 우리의 한계와 더불어 사는 법을 배우는 가장 좋은 길은, 그 한계를 아는 것이다. 다른 시도들은 우리의 지적 통제를 벗어나기 때문에, 잘 분해된 해법만 시도할 만큼 우리가 충분히 겸손해질 때, 우리는 시스템을 유익한 방식으로 분해하는 능력을 해치는 모든 인터페이스를 피하려고 최선을 다할 것이다. 그리고 나는 이것이 반복적으로, 처음에는 다룰 수 없던 문제도 결국은 분해할 수 있다는 발견으로 이어지리라고 기대할 수밖에 없다. 컴파일 단계 중 “코드 생성(code generation)”이라 불리는 부분의 문제 대다수가 명령어 체계의 우스꽝스러운 성질로 거슬러 올라감을 본 사람이라면, 내가 염두에 둔 것이 어떤 종류인지에 대한 단순한 예를 알 것이다. 잘 분해된 해법의 더 넓은 적용 가능성, 이것이 현재 10년 안에 일어날지도 모를 혁명의 기술적 가능성에 대한 여섯 번째이자 마지막 논거다.

원칙적으로는 여러분 각자가 내 고려 사항에 얼마나 무게를 둘지 스스로 결정하도록 맡기겠다. 내가 내 믿음을 남에게 강요할 수 없다는 점을 너무도 잘 알고 있기 때문이다. 모든 진지한 혁명처럼, 이것도 격렬한 반대를 불러일으킬 것이다. 그래서 이런 발전을 막으려는 보수적 세력이 어디에서 나올지를 물을 수 있다. 나는 그것이 주로 대기업, 심지어 컴퓨터 산업에서 나올 것이라고는 생각하지 않는다. 오히려 오늘날의 교육을 제공하는 교육기관과, 자기들의 오래된 프로그램을 너무 중요하게 여겨서 그것을 다시 쓰고 개선할 가치가 없다고 생각하는 보수적 사용자 집단에서 나오리라 본다. 이와 관련해 슬픈 점은, 많은 대학 캠퍼스에서 중앙 계산 시설의 선택이 너무 자주, 소수의 이미 자리 잡은 그러나 값비싼 응용의 요구에 의해 좌우됐다는 사실이다. 그 선택으로 인해 기꺼이 자기 프로그램을 직접 쓰려는 수천 명의 “소규모 사용자(small users)”가 얼마나 고통받게 될지는 무시한 채 말이다. 예를 들어 고에너지 물리학이, 남아 있는 실험 장비의 비용을 내세워 과학 공동체를 사실상 협박해 온 경우가 너무 많아 보인다. 가장 쉬운 대답은 물론 기술적 가능성을 단호히 부정하는 것이다. 그러나 그러려면 꽤 강한 논거가 필요하리라 생각한다. 애석하게도 오늘날 평균적인 프로그래머의 지적 한계가 이 혁명을 막을 것이라는 말에서는 아무 위안도 얻을 수 없다. 다른 이들이 훨씬 더 효과적으로 프로그래밍하게 되면, 그는 어차피 그림에서 밀려날 가능성이 크기 때문이다.

정치적 장애도 있을 수 있다. 설령 우리가 내일의 전문 프로그래머를 어떻게 교육해야 하는지 안다고 해도, 우리가 사는 사회가 실제로 그렇게 하도록 허용할지는 확실치 않다. 지식을 퍼뜨리는 것보다는 방법론(methodology)을 가르칠 때 가장 먼저 나타나는 효과는, 이미 유능한 사람들의 능력을 더욱 키운다는 점이다. 그래서 지능의 차이가 더 커진다. 교육 제도가 균질화된 문화를 확립하는 도구로 쓰이고, 가장 뛰어난 이들이 위로 떠오르지 못하게 막는 사회에서는, 유능한 프로그래머를 교육하는 일이 정치적으로 받아들이기 어려울 수도 있다.

이제 결론짓겠다. 자동 컴퓨터는 이제 우리 곁에 25년 동안 있었다. 도구로서 그것은 우리 사회에 큰 영향을 미쳤다. 그러나 도구로서의 영향은, 인류 문화사에서 전례 없는 지적 도전(intellectual challenge)이라는 자격으로 그것이 미칠 훨씬 더 깊은 영향에 비하면, 우리 문화의 표면에 이는 물결에 불과할 것이다. 계층적 시스템(hierarchical systems)은 이런 성질을 지니는 듯하다. 한 수준에서는 나뉘지 않은 단일 개체로 여겨지는 것이, 그보다 더 자세한 바로 아래 수준에서는 복합 객체로 여겨진다. 그 결과 각 수준에서 적용되는 공간 또는 시간의 자연스러운 입도(grain)는, 한 수준 아래로 주의를 옮길 때마다 한 자릿수 정도씩 작아진다. 우리는 벽을 벽돌로 이해하고, 벽돌을 결정으로 이해하고, 결정을 분자로 이해하는 식이다. 그 결과 계층적 시스템에서 의미 있게 구별할 수 있는 수준의 수는, 가장 큰 입도와 가장 작은 입도의 비의 로그에 비례하는 셈이 된다. 따라서 이 비가 매우 크지 않다면 많은 수준을 기대할 수 없다. 컴퓨터 프로그래밍에서 우리의 기본 구성 블록은 1마이크로초보다 작은 시간 입도를 가지지만, 우리의 프로그램은 몇 시간 동안 계산을 수행할 수도 있다. 나는 10 10 이상의 비율을 다루는 다른 기술을 알지 못한다. 컴퓨터는 그 환상적인 속도 덕분에, 고도로 계층적인 인공물이 가능할 뿐 아니라 필수적이기도 한 환경을 우리에게 처음으로 제공하는 것처럼 보인다. 바로 이 도전, 곧 프로그래밍 작업과의 대면은 너무도 독특해서, 이 새로운 경험은 우리 자신에 대해 많은 것을 가르쳐 줄 수 있다. 그것은 설계와 창조의 과정에 대한 이해를 깊게 해야 하며, 우리 생각을 조직하는 작업을 더 잘 통제하게 해 주어야 한다. 만약 그렇지 않다면, 적어도 내 생각에는 우리는 컴퓨터를 가질 자격이 없다!

컴퓨터는 이미 우리에게 몇 가지 교훈을 가르쳐 주었다. 그리고 이 강연에서 내가 강조하기로 선택한 교훈은 다음과 같다. 우리는 그 엄청난 어려움을 충분히 인식한 상태로 작업에 접근할 때, 겸손하고 우아한 프로그래밍 언어를 고수할 때, 인간 정신의 본질적 한계를 존중할 때, 그리고 매우 겸손한 프로그래머(Very Humble Programmers)로서 이 일을 받아들일 때 훨씬 더 나은 프로그래밍을 하게 될 것이다.


필사
2013년 2월 5일 수정