본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 27. 22:55

당신의 AI가 나쁜 코드를 작성하는 것이 아닙니다. 당신이 나쁜 명세(Spec)를 작성하고 있는 것입니다.

요약

AI 에이전트가 잘못된 결과를 내는 원인은 모델의 성능 부족이 아니라 모호한 명세(Spec) 작성에 있음을 지적합니다. 명확한 제약 조건과 예외 상황이 누락된 명세는 AI가 의도와 다르게 동작하게 만드는 'GIGO' 현상을 초래합니다.

핵심 포인트

  • AI 에이전트는 인간과 달리 모호함을 스스로 판단하지 않고 명세대로만 실행함
  • 명확한 제외 규칙과 제약 조건이 없는 명세는 치명적인 오류를 유발함
  • AI 시대의 GIGO는 데이터 품질을 넘어 명세(Spec)의 품질 문제로 확장됨
  • 에이전트 활용 시 예외 상황(edge case)을 명시적으로 정의하는 것이 필수적임

알림이 도착했을 때 아웃리치(outreach)는 이미 10분 동안 실행 중이었습니다. 제 전체 설정이 지난 6개월 동안 조용히 피해 왔던 연락처로 이메일이 발송되었습니다. 제 실수였습니다. 너무 늦었습니다...

저는 명세(spec)를 다시 읽었습니다. 거기에는 이렇게 적혀 있었습니다: "**활성 잠재 고객(active prospects)**에게 후속 조치를 취할 것." 그것이 전부였습니다. 제외 목록도, 상태 필터도, 누구에게 연락하지 말아야 할지에 대한 제약 조건도 없었습니다. Claude는 망설임 없이, 아무것도 빠뜨리지 않고 제가 작성한 그대로를 실행했습니다.

병목 현상은 모델도, 프롬프트(prompt)도 아닌 바로 명세(spec)입니다.

저는 한 줄을 빠뜨렸습니다. Claude는 나머지를 수행했습니다.

해당 에이전트(agent)는 전체 연락처 목록에 접근할 수 있었습니다. 활성 잠재 고객, 휴면 리드(dormant leads), 6개월 전 조용히 거래를 중단한 유통업체, 그리고 자동화된 이메일을 통해 누구도 다시 언급하고 싶지 않은 방식으로 종료된 파트너십의 인원 2~3명까지 모두 포함되어 있었습니다. 명세(spec)에는 그런 내용이 전혀 없었습니다. 명세(spec)에는 "활성 잠재 고객"이라고만 되어 있었는데, 제 머릿속에서는 그것이 수개월간의 맥락, 12번의 비공식적인 결정, 그리고 제가 어디에도 기록하는 것을 완전히 잊어버린 최소 한 번의 불편한 대화로부터 만들어진 의미를 담고 있었습니다.

Claude는 그 중 어떤 것도 알지 못했습니다. Claude는 명세(spec)에 적힌 내용만을 알았습니다.

그래서 Claude는 후속 조치를 취했습니다. 명시적으로 제외되지 않은 모든 사람에게 말이죠. 전문적이고 신속하게, 제가 요청한 정확한 어조로 말입니다. 연락처 목록에는 "이메일 발송 금지" 플래그가 없었는데, 제가 추가할 생각을 하지 못했기 때문입니다. 명세(spec)에는 제외 규칙이 없었는데, 저는 그것이 당연하다고 가정했기 때문입니다. 에이전트(agent)는 제가 의도한 것이 아니라, 존재하는 것을 바탕으로 실행되었습니다.

이것이 AI의 실행 속도와 함께 변화하는 지점입니다. 동일한 명세(spec)를 전달받은 인간 개발자(dev)라면 명확한 질문을 던졌거나, 판단을 내렸거나, 적어도 예외 상황(edge case)에서 망설였을 것입니다. 에이전트(agent)는 망설이지 않습니다. 결과물을 내놓을 뿐입니다. (C 언어에서는 이를 **미정의 동작 (undefined behavior)**이라고 부릅니다. AI 에이전트(agent)에서는 이를 "내가 작성한 명세(spec)"라고 부릅니다.)

