당신의 AI 코딩 어시스턴트는 왜 그렇게 작성했는지 기억하지 못합니다
요약
AI 코딩 어시스턴트 사용으로 인해 발생하는 '이해 부채(Comprehension debt)'의 위험성을 경고합니다. AI가 생성한 코드는 속도를 높여주지만, 작성 과정의 논리적 단계를 생략하여 팀이 코드의 의도를 파악하지 못하게 만듭니다.
핵심 포인트
- 기술 부채와 달리 이해 부채는 의도치 않게 획득되는 부채임
- AI는 코드 생성 속도를 높이지만 논리적 추론 단계를 생략함
- 이해 부채는 기존 대시보드 지표(속도, 빈도 등)로 측정하기 어려움
- 코드의 동작과 팀의 이해 사이의 간극이 장애의 원인이 됨
풀 리퀘스트(Pull Request)가 도착합니다. 승인 두 건, 테스트 통과(Green), CI 통과(Blue). 화요일에 배포됩니다.
3주 후 장애(Incident)가 발생합니다. 누군가 파일을 열었는데 아무도 논의한 기억이 없는 브랜치를 발견합니다. 그것은 희귀한 케이스를 처리하고 있는데, 잘못 처리하고 있습니다. 당신은 작성자에게 이것이 무엇을 위한 것인지 묻습니다. 그들은 코드를 뚫어지게 쳐다봅니다. 본인이 작성했습니다. 하지만 왜 그렇게 작성했는지는 말하지 못합니다.
이것은 실력이 부족한 엔지니어에 대한 이야기가 아닙니다. 저를 포함하여 제가 아는 모든 엔지니어가 그런 작성자였던 적이 있습니다. 제가 배포한 것 중 가장 이해하지 못하는 코드는 제가 가장 빠르게 생성한 코드이며, 그것은 거의 항상 제가 가장 자신 있는 도메인(Domain)에서 가장 멀리 떨어진 코드입니다. 제 전문 분야에서는 잘못된 제안이 즉시 이상하게 느껴집니다. 하지만 두 디렉토리 떨어진 곳에서는 그저 코드처럼 보일 뿐입니다.
그래서 우리는 더 빨라지지만 더 적게 이해하게 됩니다. 이 두 사실은 서로 다른 곳에서 나타나기 때문에 연결되어 있다고 느껴지지 않습니다. 하나는 대시보드상의 속도(Velocity)로 나타나고, 다른 하나는 6주 뒤 사후 분석(Postmortem)에서의 혼란으로 나타나기 때문입니다.
하지만 이 둘은 연결되어 있습니다.
우리가 이름을 붙이지 못한 것
우리는 이해했음에도 불구하고 배포한 코드에 대해 부를 이름이 있습니다. 바로 기술 부채(Technical debt)입니다. 이것은 정확히 하나의 '결정(Decision)'을 명명한다는 점에서 좋은 단어입니다. 누군가가 지름길을 저울질했고, 그것을 선택했으며, 운이 좋은 경우라면 그것을 되돌리는 데 비용이 얼마나 들지 기록해 두었을 것입니다. 부채(Debt)는 대출자, 잔액, 그리고 계획을 암시합니다. 로드맵(Roadmap)에 올릴 수 있고, 진지한 얼굴로 논쟁할 수도 있습니다.
우리가 이름을 붙이지 못한 것은, 그 누구의 이해를 거치지 않고 시스템에 들어온 코드입니다.
이를 **이해 부채 (Comprehension debt)**라고 부릅시다. 이것은 여러분의 코드베이스(Codebase)가 수행하는 일과 여러분의 팀이 설명할 수 있는 것 사이의 간극입니다. 중요한 차이점은 이것입니다: 기술 부채는 여러분이 '선택한' 부채이고, 이해 부채는 여러분이 '획득한' 부채입니다. 아무도 결정하지 않았습니다. 티켓도 없고, 코멘트도 없으며, // TODO: 이것은 ~때문에 임시방편(Hack)입니다와 같은 주석도 없습니다. 커밋(Commit)은 주변의 다른 커밋들과 똑같이 보입니다. 그것이 바로 문제의 핵심입니다: 생성되는 순간에는 보이지 않고, 발견되는 순간에는 비용이 많이 들며, 그 두 순간 사이의 간격이 너무 멀어서 아무도 그것들을 연결 짓지 못한다는 것입니다.
또한 그것은 팀 대시보드의 그 어떤 지표에도 보이지 않습니다. 속도(Velocity)는 이를 볼 수 없습니다. 배포 빈도(Deployment frequency)도 볼 수 없습니다. 특히 커버리지(Coverage)는 더욱 그렇습니다. 생성된 테스트는 생성된 코드의 동작을 기쁘게 단언(assert)할 것이며, 그 둘은 완벽한 일치를 이룬 채 영원히 함께 틀리게 될 것입니다. 만약 당신의 대시보드가 이해 부채(comprehension debt)를 측정했다면, 1년 전부터 그것이 쌓이고 있다는 것을 알아차렸을 것입니다. 하지만 측정하지 않으므로, 당신은 장애(incident)가 발생하는 동안에야 그것을 알아차리게 될 것입니다. 아무의 보너스도 이해(comprehension)에 달려 있지 않습니다.
이것이 어휘입니다. 이제 메커니즘을 설명하겠습니다.
AI가 코드를 나쁘게 만든 것이 아닙니다. AI는 단계를 삭제했습니다.
이 불만의 흔한 버전은 AI가 나쁜 코드를 작성한다는 것입니다. 그것은 틀렸으며, 틀렸기 때문에 논거로서 가치가 없습니다. 당신이 "AI는 나쁜 코드를 작성한다"라고 말할 때, 누군가 당신에게 깔끔하고, 관용적(idiomatic)이며, 잘 팩터링(factored)된 함수를 보여준다면 대화는 끝납니다. 당신은 졌습니다. 당신이 무언가에 대해서는 맞았을지 몰라도, 그것은 아니었습니다.
판매자들은 초능력(superpowers)이라고 말합니다. 비판가들은 슬롭(slop, 쓰레기)이라고 말합니다. 둘 다 코드를 이야기하고 있지만, 코드는 결코 문제가 아니었습니다.
실제로 일어난 일은 다음과 같습니다.
소프트웨어를 구축하는 것은 과거에 하나의 시퀀스(sequence)였습니다. 폭포수(waterfall) 모델이 아니라, 실제 단계들이 포함된 시퀀스였습니다. 당신은 문제를 파악했습니다. 무엇이 "완료(done)"를 의미하는지에 대해 논쟁했습니다. 설계를 스케치하면 시니어(senior)가 그 설계의 허점을 찾아냈습니다. 당신은 무엇을 수용할지 기록했습니다. 그런 다음 그것을 구축했습니다. 그 단계 중 일부는 10분이 걸렸고 화이트보드 위에 머물렀을 수도 있습니다. 괜찮습니다. 그 단계들은 여전히 존재했으며, 각 단계는 무언가를 남겼습니다: 결정, 제약 조건, 혹은 문서화된 완료(done)의 정의 말입니다. 그 단계들의 잔여물은 그 단계를 수행한 사람들에게 분산된 "이해(understanding)"였습니다.
AI는 그 시퀀스를 단 하나의 단계로 붕괴시켰습니다. 프롬프트(Prompt)를 넣으면 구현(implementation)이 나오는 식입니다.
우리는 이제 이 단일 단계 버전에 이름을 붙였습니다. 바이브 코딩(Vibe coding)입니다. 이는 정직한 이름이며, 저는 이를 높게 평가합니다. 이 이름은 프로세스의 얼마만큼이 살아남았는지를 정확하게 알려주기 때문입니다.
그리고 무엇이 생략되었는지 주목하십시오. 코딩이 아닙니다. 코딩은 오히려 더 빨라진 유일한 부분입니다. 생략된 것은 그 이전의 모든 과정입니다: 문제 프레이밍 (problem framing), 설계 논의 (design argument), 수락 기준 (acceptance criteria). 이것들은 코드 '안'에 존재한 적이 없었기에, 코드에 대해 학습된 모델은 그것들이 존재한다는 사실조차 배운 적이 없습니다. 당신은 1단계의 입력을 제공하면서 5단계의 출력을 요구하고 있으며, 모델은 이에 이의를 제기하지 않습니다. 모델은 2단계부터 4단계가 누락되었다는 것을 알 수 없습니다. 모델은 그 어떤 것에도 이의를 제기한 적이 없습니다. 모델은 '왜(why)'에 대한 기억이 없습니다. '왜'를 보여준 적이 없기 때문입니다. 모델에게는 수억 개의 '무엇(whats)'만이 보여졌을 뿐입니다.
교묘한 부분은 증거의 비대칭성입니다. 생략된 설계 논의는 어떠한 산출물 (artifact)도 만들어내지 않습니다. 저장소 (repo)에는 the-argument-we-didnt-have.md와 같은 이름의 파일이 존재하지 않습니다. 따라서 그 부재는 코드베이스 내에 아무런 흔적을 남기지 않습니다. 설계에 대해 한 시간 동안 토론했든 아니면 전혀 고려하지 않았든, 디프 (diff)는 똑같아 보입니다. 유일한 차이점은 누군가의 머릿속에만 존재하며, 만약 그 단계가 생략되었다면 그조차도 누구의 머릿속에도 남아있지 않게 됩니다.
"더 절제하세요"는 통제 수단이 아닙니다
뻔한 답은 우리가 타이핑하기 전에 생각해야 한다는 것입니다. 모두가 이미 알고 있는 사실입니다. 저도 알고 있습니다. 커리어 초기에 저는 누군가로부터 거의 한 시간 동안 설계에 대해 방어하도록 요구받았던 코드 리뷰 (code review)에 참여한 적이 있는데, 저는 오늘날에도 그 설계를 기억합니다. 그 직장에서의 다른 것들은 거의 기억나지 않습니다.
그럼에도 저는 여전히 그 단계를 건너뜁니다. 항상 그런 것은 아니지만, 2년 전보다 더 자주 그러는데, 그 이유는 구조적입니다.
환경이 아무런 저항을 제공하지 않기 때문입니다. 설계 논의를 건너뛰는 것은 즉각적인 비용이 들지 않으면서 즉각적인 이득을 줍니다. PR (Pull Request)은 올라가고, 티켓 (ticket)은 이동하며, 대시보드 (dashboard)는 초록색이 됩니다. 그리고 그 비용은 6주 후에 관련 없는 사고로 위장하여 나타납니다. 즉각적인 보상이 있고 지연되며 잘못 귀인된 처벌이 따르는 모든 행동은 당신의 의도를 이길 것입니다. 이것은 도덕적 실패가 아닙니다. 이것은 피드백 루프 (feedback loop)이며, 피드백 루프는 의지력으로 고치는 것이 아닙니다. 환경이 허용하는 것을 바꿈으로써 고치는 것입니다.
당신을 측정하는 그 누구도 당신이 그것을 이해했는지 묻지 않았습니다. 그들은 얼마나 많이 했는지만을 묻습니다.
여기서 유일하게 흥미로운 질문은 이것입니다: 만약 단계를 건너뛸 수 없다면 어떤 모습일까?
Waves (파동)
저는 그 해답을 만들어 온 팀의 일원입니다. 그러니 이 점을 염두에 두고 남은 글을 읽어주세요. 저는 이 도구를 만드는 데 참여했으며, 이 도구가 제대로 작동한다고 말하기에 가장 자격이 없는 사람입니다.
이 도구의 이름은 nWave입니다. 이 도구는 Claude Code 내부에서 실행됩니다. 즉, 여러분이 이미 사용 중인 것과 동일한 하네스 (harness)를 사용하므로 새로 도입할 필요가 없습니다. 아이디어는 간단합니다. 시퀀스 (sequence)를 다시 배치하고, 각 단계에 이름을 붙이며, 각 단계가 당신이 반드시 확인해야 하는 아티팩트 (artifact)를 생성하도록 만드는 것입니다.
일곱 개의 파동 (waves). 문제를 발견합니다. 접근 방식에 대해 발산 (diverge)합니다. 사용자 스토리 (user stories)와 수락 기준 (acceptance criteria)이 생길 때까지 토론합니다. 아키텍처 (architecture)를 설계합니다. 전달 경로 (delivery path)를 계획합니다. 수락 테스트 (acceptance tests)를 추출합니다. 테스트 우선 (test-first) 방식으로 코드를 전달합니다. 마지막 두 단계만이 필수적입니다. 모든 기능은 수락 테스트와 테스트 주도 (test-driven) 코드로 마무리되어야 하며, 앞선 다섯 단계의 파동은 선택 사항입니다. 버그 수정에는 시장 조사 (market discovery)가 필요하지 않으며, 그렇지 않은 척하는 것이 바로 방법론 (methodologies)이 사멸하는 방식이기 때문입니다.
그리고 특정 메커니즘이 작동할지 진심으로 알 수 없을 때는, 탐색 (probe)에 타임박스 (timebox)를 설정하여 설계에 반영하기 전에 미리 확인합니다. 그 결과는 하나의 발견 (finding)이며, 이는 다른 모든 것과 마찬가지로 하나의 아티팩트 (artifact)입니다.
파동 목록(가장 흥미롭지 않은 부분)보다 더 중요한 세 가지가 있습니다.
각 파동은 아티팩트 (artifact)를 남깁니다. 이것이 실제 메커니즘입니다. 단계를 건너뛰었을 때 그것이 보이지 않는 이유는 아무것도 생성하지 않기 때문입니다. 해결책은 모든 단계가 무언가를 생성하도록 만드는 것입니다. 사용자 스토리 (user story), 설계 문서 (design document), 혹은 Given-When-Then 시나리오 (scenario) 같은 것 말입니다. 이제 부재 (absence)는 눈에 보이게 됩니다. 파일이 있거나 없거나 둘 중 하나이며, 당신은 파일을 검토할 수 있기 때문입니다.
게이트(gates)는 결정론적(deterministic)입니다. LLM이 LLM을 채점하는 방식이 아닙니다. 그런 방식은 대규모로 확신에 찬 헛소리(confident nonsense)를 생성하기에 아주 좋은 방법이죠. 검증(checks)은 훅(hooks)입니다. 즉, 결과물(artifacts)을 검사하고 차단하거나 허용하는 코드입니다. 잘못된 이유로 실패한 테스트는 실패(red)로 간주되지 않습니다. 테스트가 없는 커밋은 통과할 수 없습니다. 기계의 의견을 묻지 않습니다.
어떠한 것도 감독 없이 엔드 투 엔드(end to end)로 실행되지 않습니다. 각 웨이브(wave)는 멈추고 당신에게 결과물을 전달합니다. 당신이 승인하거나, 승인하지 않거나 둘 중 하나입니다. 설계상 약간 번거롭게 만들어져 있습니다.
인간이 위치하는 곳
만약 제가 이 글을 읽고 있다면, 다음과 같은 강력한 이의를 제기할 것입니다. "당신은 방금 AI 에이전트가 전체 라이프사이클(lifecycle)을 오케스트레이션(orchestrate)하는 시스템을 설명해 놓고서는, 이를 AI에 과도하게 위임하는 것에 대한 방어책이라고 홍보하고 있군요." 이것은 마치 방화범이 화재 경보기를 팔고 있는 것처럼 들립니다.
제가 주장하고자 하는 차이점은 인간이 필요한(required) 지점과 단순히 허용된(permitted) 지점의 차이입니다.
오늘날 당신은 AI의 결과물을 검토할 수 있도록 허용되어 있습니다. 설계를 먼저 고민하는 것도 허용되어 있습니다. 하지만 허용(permission)은 통제(control)가 아닙니다. 실제로 일어나는 일은 허용이 습관으로 변질되고, 습관은 가장 저렴한 방식(whatever is cheapest)으로 변질된다는 것입니다.
웨이브(waves)가 존재하는 이유는 결정을 내리기에 비용이 저렴하면서도, 건너뛰었을 때 비용이 많이 드는 지점에서 결정을 강제하기 위함입니다. 즉, 우리가 무엇을 만들고 있는지, '완료(done)'의 의미는 무엇인지, 이것이 어떤 형태를 갖춰야 하는지, 무엇을 수용할 것인지와 같은 것들입니다. 이것들은 판단(judgment calls)의 영역이며, 시스템 내의 그 어떤 것도 당신을 대신해 이를 결정할 수 없습니다. AI는 초안을 작성하고, 제안하고, 논쟁하고, 생성할 수는 있지만, 당신이 승인(ratify)하지 않은 단계 너머로 진행할 수는 없습니다. 그것이 설계의 전부입니다. 자율성(autonomy)이 아니라 체크포인트(checkpoints)인 것입니다.
그것이 실제로 제대로 작동하느냐는 별개의 문제이며, 제가 구축을 도운 이 도구 내부에서는 당신을 위해 답변해 드릴 수 없는 문제입니다.
비용
더 느립니다. 기능당 비용 측면에서 피할 수 없이 더 느려지며, 자신의 방법론이 무료라고 말하는 사람은 무엇인가를 팔고 있는 것입니다. 당신은 이전에는 무료로 건너뛰었던 단계들에 대해 실제 시간 (wall-clock time)과 토큰 (tokens)을 지불하고 있는 것입니다. 이것이 전체적인 거래의 핵심입니다. 당신은 비용을 사고(incident)가 발생하는 시점—비싸고 예측 불가능하며 새벽 2시에 당신을 덮치는 시점—에서, 비용이 저렴하고 계획된 시점인 설계 대화(design conversation)로 옮기는 것입니다. 이는 사고의 비용이 매우 큰 상황에서는 정확히 좋은 거래입니다.
즉, 이는 많은 작업에 있어 잘못된 도구라는 뜻이기도 합니다. 일회용 스크립트, 당신이 곧 전체 청중인 개인 프로젝트, 그 누구에게도 설명할 필요가 없는 모든 것들—이러한 경우에는 절차(ceremony)를 내려놓고 빠르게 진행하십시오. 이해할 대상이 없다면 이해 부채 (comprehension debt)도 존재하지 않습니다.
이 방법론은 "빠르게 출시하고 나중에 수정한다"는 방식이 "나중에"라는 답변을 받아들이지 않는 상황에 직면했을 때 그 비용만큼의 가치를 증명합니다. 결제 시스템 (Payment rails), 마지막에 컴플라이언스 감사 (compliance audit)가 따르는 모든 것, 혹은 당신이 생각하지 못한 예외 케이스 (edge case)가 누군가의 돈이나 그보다 더 심각한 문제와 직결되는 모든 상황이 이에 해당합니다. 만약 당신이 그런 곳에서 일하고 있다면, 완전히 설명할 수 없는 무언가를 출시할 때의 기분을 이미 알고 있을 것이며, 그것이 저절로 나아지지 않는다는 사실도 이미 알고 있을 것입니다.
요구 사항
저는 당신에게 이것을 채택하라고 강요하지 않을 것입니다. 당신은 수년간 이 일을 해왔으며, 인터넷상의 낯선 이가 제안하는 프레임워크 (framework)가 필요하지 않을 것입니다. 이미 여러 개가 존재하니까요.
제가 실제로 원하는 것은, 당신이 리포지토리 (repo)를 열어보고 이 방법론의 어디가 틀렸는지 저에게 말해주는 것입니다. 다음 포스트에서는 구체적인 내용을 다룰 것입니다. 단일 웨이브 (single wave)를 실행할 때 어떤 모습인지, 그리고 왜 수락 테스트 (acceptance tests)가 구현 (implementation)을 담당하는 에이전트와 다른 에이전트로부터 나오는지에 대해 설명하겠습니다.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/nWave-ai/nWave/main/scripts/install/install.sh)"
네, 그것은 쉘(shell)로 파이프(pipe)된 curl 명령입니다. 만약 실행하기보다 스크립트를 먼저 읽어보려는 본능이 들었다면, 아주 좋습니다. 그것이 바로 이 포스트의 핵심 논지가 저 자신에게 적용된 사례입니다. 여기서 확인하실 수 있습니다. 이 스크립트는 uv를 우선적으로 사용하며, 실패할 경우 pipx로 대체하고, CLI를 설치한 다음 설치 프로그램을 실행합니다. 만약 쉘로 무언가를 파이프하는 것을 원치 않는다면, pip install nwave-ai && nwave-ai install 명령을 통해 동일한 결과에 도달할 수 있습니다. 가능하다면 uv를 사용하세요. 그것이 권장되는 경로이며, pip와 pipx는 동등한 선택지가 아닌 지원 가능한 대체 수단(fallbacks)입니다.
파도(waves)는 마크다운(markdown)이며, 게이트(gates)는 파이썬(Python)입니다. 이 모든 것은 저녁 한때면 읽을 수 있는데, 이는 의도된 것입니다. 사용자가 검사할 수 있는 수준보다 더 많은 신뢰를 요구하는 도구는, 그것이 통제하려는 AI와 동일한 문제를 안고 있기 때문입니다.
그동안, 다음 PR(Pull Request) 리뷰 시 염두에 둘 만한 질문 하나를 던져봅니다. 작성자가 '왜' 그렇게 했는지 설명할 수 있는가?
만약 대답이 '아니오'라면, 코드는 어쨌든 배포되었습니다. 항상 그렇습니다. 바로 그 점이 당신을 괴롭혀야 하는 부분입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기