명세 주도 개발 (Spec Driven Development)
2026-04-18
- Opus 4.6 기반 (RAG, 생각의 나무 포함)
- 프롬프트 엔지니어링(압박 사이클, 헤겔의 변증법, 반례 제시법) 적용.
- Human In The Loop 검증 오라클 적용.
1. 초록
이 문서는 명세 주도 개발(Spec Driven Development) 을 LLM 기반 바이브 코딩이 쌓는 기술 부채의 구조적 해결책으로 제시한다. 출발점은 이론이 아니라 조직의 업무 환경이 아닌 개인 시간에 집에서 수행한 실험 경험이다. 소규모 바이브 코딩은 만족스러운 결과를 낸다. 그러나 규모가 커지면 LLM의 컨텍스트 윈도우 한계 때문에 오래된 결정이 체계적으로 소실된다. 개발자는 같은 결정을 반복 재입력하다가 노트를 만들고, 노트를 체계적 문서로 전환한다. 이 수렴 지점이 명세 주도 개발이다. 동일한 구조는 개인뿐 아니라 오픈소스 커뮤니티(GitHub spec-kit)와 대기업(Amazon Kiro) 수준에서도 동시에 형성되고 있다. 개인·커뮤니티·기업의 세 층위에서 제약과 자원이 완전히 다른 주체들이 같은 구조에 도달했다는 사실은, 이것이 우연한 선호가 아니라 LLM 기반 개발의 구조적 요구임을 시사한다.
평행 수렴의 두 층위. 산업에서 나타난 수렴은 두 층위로 구분된다. 명시적 SDD 프레임워크는 워크플로우 자체를 SDD로 설계한다 — GitHub spec-kit의 /specify → /plan → /tasks → /implement 체인, Amazon Kiro의 requirements → design → tasks 문서 흐름. 세션 디렉티브 레이어는 프로젝트 단위 영속 지시로 SDD의 일부만 구현한다 — CLAUDE.md, .cursor/rules/, .clinerules/, AGENTS.md 등. 후자는 본격적인 명세 코퍼스가 아니라 매 세션 주입되는 짧은 세션 프롬프트다. 둘을 혼동하면 “작은 지시 파일 하나로 전체 명세를 대체한다“는 오해가 생긴다.
바이브의 개인성과 다인 개발. 개인 개발에서 SDD의 필연성은 규모에서 나오지만, 다인 개발에서는 규모 이전에 바이브의 성질 자체에서 나온다. 바이브는 개인의 머릿속에서 LLM으로 흐르는 실시간·고대역 의도이며 본질적으로 개인적이다. 두 개발자의 바이브는 자동으로 병합되지 않는다. 팀이 협업하려면 바이브를 외재화해야 하며, 외재화된 바이브가 곧 명세다. 다인 환경에서 SDD는 검증이 필요한 가설이 아니라 첫 날부터 작동하는 구조적 강제다.
문서의 역할과 장소. 코드는 어떻게를 보여주지만 왜를 말하지 않는다. 테스트는 동작을 검증하지만 그 동작이 왜 필요한지는 담지 않는다. 문서가 담아야 하는 것은 세 가지다. 의도, 구조적 결정, 그리고 경계를 넘는 식별자. 이 문서들은 반드시 코드 파일 밖의 별도 파일로 분리되어야 한다 — LLM이 코드 내부 주석은 명시적 보존 지시가 없으면 제거하지만 별도 파일은 지시 없이도 보존하기 때문이다. 동시에 이 문서들은 같은 코드 저장소 안에 있어야 한다 — 명세 변경과 코드 변경이 같은 커밋 안에서 움직여야 드리프트가 감소하고, Git 이력이 “고고학적 발굴“을 방지한다. 완전한 위치 명세는 “같은 repo, 별도 파일“이며, 이것이 spec-as-code 원칙이다. 예외는 여러 애플리케이션이 공유하는 명세로, 이것은 별도 저장소로 분리되고 버전 태그로 참조되며, 파급 효과 때문에 무겁게 변경된다.
단방향 원칙과 부트스트랩. 명세 주도 개발의 핵심은 명세가 항상 선행하고 코드는 명세의 파생이라는 단일 방향성이다. 전통적 living specification 담론이 제안하는 양방향 동기화는 인간이 코드를 직접 편집하던 시대의 타협이었다. 100% 바이브 매개 개발에서는 양방향이 불필요하며, 오히려 양방향을 허용하는 순간 소스가 둘로 갈라져 반드시 어긋난다. 단 하나의 정당한 예외는 부트스트랩이다. 명세 없는 레거시 코드를 인수하는 경우, 코드로부터 명세를 역으로 추출하는 일회성 작업이 선행되어야 순방향 규율이 시작될 수 있다. 이것은 원칙의 위반이 아니라 원칙의 시작 조건이다.
경계 원칙과 granularity. 명세의 범위는 경계로 판단한다. 데이터 스키마, 통신 스펙, 파일명·앱 이름·테이블 이름, 공개 API 함수명, 정책, 상수 값 — 경계를 넘는 식별자는 명세에 담는다. 지역 변수명, 내부 헬퍼 함수명, 결과에 영향이 없는 구현 세부 구조 — 경계 안에 머무는 식별자는 LLM 재량에 맡긴다. 이 구분의 반증 규칙은 단순하다. 명세가 코드 수준의 식별자를 지정하기 시작하는 순간, 그 명세는 코드의 중복이며 존재 이유를 잃는다. 이 규칙이 과잉 상세화를 방지하고 명세 규모가 코드 규모에 비례해 팽창하지 않게 한다.
두 개의 지속 축. 명세 주도 개발은 두 축 위에서 지속된다. 첫 번째 축은 문서의 외부 파일화로, LLM이 코드 내부 주석을 기본 동작으로 제거하는 비대칭으로부터 맥락을 보호한다. 두 번째 축은 검토 과정의 도구화다. “빠진 결정 확인”, “오류·비약·일관성 검토”, “명세-코드 드리프트 점검” 같은 반복 지시를 스킬로 승격시키면 세션마다의 마찰이 사라진다. 마찰이 사라지면 검토는 의지의 문제에서 한 단어를 치는 문제로 바뀌고, 규율은 예외가 아니라 습관이 된다. 두 축이 함께 작동하는 한, 명세 유지가 개인 의지력에 의존하지 않고 습관으로 내려앉는다.
살아있는 명세. 명세는 고정되지 않는다. 개발 과정에서 계속 변경되며, 변경된 명세는 즉시 코드 재생성, 테스트, 배포 갱신을 요구한다. 워터폴과의 결정적 차이는 여기에 있다. 명세-코드 불일치가 감지되면 해결 방향은 하나다. 언제나 명세가 맞으며, 코드를 재생성한다. 코드를 명세의 소스로 쓰는 역방향은 (부트스트랩 예외를 제외하고) 허용되지 않는다. LLM에게 매 세션 시작 시 불일치 지점을 나열하도록 요구하는 것이 집행 메커니즘이며, 이것은 LLM 이전 시대에 없던 새로운 품질 보장 방식이다.
명세 코퍼스의 규모와 한계. 방법론은 성립하지만 집행은 미완이다. 중규모 시스템조차 명세 문서가 수십~수백에 이르며, 전체를 매 세션 컨텍스트에 주입할 수 없다. 긴 컨텍스트에서 LLM 주의가 저하되는 “lost in the middle” 현상 때문에 전부 주입한다고 전부 읽히는 것도 아니다. “어느 명세를 언제 주입하는가“의 자동화(RAG, 메타 명세, diff 파급 분석)는 2026년 4월 현재 미성숙하다. 실무는 인간이 관련 명세를 식별·편집하고 그 파일을 LLM에 명시 지정하는 수작업 큐레이션으로 운영된다. 인간이 선택 지능을, LLM이 생성 지능을 공급하는 분업이다. 이 한계를 은폐하는 SDD 담론은 실무자에게 도움이 되지 않는다.
바이브 코딩은 생산성의 도약이다. LLM에게 의도를 말하면 코드가 나온다. 동작한다. 배포된다. 문제는 그 다음이다.
바이브 코딩으로 생성된 코드베이스에서 맥락의 소실은 일반적인 코드베이스보다 훨씬 빠르고 완전하게 일어난다. 일반 코드베이스에서는 개발자의 머릿속에라도 맥락이 남는다. 바이브 코딩에서는 개발자조차 코드의 세부를 완전히 이해하지 못한 채 프로덕션에 밀어넣는 일이 발생한다. 코드가 왜 그렇게 생성됐는지, 어떤 판단이 있었는지, LLM이 어떤 프롬프트를 받았는지. 그 모든 것이 세션이 끝나는 순간 사라진다.
2. 경험이 도달한 지점
2.1 실험의 장소: 왜 조직이 아니라 집이었는가
먼저 이 방법론이 관찰된 조건을 명시한다. 이 연구는 조직의 업무 환경이 아니라 개인 시간에 집에서 수행한 실험의 결과다.
새로운 개발 방법론을 조직의 실제 프로세스에 적용하려면 팀 합의, 도구 도입, 검증 기간, 리뷰 문화의 조정이 필요하다. 이 비용은 한 명의 개발자가 주도할 수 있는 범위를 넘는다. 그래서 실험의 장소는 조직 바깥이 되었다. 퇴근 후의 시간, 주말, 그리고 개인 프로젝트가 이 방법론이 형성된 실제 환경이다.
이 조건은 방법론의 정당성을 약화시키지 않지만 일반화의 범위를 제한한다 — 한 경로에서는. 이 문서가 관찰한 규모 기반 수렴 경로(맥락 소실 → 즉흥 대응 실패 → 체계적 명세로 수렴)가 팀 환경에서 같은 형태로 재현되는지는 별도 검증이 필요한 가설이다. LLM 컨텍스트 윈도우의 구조적 제약에서 오는 것이므로 재현될 가능성은 높지만, 개인 경험에서 곧바로 일반화하기는 이르다.
그러나 다인 환경에는 이 규모 경로와 독립된 제2의 경로가 있다. 바이브가 본질적으로 개인적이므로, 다인 협업은 규모 이전에 이미 명세를 요구한다. 이 두 번째 경로는 가설이 아니라 바이브의 정의에서 직접 도출되며, 따라서 개인 경험의 일반화 한계에 의존하지 않는다. “바이브의 개인성과 다인 개발의 구조적 요구” 절에서 상세히 다룬다.
2.2 단계별 경험
이 방법론은 이론에서 유도된 것이 아니라 경험에서 도달한 것이다. 단계별로 정리하면 이렇다.
1단계. 소규모 바이브 코딩의 성공. 웹페이지를 바이브 코딩으로 만들었다. 만족스러웠다. 세션이 짧았고 맥락이 끝나기 전에 결과가 나왔다. 이 단계에서는 맥락 소실이 문제가 되지 않는다. 바이브 코딩이 가진 단기 속도의 장점이 단점을 압도한다.
2단계. 규모 증가와 맥락 소실. 서버-클라이언트 구조의 과제 관리 서비스를 개발했다. 규모가 커지자 세션이 길어졌고, LLM의 컨텍스트 윈도우가 한계에 도달했다. 앞쪽 토큰이 압축되고 새 맥락이 추가되는 사이클이 반복되자, 오래된 결정은 체계적으로 소실됐다. “왜 이 라이브러리를 골랐는지”, “어떤 예외 처리 규칙을 세웠는지” 같은 결정을 LLM에게 반복적으로 다시 입력해야 했다. 입력 노동이 개발 속도의 병목이 되기 시작했다.
3단계. 즉흥적 대응. 재입력할 맥락을 미리 노트에 적어두기 시작했다. 맥락이 소실될 때마다 노트를 복사해 다시 주입했다. 일시적 해결이었다. 그러나 노트가 증가하자 어느 노트를 언제 주입할지 결정하기 어려워졌다. 즉흥적 대응은 규모에서 무너졌다.
4단계. 체계적 전환. 노트를 주제별로 조직된 문서로 전환했다. 요구사항, 아키텍처, 데이터 모델, API 계약이 각각의 파일이 됐다. 이 과정에서 또 하나의 발견이 있었다. 인간이 빈 문서 앞에 앉아 처음부터 쓰는 것이 아니라, LLM과의 대화에서 AI가 초안을 쓰고 인간이 다듬는 방식이 훨씬 효율적이라는 점이었다. 문서 작성의 마찰이 낮아졌다.
5단계. 개인적 명명. 이 방식을 Document Driven Vibe Development라고 스스로 불렀다.
6단계. 커뮤니티와 기업의 평행 수렴 확인. 이후 동일한 구조가 여러 경로에서 동시에 형성되고 있음을 알았다.
- 오픈소스 커뮤니티: GitHub의 spec-kit은 명세 → 계획 → 태스크 분해 → 구현의 단계적 워크플로우를 오픈소스 프레임워크로 공식화했다. 자발적 수렴이다.
- 대기업: Amazon의 Kiro는 AI IDE 수준에서 requirements → design → tasks의 문서 흐름을 제품의 핵심 개념으로 채택했다. 개인의 손장난이 아니라 엔지니어링 조직의 제품 결정이다.
- AI 코딩 도구 일반: Anthropic의 Claude Code는
CLAUDE.md를 프로젝트 영속 지시로 표준화했고, Cursor는 rules 파일로 동일한 기능을 제공한다. 이들은 명세가 아니라 세션 단위로 주입되는 짧은 디렉티브이지만, “세션을 넘어 보존되는 맥락“이라는 점에서 같은 방향의 부분적 수렴이다.
이 평행 수렴은 개인, 커뮤니티, 기업의 세 층위에서 동시에 일어났다. 세 층위는 제약과 자원이 완전히 다르다. 개인은 자유롭지만 규모가 작고, 커뮤니티는 합의가 느리지만 다양성이 크며, 기업은 자원이 많지만 움직임이 무겁다. 서로 다른 제약 아래에서 같은 구조에 도달했다는 사실은, 이것이 우연한 선호가 아니라 LLM 기반 개발의 구조적 요구임을 강하게 시사한다. 각 도구가 구체적으로 어떤 워크플로우와 산출물 구조를 가지는지는 뒤의 “평행 수렴의 지형” 절에서 상세히 다룬다.
이 여정을 단순화하면 세 개의 전환점이 있다. 맥락 소실을 겪은 순간(1→2), 즉흥 대응을 체계로 전환하기로 결정한 순간(3→4), 개인적 발견이 다층 평행 수렴이었음을 확인한 순간(5→6). 세 전환점은 규모가 일정 이상으로 커지면 누구나 겪는 구조적 지점이다. 맥락 소실은 규모에서 불가피하고, 즉흥 대응은 반드시 한계에 도달하며, 그 해결은 같은 구조로 수렴한다.
맥락 소실은 추상적 우려가 아니라 실제로 겪는 사이클이다. 2단계에서 겪은 것을 단순화하면 이렇다. 세션 초반에 “결제 실패는 재시도 없이 데드레터 큐로 보내기로 결정“이라고 LLM에게 말한다. 수십 회의 왕복이 지난 후, 같은 세션에서 결제 관련 새 기능을 요청한다. LLM은 재시도 로직을 포함한 코드를 제시한다. 앞 결정이 압축 과정에서 사라진 것이다. 이 현상이 반복되면 개발자는 “중요한 결정은 매번 다시 말해야 한다“는 것을 깨닫고, 결국 “매번 말하느니 적어두자“로 이동한다. 명세 주도 개발은 이 이동의 끝에 있다.
3. 평행 수렴의 지형
앞 절의 6단계에서 개인·커뮤니티·기업의 평행 수렴을 언급했다. 이 절은 각 층위에서 무엇이 구체적으로 형성되고 있는지를 본다. 두 범주로 나뉜다. 워크플로우 자체를 SDD로 설계한 명시적 SDD 프레임워크와, 프로젝트 단위 영속 지시로 SDD의 일부를 구현하는 세션 디렉티브 레이어. 이 절의 기술적 세부는 2026년 4월 기준이며, 이 분야는 빠르게 변하므로 구체 구현은 향후 달라질 수 있다.
3.1 명시적 SDD 프레임워크
GitHub spec-kit. GitHub가 공식 배포하는 오픈소스 툴킷이다. 워크플로우는 /constitution → /specify → /plan → /tasks → /implement의 슬래시 명령 체인으로 구성된다. 자연어 기능 설명을 받아 구조화된 명세, 기술 계획, 단위 태스크로 단계적으로 분해하며, 각 단계는 템플릿으로 형식이 고정되어 있다. 도구 비종속을 지향해 GitHub Copilot, Claude Code, Gemini CLI, Cursor, Windsurf 등 여러 에이전트에서 동작한다. 특징은 명세·계획·태스크가 레포지토리 안의 마크다운 파일로 산출되어 Git 이력에 편입된다는 점이다. 이 문서의 “spec-as-code“와 정확히 일치하는 설계 선택이다.
Amazon Kiro. AWS가 배포하는 에이전트형 AI IDE다. Bedrock 위에서 Claude Sonnet으로 구동된다. 워크플로우는 세 문서의 체인으로 고정돼 있다. requirements.md(자연어 요구를 EARS 표기의 수용 기준으로 변환) → design.md(코드베이스 분석을 바탕으로 아키텍처·시스템 설계·기술 스택) → tasks.md(의존성 기반으로 순서화된 구현 태스크, 선택적 테스트 포함). Kiro의 명시적 전제는 “명세가 소스, 코드는 빌드 산출물“이다. 추가로 hooks 메커니즘으로 파일 저장·PR 오픈 등 이벤트에 테스트 실행, 문서 업데이트, 명세 변경의 하위 전파를 자동화한다. 이 문서의 단방향 원칙과 살아있는 명세의 집행 메커니즘이 IDE 수준에서 제품화된 사례다.
두 프레임워크는 동일한 구조로 수렴한다. 기능 설명 → 요구사항 → 설계 → 태스크 → 구현의 단계적 분해, 문서가 Git에서 관리되는 spec-as-code, 명세가 단일 진실 원천이라는 원칙. 오픈소스 프레임워크와 대기업 제품이 서로 독립적으로 같은 지점에 도달했다.
3.2 세션 디렉티브 레이어
명시적 SDD 워크플로우까지 가지 않더라도, 대부분의 AI 코딩 도구는 프로젝트 단위로 지속되는 짧은 지시 파일을 표준 기능으로 제공한다. 이 범주를 ’명세’로 부르는 것은 정확하지 않다. 이들은 본격적인 명세 코퍼스(수십~수백 문서)가 아니라 매 세션 시작 시 주입되는 세션 프롬프트 — 프로젝트별 상시 시스템 지시문에 가깝다. 혼동하면 “작은 CLAUDE.md 하나로 전체 명세를 대체할 수 있다“는 오해가 생긴다. 다만 세션이 끝나도 보존되는 맥락이라는 점에서 SDD와 같은 방향의 부분적 수렴이다.
- Anthropic Claude Code —
CLAUDE.md: 계층적 로드가 핵심 특징이다.~/.claude/CLAUDE.md(전역 선호), 프로젝트 루트의CLAUDE.md(범용 규칙), 서브디렉토리의CLAUDE.md(모듈별 규칙)가 결합된다. 서브디렉토리 파일은 해당 폴더에서 작업할 때만 로드되어 토큰을 절약한다. Skills, Subagents, Hooks, MCP 등 보조 메커니즘과 계층을 이룬다. - Cursor —
.cursor/rules/: MDC(Markdown + metadata) 파일 단위 규칙. 과거.cursorrules단일 파일은 지원되지만 deprecation 경로다. 각 규칙 파일은 적용 범위(파일 패턴, 자동/수동 적용 등)를 메타데이터로 지정할 수 있다. - Cline —
.clinerules/와 Memory Bank:.clinerules/는 다른 도구들과 동일한 세션 디렉티브다. 단, Cline이 공식화한 Memory Bank는 여러 마크다운 파일(productContext, activeContext, progress 등)로 프로젝트 지식을 구조화하는 문서 방법론으로, 단일 지시 파일이 아니라 다중 파일 맥락 구조다. 이 점에서 Memory Bank는 세션 디렉티브와 명세 코퍼스 사이의 혼합 형태다. 다만 수백 문서 규모에서는 Memory Bank도 같은 scale 한계를 공유한다 — 이 한계는 뒤의 “명세 코퍼스의 규모와 선택적 주입” 절에서 다룬다. - Continue.dev —
.continue/rules/: Markdown 또는 YAML 규칙. Chat, Edit, Agent 세 모드별로 적용 가능. 다중 모델 백엔드를 전제로 설계되었다. - Aider —
CONVENTIONS.md: 코딩 컨벤션을 단일 파일에 기술. 구조는 단순하지만 용도가 명확하다. - GitHub Copilot —
.github/copilot-instructions.md: Copilot Chat/Agent가 참조하는 저장소 단위 지시.
AGENTS.md 통일 표준 시도. 도구별로 파편화된 규칙 파일 포맷을 통합하려는 커뮤니티 이니셔티브다. 프로젝트 루트 단일 AGENTS.md 하나로 여러 AI 도구가 같은 지시를 공유하도록 한다. Cursor는 AGENTS.md의 중첩 서브디렉토리 지원을 이미 추가했고, 다른 도구들도 수용하는 추세다. 이 수렴 자체가 바이브의 외재화가 도구 간 경계마저 넘는 공유 자산이 되고 있음을 보여준다.
3.3 수렴과 경계 원칙의 간극
두 범주를 가로질러 관찰되는 공통 패턴은 세 가지다. 첫째, 지시는 레포지토리 안의 파일이다. 외부 시스템(이슈 트래커, 위키)이 아니라 코드와 함께 버전 관리된다. 둘째, 계층적 스코프를 가진다. 전역/프로젝트/서브디렉토리 구조가 반복적으로 채택된다. 셋째, 명세의 형식화가 강해진다. Kiro의 EARS 표기, spec-kit의 템플릿처럼, 자연어 지시에 구조를 부여하는 방향으로 진화한다.
다만 이 문서의 경계 원칙과 비교하면 한 가지 간극이 있다. 현재 대부분의 세션 디렉티브 레이어는 “경계를 넘는 식별자“와 “경계 안의 구현 세부“를 구분하지 않는다. 규칙 파일에 변수명, 함수명, 내부 구조까지 무분별하게 담는 관행이 업계에서 형성되고 있으며, 이는 명세 팽창과 코드 중복을 낳는다. 경계 원칙은 기존 도구들이 아직 명시화하지 못한 층위이며, 과잉 상세화를 피하는 규율은 도구가 아니라 사용자가 공급해야 한다.
4. 바이브의 개인성과 다인 개발의 구조적 요구
개인 개발에서 SDD의 필연성은 규모에서 나온다. 세션이 길어지고 컨텍스트 윈도우가 한계에 도달할 때 비로소 명세가 필요해진다. 문서의 1→4단계 전환이 이 경로다. 그러나 다인 개발에서 SDD의 필연성은 규모 이전에 바이브의 성질 자체에서 나온다.
바이브는 세션 안에서 개발자의 머릿속에서 LLM으로 흐르는 실시간·고대역 의도 전달이다. 그 안에는 명시적 요구뿐 아니라 개발자의 암묵지, 판단 습관, 미언급 전제가 섞여 있다. 출처는 한 사람의 머리다. 바이브는 본질적으로 개인적이다.
두 개발자가 같은 요구사항을 받고 각자 바이브 코딩으로 작업하면 다른 코드가 나온다. 머릿속 맥락이 다르기 때문이다. 두 결과물이 같은 코드베이스에 모이면 일관성이 깨지고, 다음 세션의 LLM은 어느 쪽을 기준으로 삼을지 추론해야 한다. 이 추론은 확률적이며 규모가 커지면 반드시 어긋난다.
이 논증은 “두 사람“에만 국한되지 않는다. 같은 사람이라도 오늘의 세션과 3개월 뒤의 세션은 서로 다른 바이브를 가진다. 기억은 흐려지고, 그 사이에 내린 다른 결정들이 정신을 덮고, 관심사의 초점이 이동한다. 3개월 뒤의 자신은 지금의 자신에게 사실상 “다른 개발자“다. 따라서 바이브 개인성의 요구는 다인 협업뿐 아니라 개인 내부의 시간 축에도 작용한다. 외재화가 없으면 오늘의 결정은 내일의 자신에게도 이미 이해 불가능한 과거가 된다.
바이브를 공유하려면 외재화해야 한다. 대화로, 메모로, 문서로. 외재화된 바이브가 명세다. 컨벤션, 아키텍처 원칙, 코딩 가이드, 프로젝트 룰 — 이름은 달라도 같은 것이다. (CLAUDE.md 같은 세션 디렉티브 파일은 바이브 외재화의 또 다른 형태이지만 명세는 아니다 — 세션 프롬프트와 명세는 별개 범주다.) 따라서 다인 환경에서 “명세 없는 협업“은 성립하지 않는다. 명세가 없다고 느껴지는 팀은 실제로는 암묵적 명세(공유 문화, 장기 협업에서 형성된 공유 멘탈 모델)에 의존한다. 그 암묵적 명세는 인원이 바뀌거나 팀이 커지는 순간 깨진다.
이 관점은 앞서 “실험의 장소” 절에서 제기한 일반화 한계를 뒤집는다. 팀 환경에서 SDD가 유효한지는 검증이 필요한 가설이 아니다. 다인 개발은 규모에 의한 맥락 소실을 경험하기 이전에, 바이브의 개인성에 의해 이미 명세를 강제받는다. 개인 개발자가 규모에서 SDD에 도달한다면, 팀은 작업이 누적되는 첫 지점부터 외재화된 명세가 있어야 일관성이 유지된다. 다인 환경에서 SDD는 더 일찍, 더 강하게 필요해진다.
5. 문서가 채우는 레이어
코드는 어떻게 구현됐는지는 보여주지만, 왜 그렇게 됐는지는 말해주지 않는다. 왜 이 라이브러리를 골랐는지, 왜 이 구조를 선택했는지, 왜 이 프롬프트를 사용했는지. 바이브 코딩에서 이 이유들은 개발자의 머릿속에도 없는 경우가 많다. LLM이 생성했고, 동작했고, 그게 전부다.
TDD와 타입 시스템, 잘 작성된 테스트는 실재하는 가치를 가진다. assert response_time < 200ms는 자연어보다 훨씬 정밀한 명세다. 코드는 동작의 정확성을 검증하는 데 있어 문서보다 우월하다. 다만 그 테스트가 왜 200ms라는 기준을 사용하는지, 이 기능이 왜 존재하는지, 왜 이 구조적 결정을 내렸는지는 테스트 코드 어디에도 없다. 코드와 문서는 경쟁하지 않는다. 서로 다른 레이어를 채운다.
문서가 담아야 하는 것은 세 가지다. 의도, 구조적 결정, 그리고 경계를 넘는 식별자. 동작의 세부와 경계 안의 식별자는 코드에 있다. 지도는 지형의 모든 돌멩이를 표시하지 않아도 유용하다. 그리고 지도가 틀렸을 때 따라야 하는 것은 지형이 아니다. 명세 주도 개발에서 지도는 지형을 생성하는 원본이다.
6. 왜 문서는 코드 파일 밖에 있어야 하는가
코드 안에 의도를 남기는 방법은 자연스럽게 떠오른다. 주석을 “무엇“이 아니라 “왜“로 쓰거나, ADR을 코드 저장소 안에 넣거나. 이 방법들은 전통적인 코드베이스에서는 유효하다.
바이브 코딩에서는 다르다. LLM에게 “주석을 유지하라“고 명시적으로 지시하지 않으면, 코드 파일을 재생성·수정할 때 LLM은 주석을 노이즈로 취급해 제거한다. 지시하면 보존되지만, 바이브 코딩의 빠른 왕복에서 그 지시는 자주 누락된다. 한 번 누락되면 그 세션에서 생성된 파일의 주석이 사라지고, 누락이 반복되면 주석이 세션마다 조금씩 깎여나간다. 이것은 인간의 무관심이 아니라 기본 동작이 제거이고 보존은 명시적 지시에 의존한다는 비대칭이다.
경험적으로 확인되는 패턴이 있다. 별도 문서 파일의 내용은 이런 지시 없이도 대체로 보존된다. 기본 동작이 반대 방향인 것이다 — 코드 내부 주석은 제거가 기본이고 보존이 예외이며, 별도 문서는 보존이 기본이고 제거가 예외다. 문서를 별도 파일로 분리하는 것은 스타일의 문제가 아니라, 매번 보존 지시를 기억하지 않아도 맥락이 유지되는 구조를 선택하는 것이다.
7. 왜 명세는 같은 코드 저장소 안에 있어야 하는가
파일 분리와 저장소 공존은 다른 차원의 요구다. 파일 분리는 LLM의 주석 제거 기본 동작으로부터 맥락을 보호한다. 저장소 공존은 명세와 코드가 같은 변경 단위 안에서 진화하도록 보장한다. 두 요구가 합쳐진 완전한 위치 명세는 “같은 repo, 별도 파일“이다.
저장소 공존의 근거는 세 가지다.
첫째, 고고학적 발굴의 방지. 명세가 Jira, Linear, Slack, Notion, Confluence 같은 별도 시스템에 있을 때, 3년 전 결정을 찾는 것은 여러 시스템을 가로지르며 닫힌 티켓, 만료된 채널, 삭제된 페이지를 뒤지는 작업이 된다. 각 시스템은 다른 검색 언어, 다른 권한 모델, 다른 보존 정책을 갖는다. Git은 영구 보존되고 diff 기반으로 “무엇이 왜 바뀌었는지“까지 함께 보존한다.
둘째, 공동 변경 단위. 명세 변경과 코드 변경이 같은 커밋·같은 PR 안에서 움직이면, 리뷰어는 “이 명세 변경이 어떤 코드 변경을 수반했는가“를 한눈에 본다. 두 아티팩트의 이력이 하나로 묶이면 드리프트가 구조적으로 감소한다. 명세가 별도 시스템에 있으면 두 이력은 어긋날 자유를 얻고, 자유는 반드시 사용된다.
셋째, Git의 고유 속성. 브랜치로 명세 변경을 실험하고, PR로 명세를 리뷰하고, bisect로 특정 결정이 언제 들어왔는지 추적할 수 있다. 위키나 이슈 시스템은 이 속성을 가지지 못한다.
일반화된 원칙은 spec-as-code다. 명세는 코드와 같은 버전 관리 시스템, 같은 리뷰 프로세스, 같은 보존 정책 아래 있어야 한다.
7.1 예외: 여러 애플리케이션이 공유하는 명세
다른 애플리케이션과 공유하는 명세는 별도의 저장소로 관리되고 참조되어야 한다. 이것이 spec-as-code 원칙의 정당한 예외다.
서비스 A와 서비스 B 사이의 API 계약, 여러 서비스가 소비하는 이벤트 스키마, 공통 도메인 모델, 여러 팀이 의존하는 OpenAPI/Protobuf 정의 — 이런 명세를 어느 한 애플리케이션의 repo에 두면 비대칭이 생긴다. 명세를 담은 repo는 “주인“이 되고 나머지는 “소비자“가 된다. 그러나 공유 명세는 계약이지 한 쪽의 소유물이 아니다. 별도 repo로 분리하면 모든 소비자가 대등하게 참조하는 공유 인프라가 된다.
참조 방식은 여러 가지다. 버전 태그로 고정된 Git 서브모듈, 스키마 레지스트리, 패키지 의존성(npm, Maven, pip). 어떤 방식이든 핵심은 소비자가 특정 버전의 공유 명세에 결정론적으로 고정된다는 점이다. 명시 버전 태그를 쓰든 lockfile로 변동을 통제하든, 소비자 쪽에서 어떤 버전을 소비하는지 재현 가능해야 한다. 결정론적으로 재현되지 않는 참조 — 예를 들어 lockfile 없이 최신 main을 무조건 따라가는 방식 — 은 사실상 공유 가변 상태이며, SDD의 단일 소스 원칙과 충돌한다.
공유 명세는 무겁게 변경된다. 한 번의 변경이 모든 소비자에게 파급되기 때문이다. 애플리케이션 고유 명세가 각 기능 개발과 함께 가볍게 바뀌는 것과 달리, 공유 명세의 변경은 호환성 검토, 버전 정책, 마이그레이션 경로, 폐기(deprecation) 주기를 요구한다. Semantic versioning과 다운스트림 소비자의 업그레이드 일정이 명세 변경의 부가 속성이 아니라 변경 프로세스 자체의 일부가 된다.
저장소 구조의 기본 규칙은 이렇게 정리된다.
- 애플리케이션 고유 명세: 해당 애플리케이션의 repo 안. 코드와 함께, 가볍게 변경.
- 공유 명세: 별도 repo. 버전 태그로 참조. 무겁게 변경.
두 경로 모두 명세가 버전 관리 시스템 안에 있다는 원칙은 유지된다. 저장소가 같으냐 다르냐만 달라진다.
8. 명세 주도 개발
명세 주도 개발의 핵심은 단순하다. 명세가 항상 선행하고, 코드는 명세의 파생이다. 이 규율은 100% 바이브 매개 개발이라는 조건에서 가능해진다. 개발자가 코드를 직접 타이핑하지 않고 모든 변경을 LLM에게 지시한다면, “LLM에게 지시하는 내용“이 이미 작은 명세다. 그 지시를 세션과 함께 사라지게 두는 대신 파일로 승격시키는 것이 명세 주도 개발이다.
워크플로우는 이렇다. 무엇을 만들고 싶은지 LLM에게 말한다. LLM은 코드 대신 명세 문서를 먼저 작성한다. 왜 이것을 만드는지, 어떤 구조적 결정을 내렸는지, 어떤 트레이드오프를 선택했는지. 개발자는 그 문서를 검토하고 수정한다. 그리고 LLM은 그 문서를 기반으로 코드를 생성한다.
명세는 한 종류가 아니다. 프로젝트 규모에 따라 여러 문서가 체인을 이룬다.
- 사용자/고객 인터뷰 기록 — 요구의 출처
- RFP 및 요구사항 명세 — 무엇을 만들 것인가
- 아키텍처 선언문 — 어떤 구조로 만들 것인가
- 코딩 컨벤션 — 코드 생성의 규칙 (정책 층위)
- 테스트 계획 — 무엇이 검증되어야 하는가
- 배포 계획 — 어떻게 전달되는가
이 문서들은 모두 LLM의 입력이다. 과거에는 인간만이 읽었고 빠르게 방치됐다. 이제는 매 세션 LLM이 소비하고 재활용한다. “죽은 중량“이었던 문서가 “생산적 입력“으로 전환된 것이 LLM 이전과 이후의 근본적 차이다.
8.1 단방향 원칙: 코드에서 명세로의 역방향은 없다
명세 주도 개발은 단일 진실 원천(SSOT)을 명세에 둔다. 코드가 명세를 갱신하는 방향은 존재하지 않는다. 이유는 간단하다.
100% 바이브 매개라는 전제가 유지되면, 인간은 코드를 직접 타이핑하지 않는다. 따라서 “코딩 중 발견“은 코드 편집 행위에서 오지 않는다. LLM이 생성한 결과를 보고 의도를 재고하는 순간이 올 수는 있다. 그러나 그 재고는 명세를 수정하고 재생성하는 방식으로만 반영된다. 코드를 직접 고치는 순간 단방향 규율은 깨진다.
전통적 living specification 담론은 “문서와 코드를 양방향으로 동기화하라“고 말한다. 이것은 인간이 코드를 직접 편집하던 시대의 타협이었다. LLM 매개 개발에서는 양방향이 필요 없다. 명세 하나가 소스이고 코드는 매번 새로 파생된다.
이 원칙을 지키기 위해서는 “100% 바이브 매개“가 실제로 100%에 가까워야 한다. 오타 수정 한 글자, 디버그 print 문 임시 삽입, 머지 충돌 해결 — 이런 미세 편집이 누적되면 규율이 새기 시작한다. 예외를 허용하려면 그 예외가 무엇이고 어떻게 명세에 반영되는지 명시적 규칙이 먼저 있어야 한다.
8.2 부트스트랩: 인수 상황에서의 역방향 생성
단방향 원칙이 절대적이라면 한 가지 현실적 상황에서 막힌다. 이미 바이브 코딩으로 생성되었고 명세가 없는 코드베이스를 인수하는 경우다. 전임 개발자가 떠났거나, 외주 결과물을 받았거나, 혼자 오래전에 만들어두고 맥락을 잊은 코드를 다시 열었을 때. 더 까다로운 경우는 여러 개발자가 각자 다른 바이브로 작성한 레거시를 팀 전체가 인수할 때다 — 코드에 혼재된 바이브 때문에 추출된 명세 초안이 내부 모순을 품는다. 이런 상황에서 명세 주도 개발을 시작하려면 출발점이 필요하다.
그 출발점을 만드는 유일한 방법은 코드로부터 명세를 역으로 추출하는 것이다. LLM에게 코드베이스 전체를 주고 “데이터 스키마, 공개 API, 아키텍처 결정을 추출해 명세 초안으로 정리하라“고 지시한다. 초안은 불완전하다. 원작자의 의도에 대한 추측이 섞여 있다. 인수자는 그 초안을 검토하고, 확인 가능한 범위 내에서 추측을 확정하고, 잘못된 추측을 수정한다.
여러 개발자의 레거시에서는 이 단계가 더 복잡하다. 한 모듈은 재시도를 기본으로 하고 다른 모듈은 빠른 실패를 택하는 식으로, 추출된 명세 초안에 상충하는 결정이 그대로 남는다. 인수자는 각 충돌에서 어느 쪽을 프로젝트 정책으로 채택할지 결정하고 나머지 쪽의 코드를 재생성해야 한다. 이 화해(reconcile) 작업이 생략되면 부트스트랩이 끝난 뒤에도 명세 내부에 상충이 남아 단방향 원칙이 작동하지 않는다.
이것이 단방향 원칙을 깨는 것처럼 보이지만 그렇지 않다. 역방향 생성은 순방향을 가능하게 만드는 부트스트랩 작업이다. 단방향 원칙이 금지하는 것은 “지속적 개발에서 코드가 명세의 소스로 기능하는 것“이다. 인수 시의 역방향은 소스가 없는 상태에서 소스를 창조하는 일회성 전환이다. 전환이 완료된 순간부터 명세가 소스가 되고 코드는 파생이 된다. 오히려 이 역방향이 선행되지 않으면 순방향 규율 자체가 시작될 수 없다.
따라서 규칙은 이렇게 정리된다. 명세 없는 레거시를 만났을 때의 첫 작업은 명세 역생성이다. 이 작업이 끝나기 전까지 새 기능을 추가하지 않는다. 추가하는 순간 명세와 코드의 격차가 커져 역생성이 점점 더 어려워진다. 부트스트랩이 끝난 후부터는 일반적인 단방향 규칙이 적용된다.
9. 무엇을 지정하고 무엇을 지정하지 않는가
명세 작성의 가장 흔한 실수는 과잉 상세화다. 변수명, 함수명, 내부 헬퍼의 구조까지 명세에 담기 시작하면 명세는 코드의 중복이 된다. 중복된 명세는 존재할 이유가 없다. 그 경우 바이브 코딩이 더 빠르다.
이것은 운영적 반증 규칙이기도 하다. 명세가 코드 수준의 식별자를 지정하기 시작하는 순간, 그 명세는 쓸 필요가 없어진다. 명세가 존재할 이유는 오직 코드가 말할 수 없는 층위에서만 나온다.
기준은 경계다. 경계를 넘는 식별자는 명세에, 경계 안에 머무는 식별자는 AI 재량에 맡긴다.
명세가 지정하는 것 (경계를 넘는 식별자):
- 데이터 스키마, 테이블 이름, 컬럼 이름
- 통신 스펙, API 엔드포인트, 메시지 포맷
- 파일명, 앱 이름, 서비스 이름
- 공개 API 함수/클래스 이름 (다른 모듈·서비스가 호출하는 것)
- 정책 (명명 규칙, 에러 처리 규칙, 로깅 규칙)
- 상수 값 (재시도 횟수, 타임아웃, 임계치)
명세가 지정하지 않는 것 (경계 안에 머무는 식별자):
- 지역 변수명
- 내부 헬퍼 함수명
- 구현 세부 구조 (결과에 영향이 없는 패턴 선택 등)
이 구분의 근거는 인터페이스는 계약이고 구현은 교체 가능하다는 원리다. 경계를 넘는 식별자는 외부에서 참조되므로 바뀌면 외부가 깨진다. 경계 안의 식별자는 LLM이 일관되게 리네임할 수 있고, 외부에 영향이 없다.
정책과 인스턴스의 구분도 중요하다. “함수는 snake_case를 쓰라“는 정책이고, “이 함수를 calculate_tax로 이름 지으라“는 인스턴스다. 정책은 명세에, 인스턴스는 LLM 재량에. 이 구분을 놓치면 “컨벤션을 명세화한다“가 “변수명을 명세화한다“로 미끄러진다.
10. 속도가 아니라 맥락의 문제
명세 주도 개발을 “단기 속도를 반납해 장기 속도를 얻는 거래“로 보는 흔한 프레이밍이 있다. 이 프레이밍은 본질을 놓친다. 명세는 빠르게 개발하려는 도구가 아니다. 맥락을 잃지 않으려는 도구다.
맥락이 없는 코드베이스에서 LLM에게 작업을 지시하려면 개발자는 매번 결정과 배경을 다시 말해야 한다. “왜 이 라이브러리를 골랐는지”, “어떤 예외 처리 규칙을 세웠는지”, “결제 실패는 재시도 없이 데드레터 큐로 보내기로 했는지”. 이 반복된 타이핑은 단순 노동이고, 단순 노동의 반복은 개발을 지루하고 지치게 만든다. 피로는 누락을 부른다. 누락된 결정은 LLM에 의해 추론으로 채워지고, 추론은 확률적으로 틀린다. 이 사이클이 바이브 코딩에서 부채가 누적되는 실제 경로다.
명세는 이 사이클을 끊는다. 한 번 써두면 다음 세션의 LLM이 그 문서를 읽고 시작한다. 개발자는 매번 같은 결정을 다시 말하지 않아도 된다. “한 번 쓰고 여러 세션에서 재사용하는 맥락” — 이것이 명세의 기능적 정체성이다.
속도는 부산물이지 목적이 아니다. 명세가 자리 잡으면 부산물로 속도가 따라올 수도 있고, 특정 단위 과제에서는 명세를 쓰는 시간이 바이브로 타이핑하는 시간보다 길 수도 있다. 그러나 개발자가 피로에 의해 누락하거나 포기하지 않고 일관된 품질로 계속 작업할 수 있느냐 — 이것이 명세 주도 개발이 지키는 실제 가치다.
명세 작성의 비용이 낮다는 점이 이 논리를 보완한다. 인간이 처음부터 끝까지 쓰지 않는다 — AI가 초안을 만들고 인간이 다듬는다. 경계 원칙이 과잉 상세화를 막아 규모가 코드에 비례해 팽창하지도 않는다. 맥락 유지의 이익은 크고, 그 비용은 생각보다 작다.
11. AI와 명세 작성의 분업
LLM은 개발자의 말을 듣고 명세 초안을 만든다. 대화에서 오간 내용, 결정의 이유, 시도했다 버린 접근을 정리한다. 인간은 그 초안을 검토하고 수정한다. 이 분업은 명세 작성의 마찰을 근본적으로 낮춘다.
다만 이 분업에는 한 가지 함정이 있다. AI가 생성한 초안은 그럴듯하게 보인다. 빈 문서 앞에서는 “이걸 어떻게 채우지“라는 긴장이 작동하지만, 잘 쓰인 것처럼 보이는 초안 앞에서는 그 긴장이 사라진다. 결과적으로 AI가 모르는 것, 즉 명시적으로 말해지지 않은 판단, 시도했다가 폐기된 접근, 특정 제약이 생긴 배경 같은 것들은 초안에도 없고 검토에서도 추가되지 않는다.
해결책은 검토 방식을 바꾸는 것이다. 초안을 읽고 승인하는 것이 아니라, “이 초안에서 빠진 결정은 무엇인가”, “이 정도 명세이면 개발을 시작 할 수 있나?“를 명시적으로 묻는 검토가 필요하다. 실용적인 방법은 LLM에게 초안을 작성하게 한 직후, 같은 세션에서 “이 명세에서 내가 아직 결정하지 않은 것은 무엇인가“를 다시 묻는 것이다. 이 한 단계가 초안의 구멍을 드러낸다.
빠진 결정을 채운 다음에는 한 번 더 검증한다. “이 명세에 오류, 비약, 일관성 손상이 있는지 재검토하라“는 지시를 같은 세션에서 던진다. 초기 작성자로서의 LLM과 검토자로서의 LLM은 같은 모델이지만 역할이 다르다. 역할이 바뀌면 LLM은 자신이 방금 쓴 것의 허점을 다른 눈으로 본다. 빠진 결정을 찾는 질문이 완전성을 점검한다면, 오류·비약·일관성을 묻는 질문은 내부 논리를 점검한다. 두 질문은 다른 층위에서 작동하므로 둘 다 필요하다.
11.1 반복 지시의 스킬화
이렇게 매 세션 반복되는 지시 — 빠진 결정 확인, 오류·비약·일관성 검토, 명세-코드 드리프트 점검 — 는 매번 손으로 입력할 것이 아니다. 반복되는 지시는 스킬로 승격시킨다. Claude Code의 skill 기능을 비롯해, 자주 쓰는 검토 프롬프트 묶음을 단축 명령으로 저장하면 세션마다의 마찰이 사라진다.
마찰이 사라지는 것이 핵심이다. 검토 지시가 길고 매번 재작성해야 한다면, 피곤한 날에는 생략된다. 생략된 검토가 누적되면 명세의 품질이 느리게 하락한다. 스킬화는 검토를 “의지의 문제“에서 “한 단어를 치는 문제“로 바꾼다. 마찰이 없어지면 검토는 예외가 아니라 규칙이 된다.
이것이 명세 주도 개발을 지속 가능하게 만드는 두 번째 축이다. 첫 번째 축이 문서의 외부 파일화라면, 두 번째 축은 검토 과정의 도구화다. 두 축이 없으면 명세 주도 개발은 개인의 의지력에 의존하는 규율이 되고, 의지력은 반드시 바닥난다.
12. 살아있는 명세: 변경과 집행
명세 주도 개발은 워터폴이 아니다. 명세는 개발 과정에서 계속 변경된다. 변경된 명세는 즉시 코드 재생성, 테스트, 배포 갱신을 요구한다. 명세가 고정된 전면 설계가 아니라 살아있는 문서로 작동하는 것이 워터폴과의 결정적 차이다.
그러나 “명세가 살아있다“는 선언만으로는 집행되지 않는다. 실제 현장에서 드리프트가 발생하는 지점은 두 가지다.
- 반영 누락: 명세가 바뀌었을 때 그 변경이 건드리는 모든 코드/테스트/배포 지점이 실제로 갱신됐는지 누가 확인하는가.
- 암묵적 결정: LLM이 명세에 없는 선택을 했을 때 (라이브러리 선택, 에러 처리 방식 등 명세가 침묵한 부분), 그 선택이 명세로 승격되지 않으면 다음 세션의 LLM은 같은 질문에 다른 답을 낼 수 있다.
이 두 지점을 닫는 데 LLM이 LLM 이전 시대에는 없던 기여를 한다. 매 세션 시작 시 LLM에게 “현재 명세와 코드의 불일치 지점을 나열하라“고 물으면, 인간 단독 리뷰로는 놓치는 드리프트가 상당 비율로 드러난다. 다만 LLM의 검사도 완전하지 않다 — 실제 불일치를 놓치거나, 존재하지 않는 불일치를 거짓으로 보고하기도 한다. 따라서 이 메커니즘은 인간 리뷰를 대체하는 것이 아니라 보완하는 층위로 이해해야 한다. 그 한계 내에서도 이점은 분명하다 — 전통적 CI 게이팅보다 섬세하고, 인간 리뷰보다 검사 범위가 넓다. 명세 주도 개발의 집행 메커니즘은 이 질문을 매 세션의 시작 의례로 만들고, LLM 결과를 최종 판단이 아니라 인간 리뷰의 출발점으로 다루는 것이다.
명세와 코드가 불일치할 때 어느 쪽이 맞는가. 언제나 명세가 맞다. 명세가 단일 소스이기 때문이다. 불일치가 감지되면 해결 방향은 하나다. 명세를 현재 의도에 맞게 갱신하고, 코드를 재생성한다. 코드를 명세의 소스로 쓰는 역방향은 허용되지 않는다. 이것을 허용하는 순간 소스가 둘이 되고, 둘은 반드시 어긋난다.
13. 명세 코퍼스의 규모와 선택적 주입
명세가 여러 문서의 체인이라는 것은 앞서 언급했다. 실무 현실은 체인보다 거대하다. 중규모 시스템조차 요구·설계·API·데이터 모델·정책·런북을 합치면 10페이지 단위 문서가 수십에서 수백 개에 이른다. 이 규모에서는 명세 코퍼스 전체를 매 세션 컨텍스트에 주입할 수 없다. 컨텍스트 윈도우 한계도 문제지만, 긴 컨텍스트에서 LLM의 주의가 중간 영역에서 저하되는 “lost in the middle” 현상이 더 본질적이다. 전부 주입한다고 전부 읽히는 것이 아니다.
“어느 명세를 언제 주입하는가“에 답하지 않는 SDD 담론은 소규모 프로젝트에서만 작동한다. 이 절은 그 답이 현재 무엇이며, 어떤 한계가 남아 있는가를 본다.
13.1 현재 실무: 수작업 큐레이션
현재 이 문제를 실무에서 작동시키는 방법은 단순하다. 인간이 관련 명세를 식별해 직접 편집하고, 해당 파일을 LLM에게 명시적으로 지정해 코드를 생성하게 한다.
흐름은 이렇다. 태스크를 인식한다(“결제 취소 정책 변경”). 관련 파일을 인간이 찾는다(specs/payment/cancellation.md, specs/api/payment.md). 에디터에서 직접 편집한다 — 변경 이유, 새 규칙, 영향 범위를 기재. LLM에게 파일 경로를 명시해 “이 명세를 기준으로 관련 코드를 재생성하라“고 지시한다. LLM은 주어진 명세만 로드해 수정한다. 인간이 검토하고, 필요하면 명세를 다시 수정한다.
이 구조는 인간 = 선택 지능, LLM = 생성 지능의 분업이다. “모든 명세를 매 세션 주입“이라는 불가능한 요구를 인간의 도메인 지식이 우회한다. 수백 문서 규모에서 실제로 작동하는 거의 유일한 방법이다.
단점은 인지적 한계에서 온다. 관련 명세를 누락하면 변경이 불완전해진다. 결제 취소 정책을 바꾸면서 관련 로깅 규칙(specs/ops/logging.md)과 감사 요구(specs/compliance/audit.md)를 떠올리지 못하면, 코드는 명세와 일부 구간에서 어긋난다. 이 누락은 규모가 클수록 잦아지고 장기 부채로 누적된다.
13.2 자동화 시도와 한계
자동화된 선택 주입은 여러 방향에서 시도된다.
- 임베딩 검색(RAG): 코퍼스를 임베딩해 태스크 질의와 유사도 상위 N개를 로드. 구현은 쉽지만, 어휘가 겹치지 않는 관련 명세를 놓치고 어휘만 겹치는 무관 명세를 긁어온다.
- 메타 명세(매니페스트): 어느 명세가 어느 모듈·기능·정책·데이터 엔티티에 걸리는지 기술하는 별도 문서. 결정론적이지만 매니페스트 자체가 유지 비용을 가지며, 명세보다 먼저 낡기 쉽다.
- diff 기반 파급 분석: 코드 변경 diff에서 영향받는 명세를 역추적. 효율적이지만 “새 기능 추가“처럼 기존 코드가 없는 경우에 무력하다.
세 방식은 상호 배타적이지 않고 조합되는 추세다. 그러나 2026년 4월 현재, 수백 문서 규모 SDD를 이런 자동화만으로 운영하는 공개 사례는 드물다. 성숙한 제품·오픈소스 패턴이 아직 확립되지 않았다.
드리프트 점검에서는 규모 문제가 더 첨예해진다. 로드된 명세와 생성 코드의 정합성은 LLM에게 물을 수 있지만, 로드되지 않은 수백 개 명세와의 불일치는 세션 내에서 감지되지 않는다. 이 사각지대를 덮는 방식은 두 가지다. 전수 배치 — 주기적으로 전체 명세-코드를 청크 단위로 LLM에게 검사시킨다. 완전하지만 느리고 비싸다. 변경 기반 파급 점검 — 이번 변경이 건드린 정책·데이터·API를 먼저 식별하고 걸리는 명세만 추가 점검한다. 더 효율적이지만 메타 명세 품질에 의존한다.
13.3 솔직한 현재 상태
앞서 “살아있는 명세” 절에서 제시한 집행 메커니즘 — 매 세션 시작 시 “명세-코드 불일치 지점 나열” — 은 로드된 명세에만 적용된다. 전체 코퍼스에 대한 자동 집행은 대부분의 팀에서 아직 없다. 실무는 수작업 큐레이션과 개별 LLM 세션의 조합으로 버틴다.
이것이 SDD의 현 시점 가장 큰 미해결 지점이다. 방법론 자체는 성립하지만, 그 집행을 완전히 자동화할 도구·패턴이 아직 미성숙하다. 이 한계를 은폐하는 SDD 담론은 실무자에게 도움이 되지 않는다. 한계를 인정하는 쪽의 대응은 세 가지다. 첫째, 수작업 큐레이션의 질을 유지하는 규율 — 태스크마다 “관련 명세 목록“을 명시적으로 기록하는 습관이 최소 장치다. 둘째, 메타 명세와 매니페스트를 점진적으로 축적한다. 셋째, 전수 배치 검사 루틴을 낮은 주기라도 돌린다. 이 셋이 결합될 때 현재 도구 수준에서도 중대규모 SDD가 붕괴하지 않고 운영된다.
14. 시작점
명세 주도 개발이 조직적 규율을 요구한다는 오해가 있다. CI/CD 게이팅, 리뷰 문화 변경, 팀 프로세스 설계. 이것들은 이미 작동 중인 명세 주도 개발을 조직 규모에서 자동화하는 장치이지, 시작점이 아니다. 시작점은 개인 수준과 조직 수준에서 각각 다른 형태로 존재하며, 순서는 개인이 먼저다.
14.1 개인 수준
개인 수준의 시작점은 단순하다. 새 기능을 바이브 코딩으로 시작하기 전에, 먼저 LLM에게 이렇게 물어라. “내가 지금 하려는 것을 한 페이지 명세로 정리해줘. 왜 이걸 만드는지, 어떤 구조를 선택할지, 어떤 트레이드오프가 있는지. 데이터 스키마와 공개 API 이름은 확정해달라. 변수명이나 내부 함수명은 쓰지 마라.” 그 문서를 읽고 수정한 다음 코드를 생성하라. 이것만으로도 다음 세션의 LLM은 맥락을 가진 채로 시작한다.
개인 수준의 시작을 강조하는 데는 이유가 있다. 나쁜 습관을 구조적으로 완전히 교정하는 방법은 존재하지 않는다. 존재하는 것은 나쁜 습관이 치명적인 결과로 이어지는 경로를 좁히는 방법뿐이다. CI/CD 게이팅으로 명세 없는 PR을 막을 수 있지만, 그것은 형식적 토큰 명세를 만들어낼 뿐이다. 구조적 강제가 문화보다 먼저 오면 명세는 통과 의례가 된다.
14.2 조직 수준
조직이 도입할 준비가 됐다면, 기술적 강제보다 리뷰 기준의 변경이 먼저다. “이 변경이 어느 명세의 어느 결정을 건드리는가“가 코드 리뷰의 첫 질문이 되는 팀에서는, 이후의 파이프라인 강제가 형식이 아니라 습관의 자동화로 작동한다. 리뷰 기준이 바뀌면 개발자들이 자연스럽게 명세를 먼저 쓰기 시작하고, 그 습관이 쌓인 다음에 CI/CD 게이팅이 들어오면 게이팅은 이미 존재하는 행동을 확인하는 역할만 한다. 순서가 뒤집히면 형식주의가 된다.
15. 결론
바이브 코딩은 생산성의 도약이다. 명세 주도 개발은 그 도약을 지속 가능하게 만드는 레이어다.
코드는 컴퓨터를 위한 언어다. 명세는 사람을 위한 언어이자, 다음 세션의 LLM을 위한 언어다. 바이브 코딩에서 잃는 것은 개발자의 맥락만이 아니다. LLM이 판단한 맥락까지 함께 잃는다. 그 맥락을 담는 장소가 명세다.
바이브는 본질적으로 개인적이다. 개인 개발자는 규모가 커질 때 SDD에 도달하지만, 팀은 작업이 누적되는 첫 지점부터 외재화된 명세 없이는 일관성이 유지되지 않는다. 개인·커뮤니티·기업의 평행 수렴은 이 구조적 요구가 세 층위에서 동시에 드러난 결과다. 평행 수렴은 두 층위로 나뉜다 — 워크플로우 자체를 SDD로 설계한 명시적 프레임워크(GitHub spec-kit, Amazon Kiro)와, 짧은 세션 프롬프트로 SDD의 일부만 구현하는 세션 디렉티브 레이어(CLAUDE.md, .cursor/rules/ 등). 후자를 명세와 혼동해서는 안 된다.
바이브 코딩 환경에서 맥락은 두 가지 방식으로 소실된다. 세션이 끝날 때 사라지는 것과, LLM이 코드를 수정할 때 보존 지시 없이 제거되는 것. 전자는 인간의 기억 한계와 같다. 후자는 바이브 코딩에 고유한 문제다 — 기본 동작이 제거이고 보존이 예외이기 때문이다. 명세 파일은 이 두 가지 소실 모두에 저항한다.
방법론은 성립하지만 집행은 미완이다. 수십~수백 문서 규모의 명세를 어떻게 선택적으로 LLM에 주입하느냐는 현재 자동화가 미성숙한 영역이며, 실무는 인간이 관련 명세를 식별·편집하고 파일을 명시 지정하는 수작업 큐레이션으로 버틴다. 이 한계를 인정하는 것이 방법론을 이상론이 아니라 실전에 맞게 유지하는 전제다.
명세는 의도와 경계를, 코드는 구현을 담당한다. 명세가 선행하고 코드가 따른다. 이 단방향이 유지되고, 바이브의 개인성이 명세로 외재화되고, 명세 큐레이션이 규율로 운영되는 한, 명세 주도 개발은 바이브 코딩의 맥락을 지키고 그 부채를 걷어낸다.