Claude는 실수를 하지 않았습니다. 제가 문을 열어두었을 뿐입니다.

(그리고 네, 이 일이 있은 후 저는 해당 파이프라인(pipeline)을 위해 작성했던 다른 모든 명세(spec)를 다시 읽어보았습니다. 첫 번째 검토에서 4개의 누락된 제약 조건을 더 찾아냈습니다.)

GIGO는 언제나 규칙이었습니다.

GIGO: Garbage In, Garbage Out (쓰레기가 들어가면 쓰레기가 나온다). 이 원칙은 1957년으로 거슬러 올라가며, 프로그래머들이 컴퓨터에 잘못된 데이터를 입력하면 완벽하게 일관된 잘못된 결과가 생성된다는 사실을 발견했던 군용 메인프레임 시절에 만들어졌습니다. 기계는 정확하게 실행되었습니다. 문제는 언제나 상류(upstream)에 있었습니다.

당시 기계들은 판단력을 가진 단위가 아니었습니다. 펀치 카드를 읽는 IBM 709였습니다. 모호함은 선택지에 없었습니다. 입력은 이진(binary)이었고, 출력도 이진이었으며, 그 사이의 간극은 언제나 인간이었습니다.

60년 동안 GIGO는 **데이터 품질 문제 (data quality problem)**를 설명해 왔습니다. 나쁜 훈련 데이터(training data)가 들어가면, 나쁜 모델 예측(model predictions)이 나옵니다. 그러한 프레임워크는 여전히 유효합니다. 변한 것은 쓰레기가 유입되는 계층(layer)입니다.

AI 에이전트의 경우, 쓰레기는 데이터 계층이 아닌 **명세 계층 (spec level)**에서 유입됩니다. 입력값은 손상된 CSV 파일이 아닙니다. 그것은 불완전한 명세입니다. 너무 느슨하게 작성된 규칙, 너무 광범위하게 정의된 범위, 누군가의 머릿속에만 존재하는 제외 목록 같은 것들 말입니다. 에이전트는 모호함을 흡수하지 않습니다. 에이전트는 당신이 부여한 속도대로, 접근 가능한 데이터를 바탕으로, 모호함을 뚫고 실행할 뿐입니다.

이번 주에 제 IDE가 2초마다 자동 저장(auto-save)된다는 사실을 깨달았습니다. 지난 몇 년간 이 설정을 켜고 끄기를 최소 5번은 반복했는데, 제가 실제로 어떤 방식을 원했는지 진심으로 기억나지 않습니다. 이 설정은 설정으로서 존재할 만큼은 중요해 보이지만, 그 이유를 기록해 둘 만큼 중요하지는 않았던 것 같습니다. 어쨌든, 요점은 우리가 작은 결정들을 문서화하는 데 서툴다는 것입니다.

Google이 지난달 새로운 병목 현상을 명명했습니다

2026년 5월, Osmani, Saboo, Kartakis가 작성한 Google의 백서(whitepaper)가 Kaggle에 올라왔고, 개발자 커뮤니티가 이를 파악하기 전까지 몇 주 동안 비교적 조용히 머물러 있었습니다. 핵심 논지는 다음과 같습니다. 우리가 겪고 있는 변화는 새로운 언어나 새로운 프레임워크의 등장이 아닙니다. 그것은 **코드를 작성하는 것 (writing code)**에서 **의도를 표현하는 것 (expressing intent)**으로의 이동이며, "AI 문제"로 나타나는 대부분의 실패는 실제로는 상류에서의 의도(intention) 문제입니다.

수치상으로 보면, 현재 전문 개발자의 약 85%가 AI 코딩 에이전트 (AI coding agents)를 정기적으로 사용하고 있으며, 모든 신규 코드의 약 41%가 AI에 의해 생성되고 있습니다. 이 정도 규모에서는 모호한 명세 (spec)가 실제 고객 데이터를 대상으로 생성기 (generator)의 속도로 실행됩니다. 코드 리뷰 (Code review)는 그 중 아무것도 잡아내지 못합니다. 그리고 수학적 계산은 빠르게 복리로 불어납니다. 만약 생성된 코드베이스 중 아주 일부라도 불완전한 명세에 따라 실행되고 있다면, 제가 겪은 아웃리치 (outreach) 사례와 같은 사고의 표면적 영역은 더 이상 예외적인 사례 (edge-case)가 아닙니다. 그것은 기본값이 됩니다.

