새로운 코드: 왜 명세(Specifications)가 프로그래밍을 대체할 것인가
요약
AI 에이전트 기반 개발에서 코드 자체보다 명세(Specification)의 중요성을 강조합니다. 코드는 명세로부터 생성되는 결과물일 뿐이며, 지속 가능한 개발을 위해 구조화된 프롬프트와 명세를 소스 코드로 관리하는 '명세 기반 파이프라인' 구축을 제안합니다.
핵심 포인트
- 코드는 명세(Spec)로부터 컴파일되는 결과물에 불과함
- 프롬프트와 명세를 버전 관리하는 것이 핵심 자산임
- 명세 기반 파이프라인(Spec-driven pipeline) 구축의 필요성
- 에이전트 활용 시 토큰 소모를 줄이기 위한 구조적 설계 중요
에이전트들은 제가 지시한 대로 정확히 수행하고 있었습니다. 그것이 문제였습니다.
저는 AI 에이전트가 명세(spec) 파일을 가져와서, 기능을 구현하고, 테스트를 실행하고, 결과를 검토한 뒤, 커밋(commit)까지 할 수 있는 파이프라인을 구축했습니다. 제가 코드 한 줄을 직접 작성하지 않고도 말이죠. 대부분 잘 작동했습니다. 수십 개의 기능이 배포되었습니다. 하지만 저는 계속해서 결과물을 검토하며 무언가 잘못되었다는 느낌을 받았습니다. 고장 난 것은 아니었습니다. 그저 이름을 붙이기 어려울 정도로 미묘하게 틀려 있었습니다.
한동안 저는 모델(models)을 탓했습니다. 그다음에는 프롬프트(prompts)를, 그다음에는 검증(validation) 단계를 탓했습니다. 결국 저는 명백한 사실을 직면해야 했습니다. 에이전트들은 제가 작성한 내용을 정확하게 구현하고 있었습니다. 제 명세(specs)가 불충분했던 것입니다. 병목 현상은 항상 계획 단계에 있는 저 자신에게 있었습니다.
대부분의 사람들이 버리는 것
바이브 코딩(vibe coding)에는 무언가 맞는 듯한 느낌이 있습니다. 당신은 의도(intent)의 수준에서 작동하며, 원하는 것을 설명하고 모델이 메커니즘을 처리하도록 맡깁니다. 그 부분은 진정으로 유용합니다.
하지만 대부분의 사람들이 결과물을 가지고 무엇을 하는지 살펴보십시오:
전통적인 개발:
소스 코드 (Source code) → 컴파일러 (Compiler) → 바이너리 (Binary)
(소스 코드를 유지하며, 언제든 바이너리를 재생성할 수 있음)
...
당신은 소스 코드를 갈기갈기 찢어버리고 바이너리만을 정성스럽게 버전 관리(version-controlled)하고 있습니다.
프롬프트(prompt) — 즉, 당신이 무엇을 원하는지, 왜 원하는지, 그리고 무엇이 "정확한" 것인지에 대한 구조화된 설명 — 가 바로 가치 있는 산출물(artifact)입니다. 생성된 코드는 그것으로부터 컴파일되는 결과물일 뿐입니다. 프롬프트를 버리고 출력물만을 커밋한다면, 당신은 실제로 중요한 것을 잃어버린 것입니다.
실질적인 결과는 6개월 후에 나타납니다. 당신은 자신이 작성한 코드를 뚫어지게 쳐다보며 자신의 의도를 역공학(reverse-engineering)하는 데 20분을 소비하게 됩니다. 명세(spec)가 있었다면 30초면 읽었을 내용인데 말이죠.
명세 기반 파이프라인(spec-driven pipeline)이란 무엇인가
저는 제가 SDLC (Software Development Lifecycle, 소프트웨어 개발 생명주기) 하네스(harness)라고 부르는 것을 구축했습니다. 코드를 직접 작성하는 대신, 무엇을 만들어야 하는지를 설명하는 명세(spec)를 작성하면 AI 에이전트가 구현, 테스트, 검토 및 문서화를 처리하는 시스템입니다.
명세(spec)가 소스(source)입니다. 코드는 그로부터 컴파일되는 결과물입니다.
단순한 아이디어입니다. 흥미로운 부분은 그 파이프라인(pipeline)을 어떻게 효율적으로 실행할지 찾아내는 것입니다. 저는 그 과정에서 값비싼 실수를 몇 번 저질렀습니다.
첫 번째 버전: 값비싼 비용
저의 원래 설계는 모든 태스크(task)를 구현(implement), 테스트(test), 검토(review), 문서화(document), 마무리(wrap-up)로 구성된 전체 파이프라인을 통해 독립된 환경에서 병렬로 각각 실행하는 방식이었습니다:
tasks.md
│
├── Task 1 (isolated) → implement → test → review → doc → wrap [~200k tokens]
...
이론상으로는 철저해 보였습니다. 하지만 실제로는 태스크당 약 200,000개의 토큰(tokens)이 소모되었습니다. 5개의 태스크로 구성된 명세(spec)는 통합된 결과물을 확인하기도 전에 100만 개의 토큰을 태워버렸습니다.
낭비는 구조적인 문제였습니다. 태스크 간에 변하지 않는 설정(setup) 단계가 N번 반복되었습니다. 태스크별 검토(per-task review)는 단 하나의 태스크 변경 사항만 볼 수 있었기에, 어차피 통합(integration) 문제는 놓칠 수밖에 없었습니다. 문서화(documentation) 역시 무언가가 조립되기 전에 고립된 상태로 실행되었습니다.
또한 태스크별 검토는 저에게 잘못된 확신을 주었습니다. 태스크 '내부'의 문제는 잡아냈지만, 통합된 결과가 실제로 작동하는지는 잡아낼 수 없었습니다. 저는 대부분 순차적(sequential)으로 진행되는 태스크들에 대해 고립(isolation) 비용을 지불하고 있었던 것입니다.
재설계한 내용
재설계는 더 단순한 질문에서 시작되었습니다: 무엇이 실제로 고립되어야 하며, 무엇을 공유할 수 있는가?
구현(implement) 단계는 태스크마다 새로운 컨텍스트(context)를 가질 때 이점이 있습니다. 즉, 관련 없는 변경 사항들 사이에 간섭이 발생하지 않습니다. 그 외의 모든 것—설정(setup), 테스트(testing), 검토(review), 문서화(documentation)—은 통합된 결과물에 대해 한 번만 실행하면 됩니다.
이를 통해 깔끔한 도구 계층(ladder of tools)이 만들어집니다:
/patch 사소한 수정, 테스트 불필요
↓
/sdlc-task 하나의 단위 → implement → fast-test → fix → commit
...
범위에 맞는 단계를 선택하십시오. 사소한 핫픽스(hotfix)는 검토(review) 단계가 필요하지 않습니다. 하지만 전체 기능 명세(feature spec)는 필요합니다. 병렬로 실행되는 여러 개의 독립적인 기능 블록(feature blocks)에는 오케스트레이터(orchestrator)가 필요합니다.
가장 큰 변화는 오케스트레이터에 있습니다. 기존 버전은 개별 태스크를 오케스트레이션했습니다. 재설계된 버전은 블록(block) 수준에서 작동합니다. 각 블록은 자신의 브랜치(branch)에서 자체적인 전체 파이프라인을 실행하는, 기능(feature) 크기의 완전한 작업 단위입니다.
master-plan.md
│
sdlc-block orchestrator
...
그리고 각 sdlc-flow 내부에서, 린 디자인(lean design)은 작업(task)당 하나의 새로운 구현 에이전트(implement agent)를 실행한 다음, 통합된 결과물에 대해 하나의 통합된 후반부(consolidated back-half)를 실행합니다:
공통 설정 (1회 실행)
│
작업 1 → 새로운 구현 에이전트
...
작업당 에이전트 격리(isolation)를 수행하며, 비용은 대략 한 번 더 실행하는 수준입니다.
하지만 파이프라인 재설계가 내 출력물을 고쳐주지는 못했다
더 빠르고 저렴해진 것은 맞습니다. 하지만 결과가 더 좋아지지는 않았습니다.
결과를 더 좋게 만든 것은 명세(spec)를 고치는 것이었습니다.
저는 계획(planning) 단계가 여전히 병목(bottleneck)인 상황에서 실행(execution) 단계만을 최적화하고 있었습니다. 에이전트들은 명세가 불충분한(underspecified) 작업을 읽고, 합리적인 가정을 내린 뒤, 기술적으로는 정확하지만 실제 의도는 놓친 결과물을 만들어내고 있었습니다. 파이프라인을 아무리 튜닝해도 이를 바꿀 수는 없었습니다.
더 나은 파이프라인으로 잘못된 명세를 고칠 수는 없습니다.
계획 산출물(planning artifact)은 실제로 어떻게 생겼는가
이 파이프라인으로 제가 했던 가장 흥미로운 일은, 이 파이프라인 자체를 재설계하는 데 사용한 것입니다.
코드 한 줄 건드리기 전에 제가 작성했던 마스터 플랜(master plan)의 요약본입니다. 목표는 네 가지 SDLC 엔진을 모두 재설계하는 것이었으며, 명세 기반(spec-driven) 파이프라인은 이를 수행하기 위해 사용한 도구였습니다:
# SDLC 엔진 재설계 — 마스터 플랜
## 목표
...
이 명세가 작동하는 방식에 대해 주목할 만한 몇 가지 사항이 있습니다.
목표(Goal) 섹션은 '무엇을 만들 것인가(what to build)'를 설명하기 전에 '왜 이런 일이 일어나는가(why this is happening)'를 포착합니다. "사용 데이터: 거의 아무도 실행하지 않음"과 같은 문구는 핵심적인 맥락(load-bearing context)입니다. 이것이 없다면, 에이전트는 실제로 아무도 사용하지 않는 동작에 대해 하위 호환성(backward compatibility)을 유지하려 할 수도 있습니다.
파일(Files) 필드는 설명이 아니라 약속(commitment)입니다. 에이전트는 정확히 어떤 파일을 수정해야 하고 어떤 파일을 그대로 두어야 하는지 알고 있습니다. 목록에 없는 모든 것은 기본적으로 범위 외(out of scope)입니다.
인터페이스 / 공유 접점(Interfaces / shared surface) 필드는 이 블록이 다음에 오는 블록들에게 무엇을 노출하는지를 에이전트에게 알려줍니다. 그것이 바로 다른 블록들이 의존하는 계약(contract)입니다. 구현 중간에 이를 변경하면 다운스트림(downstream) 작업이 깨지게 됩니다.
범위 외 (Out of scope) 필드는 대부분의 명세(specifications)가 완전히 생략하는 기능을 수행합니다. 바로 당신이 만들지 않을 것들을 명시하는 것입니다. 이것이 중요한 이유는, 도움을 주려는 에이전트(agent)가 명백히 관련되어 보이는 인접한 기능들을 구현하려 시도하는 경우가 많기 때문입니다. 명시적인 범위 외(out-of-scope) 항목은 그러한 이탈(drift)을 방지합니다.
그리고 수락 기준(acceptance criteria)은 차이점(diff)을 통해 검증 가능해야 합니다. "엔진이 더 잘 작동함"과 같은 모호한 내용이 아니라, "이 특정 심볼들이 제거됨, 이 파일이 커밋됨, node --check 통과"와 같이 구체적이어야 합니다. 이것들은 리뷰(review) 단계에서 실행될 정확한 체크리스트가 됩니다.
실질적인 테스트: 사전 맥락이 전혀 없는 똑똑한 엔지니어에게 이 명세를 전달했을 때, 그가 올바른 것을 만들어낼 수 있습니까? 만약 그렇지 않다면, 그 명세는 완성되지 않은 것이며, AI 에이전트 또한 똑같은 방식으로 실패할 것입니다.
생성 전의 계획 (Planning before generating)
현재 저에게 효과적인 흐름은 다음과 같습니다:
기능 아이디어 또는 요구사항
│
/plan 또는 /generate-master-plan
...
계획(planning) 단계는 어려운 질문들이 발생하는 지점입니다. '완료'란 실제로 어떤 모습인가, 실제 제약 조건은 무엇인가, 엣지 케이스(edge cases)는 어디인가와 같은 질문들 말입니다. 이러한 질문들은 구현(implementation)이 시작된 후보다 시작하기 전에 답하는 것이 비용이 훨씬 적게 듭니다.
계획 단계를 서두르면 리뷰 사이클(review cycles)에서 그 대가를 치르게 됩니다. 근본 원인은 거의 항상 명세가 불충분한 작업(underspecified task) 때문이며, 해결책은 항상 "더 나은 모델을 사용하는 것"이 아니라 "더 명확한 명세를 작성하는 것"입니다.
계획 단계에서 속도를 늦추면, 구현은 대개 첫 번째 패스(pass)에서 깔끔하게 완료됩니다.
희소한 기술 (The scarce skill)
희소한 기술은 코드를 작성하는 것이 아닙니다. 프롬프팅(prompting)도 아닙니다. 그것은 의도(intent)를 완전히 포착하는 명세를 작성하는 것입니다. 즉, 상태가 없는(stateless) 에이전트가 후속 질문을 던지지 않고도 올바른 결정을 내릴 수 있을 만큼 명확하게 작성하는 것입니다.
이것은 어렵습니다. 시작하기 전에 자신의 가정을 깊이 생각해야 합니다. "내가 무엇을 원하는지 막연하게 알고 있는 상태"와 "다른 사람이 실행에 옮길 수 있을 만큼 내가 원하는 것을 정밀하게 표현할 수 있는 상태"를 구분하는 능력이 필요합니다.
하지만 이는 전이됩니다. 훌륭한 에이전트 명세(Agent spec)를 만드는 것과 동일한 규율이 더 나은 아키텍처 결정 기록(Architecture decision record), 더 명확한 PR(Pull Request) 설명, 더 유용한 설계 문서(Design doc)를 만드는 밑거름이 됩니다. 상대방이 인간이든 AI든 관계없이, 당신을 더 나은 협업자로 만들어 줍니다.
도구(Tooling)는 빠르게 변하고 있습니다. 하지만 그 근저에 있는 기술은 변하지 않습니다.
명세(Spec)부터 시작하십시오.
이 내용이 유익했다면, 저는 learn-agentic-ai.com에서 프로덕션용 AI 및 에이전트 시스템(Agentic systems) 구축에 대해 글을 쓰고 있습니다. 여기에는 영어와 브라질 포르투갈어로 제공되는 실습 학습 경로가 포함되어 있습니다. 함께 실제적인 무언가를 만들어 봅시다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기