요약
데이크스트라는 1978년에 모국어로 기계를 부리면 프로그래밍이 쉬워진다는 기대를 정면으로 반박했습니다.
컴퓨터 과학자 에츠허르 데이크스트라가 남긴 짧은 메모 EWD 667입니다. 형식 기호(규칙이 엄밀하게 정해진 수식이나 코드 같은 표기)를 다루는 데 드는 주의를 결함으로 여기고, 사람 말을 알아듣는 기계를 바라는 이들에게 답한 글입니다.
왜 중요한가
- 사람의 부담을 기계로 넘기면 편해진다는 직관에, 주고받는 소통 비용까지 따져야 한다는 기준을 세웁니다.
- 형식 언어의 엄격함을 없앨 불편이 아니라 실수를 걸러 내는 장치로 다시 보게 합니다.
- LLM(대규모 언어 모델) 덕분에 영어가 새 프로그래밍 언어가 된다는 주장에 맞서는 근거로 HN에서 다시 읽히고 있습니다.
핵심 내용
- 인터페이스를 바꾸는 일은 정해진 일을 나누는 것이 아니어서, 소통 비용이 더해져 양쪽 일이 모두 늘 수 있다고 했습니다.
- 형식 기호는 단순한 규칙 몇 개만으로 모국어에서는 피하기 힘든 헛소리를 걸러 준다고 봤습니다.
- 그리스 수학은 말과 그림에 머물러 막혔고, 비에트·데카르트·라이프니츠·불의 기호가 근대를 열었다고 짚었습니다.
- 기호 덕분에 학생도 예전에는 천재만 하던 일을 해내니, 형식 기호는 짐이 아니라 특권이라고 했습니다.
- 모국어의 '자연스러움'은 헛소리를 티 나지 않게 말하기 쉽다는 뜻일 뿐이라고 꼬집었습니다.
HN 반응
- 여러 이용자는 LLM이 컴파일러가 아니라 개발자를 대신할 뿐이며, 동작을 확정하려면 여전히 코드가 필요하다고 봤습니다.
- 자연어도 세세하게 지시하면 LLM이 컴파일러 구실을 한다는 반론에는, 글의 요지가 형식과 비형식의 차이라는 재반론이 나왔습니다.
이 에세이는 꽤 잘 나이 들었고, 지금도 상당히 정확하다고 생각합니다.
에이전트 개발이 “자연어 프로그래밍”이라고 반박하는 사람도 있겠지만, 저는 그게 맞지 않다고 봅니다. 그 경우 프로그래밍을 하는 쪽은 에이전트이고, 프로그래밍은 여전히 늘 Rust/Python/Typescript 같은 결정론적 언어로 이루어집니다. 나중에 바뀔 수도 있겠지만, 적어도 제 생각에는 그 반대 방향은 재앙일 겁니다.
그래도 제가 아는 가장 좋은 예외는 단연 텍스트 어드벤처를 만드는 언어인 Inform 7입니다[0]. 팬층이 두텁고, 저도 깊이 파 본 적은 없지만, 자연어와 프로그래밍을 독특한 방식으로 꽤 우아하게 엮어 낸 것 같습니다.
어느 쪽이든, 자연어 프로그래밍에 관심 있는 언어 덕후라면 꼭 확인해 볼 만합니다.
[0] https://ganelson.github.io/inform-website/
좋은 지적입니다. 저는 계속 이 입장을 지켜 왔습니다.
“프롬프트를 버전 관리에 저장해 두면 에이전트가 알아서 가져다 쓸 것이다”, “코드는 더 이상 존재하지 않게 될 것이다”, “소프트웨어가 해야 할 일을 포괄적인 명세로 쓰기만 하면 된다” 같은 말들이 있습니다.
제 생각에는 근시안적인 얘기입니다. (거꾸로 제 주장이 근시안적일 수도 있겠지만요.)
우리가 코드를 쓰는 이유가 바로 그겁니다. 자연어는 지나칠 정도로 모호합니다. 문장 하나가 그에 의존하는 다른 문장들에 여러 방식으로 영향을 주고, 그래서 수백만, 수십억, 수조 가지의 “그럴듯한” 명세가 나오는 거대한 의존성 그래프가 만들어집니다.
실제 소프트웨어를 만들 때는 결정론적 프로그래밍 언어를 쓰는 편이 거의 언제나 더 쉬울 겁니다.
우리가 코드를 볼 일도, 쓸 일도 없어질 수는 있습니다. 그래도 소프트웨어 시스템이 하는 일을 결정론적으로 명시해 둔 것이 에이전트와 일할 때 훨씬 쉬운 출발점이 될 것 같습니다(우리가 그 코드를 직접 다루든 아니든 말이죠).
저한테는 이미 결론이 난 문제처럼 느껴집니다. 사용자를 만들고, 로그인시키고, 대시보드를 보여 주는 단순한 소프트웨어 예제만 해도 엄청난 양의 말과 모호함으로 불어납니다.
대시보드 데이터 A를 보려면 로그인해야 한다. 이메일이나 사용자 이름으로 로그인할 수 있다. IP가 [범위] 밖이면 로그인할 수 없다. 로그아웃할 수 있다. 대시보드를 갱신하는 도중에 로그아웃하면 트랜잭션이 취소된다. 기밀 데이터는 볼 수 없다. 단, 사용자가 관리자라면 볼 수 있다. 사용자는 강력한 비밀번호가 필요하다. 비밀번호에는 대문자가 들어 있어야 한다.
이런 건 코드로 두는 편이 파악하기도 수정하기도 더 쉬워 보입니다(사람이든 에이전트든요). 게다가 자연어를 디버깅한다는 건 정말 찝찝합니다. 비밀번호 규칙을 형식 명세에서 빠뜨린 문장이 대체 어느 것이었을까요?
그리고 가장 큰 논거는 이겁니다. 이미 코드가 있는데, 잘 돌아가는 걸 굳이 다시 발명하겠다는 건 잘못된 방향 아닐까요?
일부러 반대 입장에서 말씀드리자면, 구현이 여러 가지 가능하다는 게 왜 나쁜 일입니까? 물론 명세를 대충 써서 제품을 제대로 못 박지 못하는 건 곤란하지만, 중요한 것을 제대로 못 박았다면 나머지는 정의상 재량의 영역입니다. 형식 명세(예: C 명세)도 이 점에서는 비형식 명세와 똑같이 동작합니다.
진짜 문제는 에이전트 워크플로에서 사람이 자기 제품에 구조적으로, 기능적으로 꼭 필요한 것을 전부 짚어낼 수 있느냐입니다. 명세를 쓰는 사람이 전문가이거나, 에이전트가 비전문가를 알아보고 함께 일할 만큼 영리해야 합니다. 지금의 에이전트는 최대한 빨리 결과물을 내놓으려고 명세가 부실하거나 나쁜 아이디어여도 그냥 밀어붙이는 걸 좋아합니다.
맞습니다. 고수준 프로그래밍 언어(PL)가 등장했다고 해서 저수준 PL이나 어셈블리가 사라지지는 않았습니다. LLM으로 자연어 프로그래밍을 하는 것은 어느 정도 고수준 PL로 프로그래밍하는 것과 비슷합니다. 그게 저수준 PL로, 맨 아래 어셈블리까지 번역되는 셈이죠.
정밀함으로 따지면 지시를 형식 언어로 표현하는 쪽이 더 쉽고, 자연어 자체가 크게 바뀌지 않는 한 이 사실이 당분간 달라질 것 같지 않습니다. 그럴 가능성은 아주 낮다고 봅니다. 형식 문법, 명확하게 정의된 의미론, 재현성, 합성 가능성이 전통적인 프로그래밍 언어를 자연어보다 프로그래밍에 더 믿음직하고 정밀하게 만드는 특성입니다.
이제 다들 변호사네요.
관련해서 말씀드리면, 모든 사용 사례의 90%에서는 LLM과 대화해서 코드를 만드는 방식이 맞는 접근이라고 봅니다.
LLM이 그때그때 직접 일을 처리하는 흐름을 만드는 것과는 반대죠.
코드는 결정론을 만들어 내지만, LLM은 그렇지 않습니다.
제가 Croft로 걸고 있는 내기가 바로 이겁니다. 지저분한 부분은 LLM이 알아내고, 일단 알아낸 뒤에는 소프트웨어가 더 나은 도구라는 거죠. 소프트웨어는 LLM이 쓰게 하면 됩니다.
소프트웨어 엔지니어가 하는 일이 원래 이거예요. 다만 더 비싸고 시간이 더 걸릴 뿐입니다.
“처음 90%의 코드가 처음 90%의 개발 시간을 차지하고, 나머지 10%가 나머지 90%를 차지한다.”
90%는 1%의 시간에 해낼 수 있어요. 나머지 10%에 99%의 시간이 들고요.
지금은 그게 딱 제 적정선이에요. 저는 아직 LLM의 “좀 더 멍청한 페어 프로그래밍 파트너” 노릇을 하고 싶거든요 ㅋㅋ
자기비하로 하신 말씀인지 정말 그러길 원하시는 건지 모르겠지만, 그런 방식은 좋은 소프트웨어 엔지니어링으로 이어지지 않습니다.
코드를 쓰는 게 본인이든 LLM이든 다른 사람이든, 만들고 있는 것의 아키텍처와 설계는 본인이 책임지고 가장 잘 아는 사람이어야 합니다.
LLM이 당신보다 “더 똑똑한” 게 아니라, 더 수완이 좋은 겁니다. LLM은 (성공률은 제각각이지만) 당신의 설계를 코드로 옮길 수 있습니다. 그 로직의 설계를 계속 책임지고 주도하지 않는다면, LLM이 일회용이나 취미용 애플리케이션 말고는 어떤 것도 성공시켜 주지 못할 겁니다.
문제를 잘 설계하고 쪼개서 좋은 시스템을 떠올릴 줄 안다면, 코드가 걸림돌이 될 가능성은 낮습니다.
어떤 아이디어든 의사코드로 구체화해 볼 수 있습니다. 그게 끝나면 실제 코드로 옮겨 보세요. 그 과정이 꽤 기계적이라는 걸 알게 될 겁니다. 그래서 사람들이 오래전부터 코딩은 문제가 아니었다고 말하는 겁니다. 어려운 부분은 알고리즘을 생각해 내는 것입니다.
코딩에서 어려움을 겪는 사람은 그 언어나 플랫폼에 익숙하지 않거나, 의사코드나 다이어그램이 아니라 실제 코드로 생각하려 드는 사람입니다.
덧붙임
실제 코드로 생각하는 기법도 있습니다. 대개 좋은 에디터와 짝을 이룬 편집-컴파일-실행 워크플로, 또는 (역시 좋은 에디터가 도와주는) REPL 워크플로입니다. 여기에 문서 시스템(매뉴얼 페이지?)과의 연동까지 잘 갖추면 금상첨화죠.
하지만 저는 개발 사이클이 로컬 재빌드 20분이나 CI 한 시간에 의존하는 프로젝트에도 있어 봤습니다. 그런 레거시 프로젝트에는 이런 워크플로를 적용하기가 정말 어려울 수 있습니다.
“꽤 기계적”이라는 주장에는 좀 의문이 듭니다. 실제 프로그래밍 언어 코드로 옮기는 순간 온갖 엣지 케이스가 드러나고, 자연어나 의사코드가 다루지 않았던 새로운 질문이 잔뜩 튀어나온다고 생각합니다.
제 경험상 이건 본질적으로 맞는 말입니다. 저는 문제 해결을 잘하고 멘토링도 잘해서 경력 초기에 일찌감치 시니어급 엔지니어 자리에 올랐습니다. 경험이 거의 없는 언어 스택을 쓰는 프로그래머 자리를 몇 번 맡았는데, 별 어려움 없이 해냈습니다.
물론 문제를 풀려면 코드를 깊이 아는 것이 필수인 문제도 있습니다. 하지만 그런 문제는 비즈니스 문제의 1%도 안 될 만큼 소수입니다.
평균적인 개발자가 이해해야 할 범위로 보면, 함수형 언어 하나와 절차형 언어 하나를 확실히 알고 있으면 적절한 자료만 있다는 전제하에 어떤 언어 생태계에서든 거의 모든 문제를 풀 수 있습니다. 그렇게 할 수 있다면 LLM의 프로그래밍 도움도 잘 활용할 수 있습니다. 그럴 수 없다면 아직은 LLM에 크게 의존하지 않는 편이 좋습니다.
Microsoft Copilot Studio 같은 클라우드 제품을 보세요. https://www.microsoft.com/en/copilot/products/copilot-studio
전통적인 코딩은 MCP 도구 쪽에만 남아 있고, 그것마저 생성할 수 있습니다.
저는 영어가 이제 고수준 프로그래밍 언어라고, 기계어에서 <자기가 좋아하는 언어>로 올라오는 사슬의 다음 추상화 단계라고 말하는 사람에게 이 글을 보내 줍니다. 이 비유 뒤에는 보통 “대부분의 프로그래머가 어셈블리 코드를 안 보는 것처럼, 고수준 소스 코드를 들여다보는 것도 소수만 하는 일이 될 것이다”라는 말이 따라붙습니다.
다익스트라의 논증이 사람들의 생각을 바꾸는 경우는 드뭅니다. 이 비유가 먹히는 건 거기에 진실의 알맹이가 있기 때문이라고 봅니다. AI 모델이 이제 자연어로 프로그래밍하는 마법 같은 능력을 주는 건 맞지만, AI 모델이 일종의 컴파일러라는 함의는 심각하게 잘못됐습니다. AI 모델은 컴파일러를 대체하는 게 아니라 소프트웨어 개발자를 대체합니다. 소프트웨어 개발자가 어셈블리 코드를 거의 안 보는 이유는, 컴파일러(그리고 에뮬레이터와 VM)가 원래 프로그램의 의미를 정확하게 보존해 주기 때문입니다.
자연어 명세는 프로그램이 아니고, 그 명세를 만족하는 프로그램은 수없이 많습니다. 소프트웨어 엔지니어와 AI 모델은 명세를 코드로 바꾸지만, 코드가 곧 명세는 아닙니다. 이 비유는 컴퓨터가 무엇을 해야 하는지 형식적으로 기술하고, 사람에게도 AI 모델에게도 맡겨서는 안 되는 프로그램의 속성을 강제하는 도구로서 프로그래밍 언어가 갖는 가치를 흐린다고 생각합니다.
지인분들이 생각을 안 바꾸는 건 논거가 약하기 때문입니다. 글도, 당신의 댓글도 자연어로 하는 상호작용의 유일한 방식이 프로그래머의 의도를 아주 높은 수준에서 기술하는 것이라고 가정하고 있습니다.
AI에게 “계산기 만들어 줘”라고 할 수도 있고, “이제 UI의 각 키에 대응하는 불리언 배열을 선언하고, 키가 눌리면 해당 배열 위치를 true로 설정해”라고 할 수도 있습니다.
이 명령들은 추상화 수준이 너무 달라서 같은 범주로 묶기 어려운 자연어입니다. 뒤쪽은 LLM이 컴파일러 역할을 하는 명령형 언어라고 불러도 공정하다고 봅니다. 제가 AI를 쓰는 방식이 그것과 아주 비슷합니다.
아니요, 글은 차이가 형식 언어와 비형식 언어에 있다고 명시적으로 말합니다.
프로그래밍 언어는 (적어도 그 일부에 대해서는) 연산적 의미론을 정의할 수 있습니다. 엉성한 언어라 해도 어차피 아주 작고 제한적이니까요. 의미 보존 정리를 형식적으로 증명할 수도 있고, 비슷한 방식으로 비형식적으로 추론할 수도 있습니다.
자연어에서는 그게 어느 정도 사라집니다. 모델이 확률적이라는 점은 건드리지도 않았는데 말이죠.
AI가 자연어 프로그래밍이라면 1978년에도 이미 있었다는 얘기가 됩니다. AI는 사실 “프로그래머에게 코드를 써 달라고 부탁하는 것”이니까요. 심지어 추상화 수준을 달리해서 부탁할 수도 있고요!
“자연어 벽돌 쌓기”랑 똑같아요. 돈 주고 고용한 벽돌공에게 말로 지시해서 벽을 세우는 거죠.
지능의 마법 같은 비결은 맥락 인식입니다. 프롬프트가 다소 포괄적이라면(“계산기 만들어 줘”) AI는 질문을 하거나, 합리적인 가정을 하거나, 둘 다 해야 합니다.
제 경험상 LLM은 대체로 합리적인 가정을 하지만, 가끔 그러지 않아서 되돌아가 “아니, 내 말은 이거였어…”라고 말해야 합니다.
과정이 잘 풀리면 거의 마법 같습니다. LLM이 당신의 필요를 제대로 예측해서, 당신이 직접 했을 때보다 훨씬 충실하게 요청을 구현해 주니까요.
그렇지 않으면 당신이 치워야 할 난장판이 생깁니다.
그래도 대체로 최고 수준의 LLM이 짧은 설명만 듣고도 시키지 않은 유용하고 올바른 세부 사항까지 알아서 채워 넣으며 Do The Thing 하는 걸 보면 인상적입니다.
컴파일러는 그렇게 동작하지 않습니다. 컴파일러는 번역기이지, 독립적인 주체성을 가진 (신입급) 발명가가 아닙니다.
어떤 컴파일러는 그럴 수 있지만 아주 통제된 방식으로만 가능합니다. 예를 들어 Haskell의 deriving이나 타입클래스에서 호출할 올바른 메서드를 추론하는 것이 그렇습니다. Prolog/Datalog/SQL도 답에 도달하는 방법의 세부 사항을 상당 부분 알아서 채워 넣습니다.
그건 “I”(지능) 부분일 뿐이죠. 인공(Artificial)의 A가 빠졌잖아요. 자연적으로 만들어진 사람한테 부탁한 거니까요.
그리고 1978년에 I가 없었다고 주장한 사람은 아무도 없으니, 이 논리가 뭘 증명하려는 건지 모르겠네요.
제 짐작에는, 자연어를 해석하는 데 임의의 수준의 지능이 필요하다면 “자연어 프로그래밍”이 존재하느냐는 문제에서는 지금과 1978년 사이에 달라진 게 없다는 걸 보이려는 것 같습니다. 지능이 이제 인공이라는 이유만으로 답이 달라지지는 않는다는 거죠.
답이 달라질 뿐 아니라, 그게 바로 질문의 전부입니다. 사람들이 “자연어 프로그래밍”의 가능성이나 불가능성을 이야기할 때는 언제나 “기계가 실행 가능한 코드로 번역해 주는 자연어 지시”를 뜻했습니다.
사람 프로그래머가 자연어 지시를 받고 코드를 쓸 수 있다는 건 아무도 의심하지 않았으니, 굳이 말로 풀어 쓸 필요가 없었습니다. 그건 ENIAC 때부터, 아니 에이다 러브레이스 때부터 이미 있던 일이니까요.
그게 지금 우리에게 있는 것이고, “자연어 프로그래밍”이 존재하느냐는 문제에서 1978년과 지금 사이에 달라진 점입니다.
다음 두 문장은 서로 다릅니다.
자연어로 프로그래밍할 수 있다.
자연어로 AI에게 할 일을 말하면, AI가 요구 사항을 수집하고, 설계를 스케치하고, 코드를 쓰고, 오류 메시지를 웹에서 검색하고, 프로그램이 동작할 때까지 반복해서 고친다.
사람들이 신경 쓰는 어떤 측면에서는 둘이 같습니다(컴퓨터에게 원하는 걸 말하면 프로그램이 튀어나오니까요). 하지만 다른 측면에서는 절대 같지 않습니다(자연어는 기계 명령어로 결정론적으로 대응되지 않으니까요).
수십 년 전에 사람들이 자연어 프로그래밍을 이야기했을 때는 자연어를 기계어로 컴파일하는 시스템을 말한 겁니다. 요구 사항을 자율적으로 수집하고, 어떤 프레임워크를 쓸지 정하고, 오류 메시지를 웹에서 찾아보고, 아이디어를 반복해서 다듬는 시스템 같은 건 말하지 않았어요. 이런 것들은 “자연어가 기계어로 대응된다”와 너무 달라서, “컴퓨터에게 원하는 걸 말한다”만이 살펴볼 가치가 있는 측면이라고 생각하신다면 유익한 대화가 되기 어렵다고 봅니다.
이건 여전히 두 가지를 혼동하고 있습니다.
하나는 AI를 통해 자연어를 기계어로 바꾸고 싶어 한 사람들입니다. 다만 그 AI를 당시 달성 가능하다고 생각한 방식(기호주의 AI)으로 구현하려 했을 뿐이죠.
다른 하나는 나중의 AppleScript나 일부 4GL 언어처럼 자연어 스타일의 프로그래밍 언어를 원한 사람들입니다.
당시 “자연어 프로그래밍”, 혹은 그때 부르던 이름으로 자연어 처리는 처음부터 첫 번째, 그리고 그 시대의 AI 연구와 묶여 있었습니다.
https://en.wikipedia.org/wiki/Natural_language_processing
그러니 그렇죠, LLM은 없었지만 그런 프로그램이나 “컴파일러”가 동작하려면 세상에 대한 사실과 자연어를 이해해야 한다는 건 꽤 잘 알고 있었습니다.
제 댓글을 오해하셨네요. 제 말은, 프로그래머에게 뭔가를 프로그래밍하라고 지시하는 것이 “자연어 프로그래밍”(에이전틱 코딩이 하는 일)이라면 1978년에도 “자연어 프로그래밍”이 있었다는 뜻이었습니다. 1978년에 AI가 있었다고 말한 게 아닙니다.
게다가 뒤쪽 예시는 계산기를 명세하는 것과도 거리가 멉니다. 그리고 “Show HN” 프로젝트들을 보면, 평범한 사람이 AI로 소프트웨어를 만드는 과정과도 아주 거리가 멀고요.
아니요, 컴퓨터가 무엇을 해야 하는지 정밀하게 기술할 필요를 대체하는 겁니다. 빈틈을 정말 잘 메워 주는 메커니즘으로요. 하지만 추측으로 갈 수 있는 데는 한계가 있습니다.