이 논문의 핵심 프레임워크는 다음과 같습니다: "정직하게 검토했을 때, 대부분의 에이전트 실패는 **설정 실패 (configuration failures)**이다." 모델의 능력 부족 (capability gaps)이 아닙니다. 하네스 (harness, 실행 환경)에서의 설정 실패입니다. Osmani는 백서 (whitepaper) 발표 후 자신의 블로그에 별도로 글을 써서, 에이전트가 예상치 못한 행동을 할 때 그의 첫 번째 조치는 모델에 의문을 제기하는 것이 아니라 하네스를 확인하는 것이라고 밝혔습니다. 대개는 누락된 도구 (tool), 너무 느슨하게 작성된 규칙, 또는 추가하는 것을 잊어버린 가드레일 (guardrail) 때문입니다. 이는 제 아웃리치 파이프라인 (outreach pipeline)에서 일어난 일과 정확히 일치합니다.

이전의 Bloomberg 기사는 잘못된 질병을 진단했습니다: 그들은 증상(생산성 공포, 버그가 있는 AI 출력물, 범위 확장)은 정확히 보았지만 도구를 탓했습니다. 이 백서는 더 정밀합니다. 능력 실패 (Capability failures)와 설정 실패 (configuration failures)는 서로 다른 해결책을 가리키는 다른 문제입니다. 한 가지 해결책은 더 나은 모델을 사용하는 것입니다. 다른 하나는 무언가를 실행하기 전에 작성했어야 할 명세를 제대로 작성하는 것입니다.

당신의 할 일 앱(To-Do App) 명세는 여기서 무너집니다.

TITLE "To-Do App Spec vs. Production Tool Spec" + subtitle "What breaks when spec complexity does not match domain complexity". Metaphor: two side-by-side control panels, one tiny with 3 clean switches, one massive with zones, dials, warning lights, and restricted sections. Style: engineer blueprint line art, technical precision on dark navy background. Palette: navy #0F1B2D, electric blue #4FC3F7, amber #FFC107, white #F5F5F5, alert red #E53935. Content: left panel labeled "TODO APP" with 3 clean switches: ADD ITEM, COMPLETE ITEM, DELETE ITEM; right panel labeled "PRODUCTION TOOL" with labeled zones: WHO TO CONTACT, STATUS RULES, EXCLUSION LIST, OVERRIDE CONDITIONS, CONTRACT FLAGS. Highlight: EXCLUSION LIST and CONTRACT FLAGS zones glow red with warning symbols, each tagged "NOT IN THE SPEC". Footer: (c) rentierdigital.xyz. NOT flat corporate vector, NOT stock infographic, NOT minimalist startup aesthetic.

소프트웨어 개발에서의 명세 복잡도 불일치 (Spec Complexity Mismatch)

이것을 시도해 보세요. 실제 운영 사고(production incident)를 일으킬 만한 할 일(to-do) 앱 명세를 작성해 보세요. 작업을 추가하고, 작업을 완료하고, 작업을 삭제하고, 작업 목록을 나열합니다. 명세가 실제 무언가를 손상시킬 정도로 잘못될 여지는 전혀 없습니다. 최악의 경우라도 완료되지 말아야 할 항목이 완료로 표시되는 정도일 것입니다.

이제 파트너 연락처와 활성 계정(active accounts)에 영향을 미치는 아웃리치 자동화(outreach automation) 명세를 작성해 보세요. 누가 연락을 받는지, 어떤 조건 하에서인지, 언제 제외되는지, 어떤 목록에서 가져오는지, 어떤 상태 필드(status field)를 기준으로 하는지, 보류 중인 계정에 대한 오버라이드(override)는 무엇인지, 그리고 당신이 지난 3주 동안 직접 관리해 온 계약 재협상 중인 2곳의 파트너는 어떻게 처리해야 하는지 말입니다. 누락된 제약 조건(constraint) 하나하나가 에이전트(agent)가 빠져나갈 수 있는 문이 됩니다.

이것은 기본적으로 튜토리얼 구역과 첫 번째 실제 던전의 차이와 같습니다. 메커니즘은 같지만, 결과(consequence)의 집합은 완전히 다릅니다. 대부분의 '바이브 코딩(vibe-coded)'된 명세들은 튜토리얼 구역을 위해 작성되어 던전에 배치됩니다.

대부분의 빌더(builder)들은 실제 세상에 영향을 미치는 운영 도구에 할 일 앱 수준의 명세 규율을 적용합니다. "활성 잠재 고객(active prospects)에게 후속 조치를 취하라"는 명령은 '활성'이 정확히 무엇을 의미하는지, 어떤 연락처가 자발적 일시 중지 상태인지, 그리고 자동 이메일이 발송되는 것을 원치 않을 누군가가 개인적으로 관리하고 있는 연락처는 무엇인지에 대해 여지를 남겨둡니다. "제품 카탈로그를 파트너 피드(partner feed)와 동기화하라"는 현재 계약 검토 중인 참조 항목들에 대해 아무것도 말해주지 않습니다. "들어온 주문을 처리하라"는 이전 실행(run)에서 이미 처리된 주문을 어떻게 해야 하는지에 대해 에이전트에게 아무런 가이드를 주지 않습니다.

누락된 모든 제약 조건은 에이전트가 데이터가 허용하는 방식대로, 당신이 부여한 속도에 맞춰 임의로 해석하게 됩니다. 파트너 피드 규모에서, 아웃리치 규모에서, 혹은 도구가 실제 계정이나 실제 돈에 영향을 미치는 그 어떤 규모에서도, 명세에 공백을 남겨두는 것은 QA에서 잡아낼 수 있는 단순한 실수가 아닙니다. 그것은 데이터와 타이밍이 적절히 결합되기를 기다리는 운영 사고(production incident)입니다.

에이전트는 두 가지 모두 동일한 확신을 가지고 구축합니다. 도메인 복잡성 (Domain complexity)이 명세 복잡성 (spec complexity)으로 자동 전이되지는 않습니다. 당신이 직접 작성해야 합니다.

간극을 포착하기 위해 내가 만든 것

해당 사고 이후, 나는 새로운 모든 자동화 작업에서 빌더 (builder)와 에이전트 (agent) 사이에 **명세 정교화 레이어 (spec refinement layer)**를 두었습니다. 복잡한 것은 아닙니다. 코드의 첫 줄이 작성되기 전에 실행되는, Claude가 안내하는 대화 과정입니다. 이것은 명세를 작성하는 것이 아니라, 당신이 반드시 답해야 하는 질문들을 던집니다.

예를 들면 다음과 같은 질문들입니다: 어떤 상황에서도 이 에이전트가 건드려서는 안 되는 대상(사람 또는 사물)은 무엇인가? 당신이 어디에도 기록하지 않았지만 이 도메인을 지배하는 암묵적인 비즈니스 규칙 (business rules)은 무엇인가? 무엇이 "완료 (done)"로 간주되며, 무엇이 "시작 금지 (never start)"로 간주되는가? 경계 조건 (edges)에서는 어떤 일이 발생하는가: 만료된 계정, 분쟁 중인 연락처, 법무팀에서 플래그를 지정한 제품, 혹은 이전 실행에서 이미 처리된 주문 등 말입니다.

새로운 배포 자동화에 이를 처음 사용했을 때, 초기 입력값은 "파트너 피드와 제품 카탈로그를 동기화하라"였습니다. 도구가 생성한 첫 번째 질문은 이것이었습니다: "이 동기화에서 제외되어야 하는 제품 카테고리가 있습니까?" 나는 약 5초 동안 멍하니 앉아 있었습니다. 그러고 나서 4개월 전의 문서를 열어보았고, 계약 검토 중으로 플래그가 지정된 12개의 제품 참조를 찾아냈습니다. 나는 그것들을 명세에 넣어야겠다는 생각을 단 한 번도 해본 적이 없었습니다.

질문 뒤에 찾아오는 그 침묵 자체가 하나의 진단 도구입니다. 2초 이상의 망설임은 곧 제약 조건 (constraint)의 누락을 의미합니다. 이는 테스트 실행을 건너뛰고 "main으로 머지 (merge to main)"를 누를 때 느끼는 직감과 같습니다. 당신의 직관은 뇌보다 먼저 알고 있습니다.

가치는 결과물인 문서에 있는 것이 아닙니다. 에이전트가 실행되기 전 겪게 되는 10분간의 불편함에 있습니다. 다만 이것은 당신이 이미 어느 정도 인지하고 있는 것들만을 포착할 수 있다고 생각합니다. 만약 어떤 제약 조건이 당신의 머릿속에 전혀 스친 적이 없다면, 그 어떤 질문도 그것을 끌어낼 수 없습니다. 그것이 솔직한 한계입니다.

명세 작성은 기술이다. 프롬프팅 (Prompting)은 그 하위 집합일 뿐이다.

**프롬프트 엔지니어링 (Prompt engineering)**은 주어진 대화 내에서 모델로부터 더 나은 출력을 얻어내는 기술입니다. **명세 작성 (Specifying)**은 에이전트가 무엇을 할 수 있는지, 누구에게 접근할 수 있는지, 어떤 데이터에 작용할 수 있는지, 그리고 어디에서 멈춰야 하는지를 정의하는 기술입니다. 이들은 스택의 서로 다른 수준에서 작동하는 별개의 분야입니다.

깔끔한 프롬프트는 에이전트가 이미 가지고 있는 범위 내에서 더 나은 답변을 얻게 해줍니다. 신중한 명세는 그 범위가 무엇인지를 정의합니다. 느슨한 명세를 바탕으로 깔끔한 프롬프트를 작성하는 빌더는 빠르게 배포하지만 운영 환경(production)에서 문제를 일으킵니다. 신중하게 명세를 작성하는 빌더는 초기에 더 느리게 배포하지만 나중에 발생하는 장애(incident)로부터 더 잘 회복합니다. AI가 배포 일정을 몇 주에서 몇 시간 단위로 압축함에 따라, 이제 "나중에"라는 말은 바로 오늘 오후를 의미합니다.

단순히 대화 수준이 아니라 프로젝트 수준에서 운영하고 싶다면, 전체 프롬프트 계약 프레임워크 (the full prompt contracts framework)가 정확히 이 부분을 다룹니다. 즉, 에이전트가 무엇을 생성할 수 있는지, 무엇을 수정할 수 있는지, 무엇에 명시적인 인간의 승인이 필요한지를 다룹니다. 이것이 제가 이메일 사건과 같은 충분한 장애를 겪은 후 전환한 방식입니다.

Vibe Coding, For Real에서 제가 제시하는 접근 방식은 더 초기 단계를 다룹니다. 즉, 어떤 코드 생성(code generation)이 시작되기 전에, 단순히 기능 목록(feature list)만이 아니라 도메인의 경계 조건(boundary conditions)을 정의하는 것입니다. 시스템이 절대 해서는 안 되는 일은 무엇인지, 그리고 모두가 당연하다고 가정했기 때문에 한 번도 문서화되지 않았던 비즈니스 규칙은 무엇인지를 정의하는 것입니다.

에이전트는 당신이 무엇을 의도했는지에 대한 직관이 없습니다. 오직 당신이 무엇을 썼는지에 대해서만 알 뿐입니다.

Claude가 권한을 얻기 전 던져야 할 3가지 질문.

이메일 사건은 더 나은 모델이나 더 나은 프롬프트로 예방할 수 있었던 것이 아닙니다. 에이전트가 실행되기 전 10분간의 추가적인 명세 작업(spec work)이 있었다면 예방할 수 있었을 것입니다.

이제 저는 어떤 에이전트라도 라이브 데이터에 접근하기 전에 다음 3가지 질문을 던집니다:

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0