명세 주도 개발 (Spec Driven Development)
2026-04-18
- Opus 4.6 기반 (RAG, 생각의 나무 포함)
- 프롬프트 엔지니어링(압박 사이클, 헤겔의 변증법, 반례 제시법) 적용.
- Human In The Loop 검증 오라클 적용.
1. 초록
바이브 코딩은 생산성의 도약이다. LLM에게 의도를 말하면 코드가 나온다. 동작한다. 배포된다. 문제는 그 다음이다.
바이브 코딩으로 생성된 코드베이스에서 맥락의 소실은 일반적인 코드베이스보다 훨씬 빠르고 완전하게 일어난다. 일반 코드베이스에서는 개발자의 머릿속에라도 맥락이 남는다. 바이브 코딩에서는 개발자조차 코드의 세부를 완전히 이해하지 못한 채 프로덕션에 밀어넣는 일이 발생한다. 코드가 왜 그렇게 생성됐는지, 어떤 판단이 있었는지, LLM이 어떤 프롬프트를 받았는지 — 그 모든 것이 세션이 끝나는 순간 사라진다. 규모가 커지면 LLM 컨텍스트 윈도우 한계 때문에 오래된 결정이 체계적으로 소실되는 기술 부채가 쌓인다.
저자가 개인 시간에 이 문제를 다루다가 즉흥적 노트 대응이 체계적 명세 문서로 자연스럽게 전환되는 자리에 도달했고, 거기서 형성된 것이 명세 주도 개발(Spec Driven Development, SDD) 이다. 같은 구조가 GitHub spec-kit·Amazon Kiro 등 오픈소스·기업 양쪽에서 동시에 형성되고 있어 LLM 기반 개발의 구조적 요구로 보이지만, 산업의 일시적 유행과 분리해 증명할 수는 없다. 본 문서는 검증된 정리가 아니라 공학적 제안이다.
핵심은 단순하다 — 명세가 항상 선행하고 코드는 명세에서 파생되며, 그 역방향은 (부트스트랩 예외를 제외하고) 허용되지 않는다. SDD는 워터폴·애자일·V모델 같은 개발 프로세스가 아니라 명세-코드 관계를 지시하는 원칙으로, 어느 프로세스와도 직교한다. 본문은 spec-as-code, 경계 원칙과 형식언어, 바이브의 개인성에서 도출되는 다인 개발의 SDD 필연성, 부트스트랩, 적용 경계(맥락 소실 신호가 전환점), 그리고 코딩 에이전트가 토론을 청취해 합의를 명세로 자동 승격하는 미래 방향을 다룬다. 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 기반 개발의 구조적 요구임을 강하게 시사한다. 다만 이 평행 수렴이 산업의 일시적 유행과 구조적 필연을 완전히 분리해 증명하지는 못한다. 같은 도구·블로그·논문을 참조하는 좁은 커뮤니티 안에서 형성된 합의일 가능성도 배제되지 않으며, 어느 한 도구가 deprecation되거나 다른 패러다임으로 대체되는 시나리오도 열려 있다. 평행 수렴은 방향의 강한 시사이지 증명은 아니다.
이 여정을 단순화하면 세 개의 전환점이 있다. 맥락 소실을 겪은 순간(1→2), 즉흥 대응을 체계로 전환하기로 결정한 순간(3→4), 개인적 발견이 다층 평행 수렴이었음을 확인한 순간(5→6). 세 전환점은 규모가 일정 이상으로 커지면 누구나 겪는 구조적 지점이다. 맥락 소실은 규모에서 불가피하고, 즉흥 대응은 반드시 한계에 도달하며, 그 해결은 같은 구조로 수렴한다.
맥락 소실은 추상적 우려가 아니라 실제로 겪는 사이클이다. 2단계에서 겪은 것을 단순화하면 이렇다. 세션 초반에 “결제 실패는 재시도 없이 데드레터 큐로 보내기로 결정“이라고 LLM에게 말한다. 수십 회의 왕복이 지난 후, 같은 세션에서 결제 관련 새 기능을 요청한다. LLM은 재시도 로직을 포함한 코드를 제시한다. 앞 결정이 압축 과정에서 사라진 것이다. 이 현상이 반복되면 개발자는 “중요한 결정은 매번 다시 말해야 한다“는 것을 깨닫고, 결국 “매번 말하느니 적어두자“로 이동한다. 명세 주도 개발은 이 이동의 끝에 있다.
3. 평행 수렴의 지형
개인·커뮤니티·기업의 평행 수렴이 각 층위에서 무엇을 구체적으로 형성하고 있는지를 본다. 두 범주로 나뉜다. 워크플로우 자체를 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 코딩 도구는 프로젝트 단위로 지속되는 짧은 지시 파일을 표준 기능으로 제공한다. 이 파일들은 크기가 작을 때는 매 세션 시작 시 주입되는 세션 프롬프트 — 프로젝트별 상시 시스템 지시문 — 에 가깝다. 크기가 커질수록 도메인 정책·아키텍처 제약·에러 처리 규칙 같은 작은 명세 역할을 겸하지만, 파일 자체가 일정 규모를 넘으면 그 안에서 맥락 소실이 시작된다 — 긴 컨텍스트에서 중간 영역의 주의가 떨어지는 “lost in the middle“이 한 파일 내부에서도 재현된다. 따라서 이 파일들은 명세로 기능할 수 있지만 본격 명세 코퍼스(주제별로 분리되고 선택적으로 주입되는 수십~수백 문서)와 같다고 단정할 수는 없다. 둘은 별개 범주라기보다 연속 스펙트럼이며, 경계가 유동적임을 인정하되 “작은 지시 파일 하나로 전체 명세를 대체한다“는 오해는 피해야 한다. 세션이 끝나도 보존되는 맥락이라는 점에서 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개월 뒤의 자신은 지금의 자신에게 사실상 “다른 개발자“다. 따라서 바이브 개인성의 요구는 다인 협업뿐 아니라 개인 내부의 시간 축에도 작용한다. 외재화가 없으면 오늘의 결정은 내일의 자신에게도 이미 이해 불가능한 과거가 된다.
바이브를 공유하려면 외재화해야 한다. 대화로, 메모로, 문서로. 명세는 외재화된 바이브의 한 형태다. 컨벤션, 아키텍처 원칙, 코딩 가이드, 프로젝트 룰 — 이름은 달라도 같은 것이다. 외재화의 다른 형태도 있다 — Slack 토론, 화이트보드, 코드 주석, CLAUDE.md 같은 세션 디렉티브 — 그러나 모든 외재화가 명세는 아니다. 명세는 단일 소스의 지위와 코드 파생의 출처가 되는 외재화 형태로 따로 정의된다. 외재화되지 않은 바이브는 구조적으로 소실된다 — 머신은 인간 머릿속의 암묵지에 접근할 방법이 없기 때문이다. 이것은 SDD가 받아들이는 비용이다.
따라서 다인 환경에서 “명세 없는 협업“은 성립하지 않는다. 명세가 없다고 느껴지는 팀은 실제로는 암묵적 명세(공유 문화, 장기 협업에서 형성된 공유 멘탈 모델)에 의존한다. 그 암묵적 명세는 인원이 바뀌면 곧바로 깨지고, 인원이 그대로여도 시간이 지나면 깨진다 — 기억은 흐려지고 멘탈 모델은 어긋나며 합의는 망각된다. 더 결정적으로, 암묵적 명세는 머신이 접근할 수 없다. LLM이 코드 생성에 참여하는 순간 암묵 영역은 누락되고, 누락된 자리는 LLM의 임의 선택으로 채워진다. 인간끼리만 일할 때는 견딜 수 있던 한계가 LLM 매개 개발에서는 즉시 부채로 환산된다.
이 관점은 앞서 “실험의 장소” 절에서 제기한 일반화 한계를 뒤집는다. 팀 환경에서 SDD가 유효한지는 검증이 필요한 가설이 아니다. 다인 개발은 규모에 의한 맥락 소실을 경험하기 이전에, 바이브의 개인성에 의해 이미 명세를 강제받는다. 개인 개발자가 규모에서 SDD에 도달한다면, 팀은 작업이 누적되는 첫 지점부터 외재화된 명세가 있어야 일관성이 유지된다. 다인 환경에서 SDD는 더 일찍, 더 강하게 필요해진다.
4.1 팀의 운영 메커니즘
원리 위에 몇 가지 운영 장치가 없으면 팀에서 SDD는 선언에 그친다. 명세가 있지만 아무도 지키지 않는 상태, 혹은 명세와 코드가 점진적으로 어긋나는 상태가 된다. 이 간극을 메우는 최소 장치는 네 가지다.
명세 PR 리뷰의 기준. 코드 리뷰의 첫 질문은 “이 변경이 어느 명세의 어느 결정을 건드리는가“여야 한다. 두 가지 PR은 자동 반려 대상이다. 명세 변경 없이 동작이 달라지는 PR(명세가 먼저 오지 않은 개발), 명세는 바뀌었는데 관련 코드가 따라오지 않은 PR(파생이 일어나지 않은 명세 변경). 이 두 반려 규칙이 단방향 원칙과 살아있는 명세를 팀 차원에서 집행한다.
명세의 소유권. 각 명세 파일의 owner를 명시한다 — CODEOWNERS로 강제하거나 문서 상단 메타데이터로 기록한다. 공유 명세는 다수 owner의 approval을 요구하고, 애플리케이션 고유 명세는 해당 팀 내부에서 결정한다. 소유권이 없으면 명세는 고아가 되어 아무도 유지하지 않고, 결국 “낡은 명세는 믿지 말라“는 팀 내 암묵지가 생긴다. 그 순간 SDD가 붕괴한다 — 명세가 진실의 소스가 아니라 참고 자료로 격하된다.
동시 편집 충돌의 해결. 두 개발자가 같은 명세를 다르게 수정할 때, 병합 전략은 코드 병합과 다르다. 명세의 충돌은 텍스트 수준이 아니라 결정 수준이다. 자동 병합이나 단순 양보로 해결하면 병합된 명세 안에 은밀한 모순이 남는다 — 앞서 부트스트랩에서 다뤘던 “여러 바이브의 레거시“가 같은 팀 안에서 실시간으로 재현되는 경로다. 두 결정을 나란히 놓고 한 쪽을 채택하거나, 합의된 세 번째 결정을 내린 뒤 병합한다. 이 화해 단계는 생략 가능한 편의가 아니라 필수 절차다.
공유 명세 변경의 공지. 여러 팀이 의존하는 공유 명세가 바뀌면 모든 소비자에게 변경이 통지되어야 한다. CODEOWNERS 기반 자동 알림, 분산 채널 공지, RFC 프로세스 중 무엇을 쓰든 원칙은 하나다 — 공지 없이 공유 명세를 바꾸지 않는다. 공지가 없으면 소비자 쪽에서 느린 드리프트가 시작되고, 그 드리프트는 공유 명세가 “언제부터 어떻게 달라졌는가“를 재구성해야 해결 가능해진다. 이 재구성 비용은 사전 공지 비용보다 언제나 크다.
이 네 가지는 팀이 규모가 커지면서 자연스럽게 발명하게 되는 관행이지만, SDD 맥락에서 명시되지 않으면 “명세가 있다“와 “명세가 팀 차원에서 작동한다“의 간극이 남는다.
4.2 다음 단계: 토론 청취 에이전트
앞의 네 운영 장치는 현재 도구 수준에서 팀이 SDD를 유지하는 최소 구성이지만, 여전히 한 지점에 인간의 수작업이 남아 있다. 의사결정이 일어나는 장소(Slack, 회의)와 명세가 저장되는 장소(repo) 사이의 다리가 그 지점이다. 팀이 채팅에서 어떤 결정을 내린 뒤, 누군가 그 결정을 명세 파일로 옮겨 PR을 올려야 한다. 이 수작업은 거의 언제나 부분적으로만 일어난다. 바쁜 날에는 생략되고, 논의가 길었던 결정일수록 요약이 부정확하며, 참여자들 사이에 “누가 올릴 것인가“가 합의되지 않으면 아무도 올리지 않는다.
그 결과 Slack 토론 안에 결정이 갇힌다. 이것은 본문이 “고고학적 발굴“로 이름 붙인 현상의 진원지이며, 팀이 명세를 유지하려 아무리 노력해도 결정의 일부가 구조적으로 새어 나간다. 명세 repo는 최신이지만 완전하지 않은 상태 — 가장 나쁜 실패 양식이다. 빠진 것이 있는지 자체가 감지되지 않기 때문이다.
Git의 한계도 같은 자리에서 드러난다. 단방향 원칙 절에서 지적했듯, 바이브 코딩에서 인간이 에디터에 직접 손을 대는 거의 유일한 실무 접점은 머지 충돌 해결이다. 두 AI 생성 브랜치의 충돌을 인간이 줄 단위 diff로 푸는 것은 일관성 보존을 인간의 국소 판단에 의탁하는 일이며, Git의 텍스트 기반 병합 모델이 AI 기반 개발의 워크플로에 더 이상 충분하지 않다는 신호다. Slack 토론과 Git 충돌은 같은 구조적 문제의 두 얼굴이다 — 인간-도구 인터페이스가 의사결정과 일관성 유지를 인간 수작업에 떠넘기는 지점.
이 간극을 메우는 것이 코딩 에이전트가 다음으로 가야 할 방향이다. 에이전트가 팀의 토론을 상시 청취하다가, 합의가 형성되는 순간을 감지해 그 합의를 명세 파일의 변경으로 승격시키고, 변경된 명세로부터 코드를 재생성하고, 테스트와 명세 내부 모순 점검을 실행해 검증 대기 상태로 올린다. 이 전체가 하나의 파이프라인이다. 토론 → 명세 → 코드 → 검증. 팀은 평소처럼 대화하고, 에이전트가 그 사이의 모든 다리 — 토론을 명세로 옮기는 다리, 명세를 코드로 옮기는 다리, 명세 간 일관성을 지키는 다리 — 를 담당한다. 인간의 역할은 두 끝점에 집중된다 — 토론(무엇을 원하는가의 판단)과 검증(결과가 의도에 부합하는가의 판단). 이 두 가지는 원래부터 인간이 잘하고 LLM이 못하는 일이며, 그 중간의 번역·전파·일관성 유지는 기계화 대상이다.
이 방향은 SDD의 여러 원칙이 자동 집행되는 지점이기도 하다. 합의에 이르지 못한 사항은 “미결정 — 잠정 방침” 항목으로 자동 기록되고, “왜 이 결정을 내렸는가“의 맥락이 원본 토론 링크와 함께 이력에 편입되며, 명세 간 충돌은 매 병합 시 기계 점검된다. 수작업 큐레이션과 머지 충돌 해결이 함께 덜어지면서, 지금까지 개인과 팀의 규율로 유지해야 했던 것들이 구조로 지지되기 시작한다.
더 멀리 보면 머지 단계 자체가 사라질 가능성도 열려 있다. 청취 에이전트가 팀의 합의를 실시간으로 명세에 반영하고 명세에서 코드가 파생된다면, 다수 개발자가 각자 로컬 브랜치에서 분기 작업을 한 뒤 사후에 텍스트 diff로 충돌을 해소하는 현재 모델 자체가 우회된다. 합의가 일어나는 시점에 이미 단일 명세가 갱신되고, 코드는 그 갱신된 명세에서 한 번에 생성되기 때문이다. 분기와 병합은 인간이 서로의 작업을 동시에 볼 수 없다는 제약에서 나온 우회로였는데, 에이전트가 그 동기화를 담당하면 우회로가 불필요해진다. 이것이 실현되면 Git의 의미는 “협업 도구“에서 “이력 보존 도구“로 좁아질 수도 있다.
구체적 메커니즘은 앞으로 팀들이 합의해 나갈 영역이다. 합의를 무엇으로 감지할지(명시적 승인, 이모지 반응, lazy consensus 기간), 에이전트가 자동으로 진행할지 단계별 확인을 거칠지, 합의 후 번복된 결정을 어떻게 되돌릴지, Slack 밖의 결정(회의, 화이트보드, 1:1 DM)을 어떻게 통합할지, 분기-병합을 대체할 동기화 모델을 어떻게 설계할지 — 이 설계 문제들은 실제 구축 과정에서 팀의 규모·문화·도메인에 따라 다르게 합의될 것이다. 이 문서는 구체 설계를 규정하지 않는다. 다만 다인 개발 팀을 위한 코딩 에이전트가 가야 할 방향을 제시한다. 지금은 방향이 명확해진 것만으로 충분하다. 세부는 그 방향을 실제로 걸어가는 팀들이 채워 나가야 한다.
5. 문서가 채우는 레이어
코드는 어떻게 구현됐는지는 보여주지만, 왜 그렇게 됐는지는 말해주지 않는다. 왜 이 라이브러리를 골랐는지, 왜 이 구조를 선택했는지, 왜 이 프롬프트를 사용했는지. 바이브 코딩에서 이 이유들은 개발자의 머릿속에도 없는 경우가 많다. LLM이 생성했고, 동작했고, 그게 전부다.
TDD와 타입 시스템, 잘 작성된 테스트는 실재하는 가치를 가진다. assert response_time < 200ms는 자연어보다 훨씬 정밀하다. 코드는 동작의 정확성을 검증하는 데 있어 문서보다 우월하다. 다만 그 테스트가 왜 200ms라는 기준을 사용하는지, 이 기능이 왜 존재하는지, 왜 이 구조적 결정을 내렸는지는 테스트 코드 어디에도 없다. TDD와 SDD는 서로 다른 종류의 지시이며 충돌하지 않는다 — TDD는 코드를 어떻게 쓸 것인지를, SDD는 무엇을 만들 것인지를 다룬다. 코드와 문서는 경쟁하지 않는다. 서로 다른 레이어를 채운다.
문서가 담아야 하는 것은 세 가지다. 의도, 구조적 결정, 그리고 경계를 넘는 식별자. 동작의 세부와 경계 안의 식별자는 코드에 있다. 지도는 지형의 모든 돌멩이를 표시하지 않아도 유용하다. 그리고 지도가 틀렸을 때 따라야 하는 것은 지형이 아니다. 명세 주도 개발에서 지도는 지형을 생성하는 원본이다.
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%에 가까워야 한다. 바이브 코딩이 성숙한 환경에서는 디버그·리팩토링·타이포 수정조차 AI에게 위임된다. 인간이 에디터에 직접 손을 대는 거의 유일한 실무 접점은 머지 충돌 해결이며, 이는 단방향 원칙의 예외라기보다 Git이 AI 기반 개발 워크플로에 더 이상 충분하지 않다는 신호다. 두 AI 생성 브랜치가 충돌했을 때 인간이 줄 단위 diff로 해결하는 것은 일관성 보존을 인간의 국소적 판단에 맡기는 것이며, AI가 전체 맥락에서 해결해야 할 일을 역류시킨다. 요점은 “인간이 절대 타이핑하지 말라“가 아니라 “인간의 타이핑이 명세의 소스가 되지 않도록 하라” 는 것이다.
8.2 부트스트랩: 코드에서 명세를 역추출하는 일회성 작업
단방향 원칙이 절대적이라면 한 가지 현실적 상황에서 막힌다. 명세가 없는 코드에 SDD를 시작해야 하는 경우다. 두 경로로 발생한다.
첫째, 외부에서 인수하는 경우 — 전임 개발자가 떠났거나, 외주 결과물을 받았거나, 혼자 오래전에 만들어두고 맥락을 잊은 코드를 다시 열었을 때. 더 까다로운 변형은 여러 개발자가 각자 다른 바이브로 작성한 레거시를 팀 전체가 인수할 때다 — 코드에 혼재된 바이브 때문에 추출된 명세 초안이 내부 모순을 품는다.
둘째, 자기 자신이 방금 바이브로 만든 코드에서 맥락을 회수할 때. “오래전에“가 꼭 몇 년일 필요는 없다. 바이브 코딩의 생성 속도 때문에 자신이 어제 쓴 코드조차 오늘은 낯설어진다. 맥락이 재현되지 않기 시작하는 순간 부트스트랩이 필요하며, 규모만 다를 뿐 첫째 경로와 메커니즘은 같다.
그 출발점을 만드는 유일한 방법은 코드로부터 명세를 역으로 추출하는 것이다. LLM에게 코드베이스 전체를 주고 “데이터 스키마, 공개 API, 아키텍처 결정을 추출해 명세 초안으로 정리하라“고 지시한다. 초안은 불완전하다. 원작자의 의도에 대한 추측이 섞여 있다. 인수자는 그 초안을 검토하고, 확인 가능한 범위 내에서 추측을 확정하고, 잘못된 추측을 수정한다.
여러 개발자의 레거시에서는 이 단계가 더 복잡하다. 한 모듈은 재시도를 기본으로 하고 다른 모듈은 빠른 실패를 택하는 식으로, 추출된 명세 초안에 상충하는 결정이 그대로 남는다. 인수자는 각 충돌에서 어느 쪽을 프로젝트 정책으로 채택할지 결정하고 나머지 쪽의 코드를 재생성해야 한다. 이 화해(reconcile) 작업이 생략되면 부트스트랩이 끝난 뒤에도 명세 내부에 상충이 남아 단방향 원칙이 작동하지 않는다.
이것이 단방향 원칙을 깨는 것처럼 보이지만 그렇지 않다. 역방향 생성은 순방향을 가능하게 만드는 부트스트랩 작업이다. 단방향 원칙이 금지하는 것은 “지속적 개발에서 코드가 명세의 소스로 기능하는 것“이다. 인수 시의 역방향은 소스가 없는 상태에서 소스를 창조하는 일회성 전환이다. 전환이 완료된 순간부터 명세가 소스가 되고 코드는 파생이 된다. 오히려 이 역방향이 선행되지 않으면 순방향 규율 자체가 시작될 수 없다.
따라서 규칙은 이렇게 정리된다. 명세 없는 레거시를 만났을 때의 첫 작업은 명세 역생성이다. 이 작업이 끝나기 전까지 새 기능을 추가하지 않는다. 추가하는 순간 명세와 코드의 격차가 커져 역생성이 점점 더 어려워진다. 부트스트랩이 끝난 후부터는 일반적인 단방향 규칙이 적용된다.
9. 무엇을 지정하고 무엇을 지정하지 않는가
명세 작성의 가장 흔한 실수는 과잉 상세화다. 변수명, 함수명, 내부 헬퍼의 구조까지 명세에 담기 시작하면 명세는 코드의 중복이 된다. 중복된 명세는 존재할 이유가 없다. 그 경우 바이브 코딩이 더 빠르다.
이것은 운영적 반증 규칙이기도 하다. 명세가 코드 수준의 식별자를 지정하기 시작하는 순간, 그 명세는 쓸 필요가 없어진다. 명세가 존재할 이유는 오직 코드가 말할 수 없는 층위에서만 나온다.
기준은 경계다. 경계를 넘는 식별자는 명세에, 경계 안에 머무는 식별자는 AI 재량에 맡긴다.
명세가 지정하는 것 (경계를 넘는 식별자):
- 데이터 스키마, 테이블 이름, 컬럼 이름
- 통신 스펙, API 엔드포인트, 메시지 포맷
- 파일명, 앱 이름, 서비스 이름
- 공개 API 함수/클래스 이름 (다른 모듈·서비스가 호출하는 것)
- 정책 (명명 규칙, 에러 처리 규칙, 로깅 규칙)
- 상수 값 (재시도 횟수, 타임아웃, 임계치)
- 비기능 요구사항 (SLO·SLA, 보안 정책, 컴플라이언스 기준, 접근성 요건)
명세가 지정하지 않는 것 (경계 안에 머무는 식별자):
- 지역 변수명
- 내부 헬퍼 함수명
- 구현 세부 구조 (결과에 영향이 없는 패턴 선택 등)
이 구분의 근거는 인터페이스는 계약이고 구현은 교체 가능하다는 원리다. 경계를 넘는 식별자는 외부에서 참조되므로 바뀌면 외부가 깨진다. 경계 안의 식별자는 LLM이 일관되게 리네임할 수 있고, 외부에 영향이 없다.
정책과 인스턴스의 구분도 중요하다. “함수는 snake_case를 쓰라“는 정책이고, “이 함수를 calculate_tax로 이름 지으라“는 인스턴스다. 정책은 명세에, 인스턴스는 LLM 재량에. 이 구분을 놓치면 “컨벤션을 명세화한다“가 “변수명을 명세화한다“로 미끄러진다.
9.1 자연어의 모호성과 형식언어
경계를 넘는 식별자를 명세에 담는다는 원칙은 무엇을 담는가의 규칙이다. 어떻게 담는가는 별개의 문제다. “사용자 API는 JSON으로 이름과 이메일을 반환한다“고 자연어로 적으면 그 문장은 이미 모호하다. 이름이 문자열인지 객체인지, 이메일이 선택 필드인지 필수인지, 빈 문자열이 허용되는지. 모호한 명세는 LLM이 매 세션 다르게 해석할 여지를 남긴다. 자연어의 모호성은 맥락 소실의 또 다른 경로다.
형식언어는 소프트웨어의 발명이 아니다. 인류는 자연어의 모호성을 피하기 위해 오랜 시간에 걸쳐 여러 형식언어를 만들어 왔다 — 수학의 기호 체계, 논리학의 술어 표기, 물리학의 방정식, 화학의 구조식, 악보, 체스 기보. 수학·물리·논리 논문과 서적은 자연어와 형식 표기가 섞인 문서의 가장 오래된 형태다. 정리와 증명의 핵심은 수식으로 고정되고, 왜 이 정리가 흥미로운지, 증명의 전략은 무엇인지는 자연어로 서술된다. 두 층이 한 문서 안에 공존하는 구조는 SDD가 발명한 것이 아니라, 엄밀성이 요구되는 영역에서 반복 재발견된 보편 패턴이다.
진실의 중복을 피하는 원칙과 모호성을 제거하는 원칙은 충돌하지 않는다. “명세가 코드 수준의 식별자를 지정하지 마라“는 규칙은 변수명·함수 몸체·내부 분기 같은 구현 수준 식별자를 겨냥한 것이지, 정밀한 형식 기술을 금지하는 것이 아니다. 경계를 넘는 식별자처럼 모호성이 곧바로 드리프트로 이어지는 층위에서는 자연어 대신 형식언어로 명세를 작성해야 한다. 소프트웨어에서 이 역할을 맡는 형식언어는 이미 풍부하다.
- 데이터 스키마: JSON Schema, SQL DDL, Protocol Buffers, Avro
- API 계약: OpenAPI, gRPC IDL, GraphQL SDL
- 이벤트·메시지: AsyncAPI, Protobuf
- 요구사항 패턴: EARS 표기 (Kiro의
requirements.md가 채택하는 형태) - 동시성·상태 모델: TLA+, Alloy (필요한 시스템에 한해)
여기서 목록이 닫히지 않는다. 도메인이 이미 자기 형식언어를 가지고 있다면 그것을 그대로 명세에 들여온다. 성능 요구에는 빅-오 표기, 불변식에는 술어 논리, 확률적 동작에는 확률 분포 표현, 금융 상품 명세에는 재무 수식, 신호 처리에는 수식과 단위계, 물리 시뮬레이션에는 편미분 방정식. 도메인 공용 형식언어를 재발명하지 않고 차용하는 쪽이 거의 언제나 낫다 — 해당 분야의 독자(그리고 LLM)가 이미 그 표기를 학습했기 때문이다.
형식언어로 작성된 명세는 구현 코드가 아니다. 스키마 파일은 실행되지 않고, 계약을 기계 검증 가능한 형태로 고정할 뿐이다. 오히려 구현 코드가 형식 명세의 파생으로 생성된다 — OpenAPI에서 클라이언트·서버 stub이, Protobuf에서 언어별 클래스가, SQL DDL에서 마이그레이션이 나온다. 이 방향은 “명세가 선행하고 코드는 파생“이라는 단방향 원칙의 가장 깔끔한 구현이다. 형식 명세를 진실의 소스로 두면 LLM이 매 세션 자연어를 다르게 해석해 코드가 흔들리는 경로 자체가 닫힌다.
그렇다고 자연어가 밀려나는 것은 아니다. 왜 이 계약을 택했는가, 어떤 트레이드오프가 있었는가, 어떤 대안을 검토하다가 버렸는가 — 이 층위는 형식언어가 표현하지 못한다. 수학 논문에서 증명이 수식만으로 구성되지 않고 동기와 주석이 함께 들어가는 것과 같은 이유다. 결과적으로 같은 명세 문서 안에 두 층이 공존한다. 형식언어로 기술된 계약부는 정밀하고 기계 검증 가능하며, 자연어로 기술된 의도·근거부는 다음 세션의 LLM이 이 계약이 왜 이런 모양인지를 재구성하는 근거가 된다. 둘 다 명세의 일부이며, 둘 다 단일 소스의 지위를 공유한다.
선택 규칙은 단순하다. 모호성이 드리프트 비용을 낳을 자리에는 형식언어를, 의도와 근거에는 자연어를 쓴다. 경계를 넘는 식별자는 대체로 전자에 해당하고, 아키텍처 선언과 트레이드오프 논의는 후자에 해당한다. 앞 절의 “미결정” 기록도 이 구분을 따른다 — 잠정 방침의 값은 형식부에, 왜 아직 결정하지 못했는지의 이유는 자연어부에 기록한다.
9.2 테스트: SDD의 명세부와 TDD의 코딩 산물
SDD와 TDD는 충돌하지 않는다 — 다른 종류의 지시이기 때문이다. SDD는 무엇을 만들 것인가 — 비즈니스 규칙, 과학 원리, 도메인 정책, 기술적 계약 — 를 지시한다. 출처는 사용자·도메인 전문가·비즈니스 요구·시스템 제약이다. TDD는 코드를 어떻게 쓸 것인가 — 테스트를 먼저 쓰고 그것을 통과하는 코드를 작성하라는 코딩 규율 — 를 지시한다. 출처는 개발 행위 자체에 대한 방법론적 선택이다. 둘은 같은 평면에서 경쟁하지 않는다.
따라서 둘은 SDD > TDD > 코딩의 계층을 이룬다. SDD가 무엇을 만들지 결정하면, 그 안에서 TDD라는 코딩 규율이 어떻게 쓸지 결정하고, 그 결과로 코드와 테스트가 나온다. TDD가 “테스트를 코드보다 먼저“라고 말하는 그 “먼저“는 코딩 단계 안에서의 순서이지 명세보다 먼저라는 뜻이 아니다. TDD가 테스트를 쓰다가 비즈니스 케이스 누락을 발견했다면, 그것은 TDD가 요구사항을 결정한 것이 아니라 TDD가 SDD에 질문을 돌려보낸 것이다. 결정 권한은 SDD에 남는다.
테스트라는 형식이 두 활동 모두에서 쓰인다는 점이 혼동의 원인이다. 같은 assert 한 줄이 SDD의 수용 기준을 표현할 수도 있고 TDD의 단위 검증을 수행할 수도 있다. 형식이 같다고 두 활동이 같은 것은 아니다. 그 테스트가 SDD의 명세 산물인지 TDD 규율을 따른 코딩 산물인지를 구분하는 것이 명확성을 회복하는 출발점이다.
- SDD 산물 — 수용 테스트, 계약 테스트, 도메인 불변식 표현 (BDD/Gherkin 시나리오, OpenAPI 응답 스키마 검증, 비즈니스 규칙의 속성 명제 등): 외부에서 관찰 가능한 동작과 경계 계약을 형식언어로 고정한다. 이것이 형식언어 절과 직접 연결되는 자리다 —
assert response_time < 200ms가 “빨라야 한다“는 자연어보다 정밀하고, JSON Schema가 구조를 기계 검증 가능하게 고정하는 것과 같은 층위에서 동작을 고정한다. 이 테스트들은 명세의 형식부 그 자체이며 명세 코퍼스의 일부로 단일 소스의 지위를 공유한다. - TDD 규율을 따른 코딩 산물 — 단위 테스트, 함수 수준 속성 기반 테스트: 코딩 단계에서 함수·모듈 단위의 동작을 검증하고 설계를 드러낸다. SDD는 이 산물의 결정에 관여하지 않는다 — 코드를 어떻게 쓰는가는 코딩 규율이 답할 문제이지 명세가 답할 문제가 아니다. 단위 테스트는 구현과 함께 진화하며, 명세 코퍼스에 독립적으로 유지될 필요가 없다.
같은 종류의 테스트도 어느 활동의 산물인지가 갈릴 수 있다. 속성 기반 테스트가 도메인 불변식(“주문의 합계는 항상 0보다 크다”)을 표현하면 SDD 산물, 함수 동작 속성(“정렬 함수의 출력은 입력의 순열이다”)을 표현하면 TDD 산물이다. 형식이 아니라 내용이 활동을 결정한다.
불일치 처리도 활동에 따라 다르다. 수용 테스트가 명세와 다르면 명세가 맞다 — SDD의 단방향 원칙이 적용된다. 테스트를 명세에 맞추고 코드를 재생성한다. 반면 단위 테스트와 코드의 차이는 다음 TDD 사이클의 입력이다 — Red-Green-Refactor가 작동하는 자리다. SDD의 단방향과 TDD의 사이클이 서로 다른 활동에서 각자 작동하므로 충돌하지 않는다.
실무 규칙으로 정리하면 두 활동의 분리가 파일 시스템에서도 드러난다. 수용 테스트는 자연어 요구사항과 같은 파일 안에 둔다 — Kiro의 requirements.md가 EARS 표기로 수용 기준을 내장하는 것, Gherkin의 .feature 파일이 시나리오를 자연어와 형식부의 결합으로 기술하는 것이 그 예다. 단위 테스트는 구현 저장소에 코드와 함께 위치한다. 위치만 보고도 “어느 테스트가 어느 활동의 산물인가“를 즉시 알 수 있다.
9.3 UI·디자인 명세
UI와 디자인은 형식언어가 상대적으로 덜 성숙한 영역이다. API나 데이터 스키마처럼 단일 표준이 없다. 그러나 SDD 원칙은 그대로 적용된다 — 경계를 넘는 것(사용자가 실제로 보는 화면 구조, 디자인 토큰, 컴포넌트 계약)은 모호성을 제거한 형태로 명세하고, 내부 구현(CSS 클래스명, 중첩 구조)은 LLM 재량에 맡긴다.
어떤 형식언어를 쓰느냐는 누가 UI를 제안하느냐에 따라 달라진다.
디자이너가 UI를 제안하는 경우에는 Figma 같은 디자인 도구가 형식 명세 역할을 한다. 디자인 파일 자체가 모호성을 제거한 시각 명세이며, 디자인 토큰(색·간격·타이포그래피·elevation)은 JSON으로 기계 판독 가능하게 내보내진다. 구현은 이 명세에서 파생된다. Storybook 스펙, 컴포넌트 프로퍼티 타입 정의가 디자인 토큰과 결합해 “이 컴포넌트는 어떤 상태를 가지며 어떤 입력을 받는가“의 형식부를 구성한다.
LLM에게 UI 생성을 맡기는 경우에는 Figma 없이도 텍스트 명세만으로 충분하다. 박스를 그리는 아스키 아트는 인간이 손으로 쓰기 번거롭고 수정하기는 더 어렵다. 대신 컨테이너 선언과 두 개의 구분자만으로 컴포지션을 표현한다. 아래 표기는 본 문서가 제안하는 한 형식일 뿐, 검증된 표준이 아니다 — 도메인이 이미 자기 UI 명세 표준(Storybook stories, MJML, JetPack Compose Preview, SwiftUI Preview 등)을 가지고 있다면 그것을 우선 사용한다.
이름:— 컨테이너 선언|— 같은 줄에서 수평 배치 (왼쪽에서 오른쪽)---— 줄을 분리하는 수직 구분 (위에서 아래)
이 세 가지로 다음 같은 명세가 가능하다.
워크스페이스:
헤더
---
바디
헤더:
로고 | 내비게이션 | 햄버거 메뉴
바디:
사이드바 | 메인 콘텐츠
각 컨테이너는 자기 줄에서 독립적으로 정의되고, 내부 구성은 |로 옆 배치를, 줄바꿈과 ---로 위아래 배치를 표현한다. 박스의 정확한 비율, 패딩, 색, 시각적 강조 같은 세부는 LLM에게 맡긴다.
이 형식의 장점은 단위성이다. 헤더만 따로 수정해도 다른 부분에 영향이 없고, 새 컴포넌트가 추가되면 새 정의 한 덩어리로 끝난다. 산문으로 “왼쪽에 사이드바가 있고 그 옆에 콘텐츠가 있는데…“를 풀어 쓰는 것보다 모호성이 적고, 박스 아스키 아트보다 손으로 쓰고 고치기가 훨씬 쉽다. 인간은 구조와 배치만 결정하고, 렌더링의 시각적 세부는 LLM에게 위임한다 — 경계를 넘는 결정과 경계 안의 구현을 분리하는 SDD 원칙이 UI 명세에서도 그대로 적용된다.
여기에 자연어로 상호작용 규칙(어떤 버튼이 어떤 상태 전이를 일으키는가), 상태 머신 다이어그램, 접근성 요구(스크린 리더 라벨, 키보드 내비게이션 순서)를 덧붙인다.
두 접근은 배타적이지 않다. 디자이너가 주요 화면의 Figma를 제공하고, LLM에게 주변 컴포넌트를 맡길 때 텍스트 컴포지션 표기로 배치를 지시하는 식의 혼용이 실무에서 자주 일어난다. 원칙은 하나다 — UI를 자연어 산문으로만 기술하지 않는다. 산문은 레이아웃과 상호작용을 표현하기에 너무 모호하며, LLM은 매 세션 다른 해석을 내놓는다.
10. 속도가 아니라 맥락의 문제
명세 주도 개발을 “단기 속도를 반납해 장기 속도를 얻는 거래“로 보는 흔한 프레이밍이 있다. 이 프레이밍은 본질을 놓친다. 명세는 빠르게 개발하려는 도구가 아니다. 맥락을 잃지 않으려는 도구다.
맥락이 없는 코드베이스에서 LLM에게 작업을 지시하려면 개발자는 매번 결정과 배경을 다시 말해야 한다. “왜 이 라이브러리를 골랐는지”, “어떤 예외 처리 규칙을 세웠는지”, “결제 실패는 재시도 없이 데드레터 큐로 보내기로 했는지”. 이 반복된 타이핑은 단순 노동이고, 단순 노동의 반복은 개발을 지루하고 지치게 만든다. 피로는 누락을 부른다. 누락된 결정은 LLM에 의해 추론으로 채워지고, 추론은 확률적으로 틀린다. 이 사이클이 바이브 코딩에서 부채가 누적되는 실제 경로다.
명세는 이 사이클을 끊는다. 한 번 써두면 다음 세션의 LLM이 그 문서를 읽고 시작한다. 개발자는 매번 같은 결정을 다시 말하지 않아도 된다. “한 번 쓰고 여러 세션에서 재사용하는 맥락” — 이것이 명세의 기능적 정체성이다.
속도는 부산물이지 목적이 아니다. 명세가 자리 잡으면 부산물로 속도가 따라올 수도 있고, 특정 단위 과제에서는 명세를 쓰는 시간이 바이브로 타이핑하는 시간보다 길 수도 있다. 그러나 개발자가 피로에 의해 누락하거나 포기하지 않고 일관된 품질로 계속 작업할 수 있느냐 — 이것이 명세 주도 개발이 지키는 실제 가치다.
명세 작성의 비용이 낮다는 점이 이 논리를 보완한다. 인간이 처음부터 끝까지 쓰지 않는다 — AI가 초안을 만들고 인간이 다듬는다. 경계 원칙이 과잉 상세화를 막아 규모가 코드에 비례해 팽창하지도 않는다. 맥락 유지의 이익은 크고, 그 비용은 생각보다 작다.
11. AI와 명세 작성의 분업
LLM은 개발자의 말을 듣고 명세 초안을 만든다. 대화에서 오간 내용, 결정의 이유, 시도했다 버린 접근을 정리한다. 인간은 그 초안을 검토하고 수정한다. 이 분업은 명세 작성의 마찰을 근본적으로 낮춘다.
다만 이 분업에는 한 가지 함정이 있다. AI가 생성한 초안은 그럴듯하게 보인다. 빈 문서 앞에서는 “이걸 어떻게 채우지“라는 긴장이 작동하지만, 잘 쓰인 것처럼 보이는 초안 앞에서는 그 긴장이 사라진다. 결과적으로 AI가 모르는 것, 즉 명시적으로 말해지지 않은 판단, 시도했다가 폐기된 접근, 특정 제약이 생긴 배경 같은 것들은 초안에도 없고 검토에서도 추가되지 않는다.
해결책은 검토 방식을 바꾸는 것이다. 초안을 읽고 승인하는 것이 아니라, “이 초안에서 빠진 결정은 무엇인가”, “이 정도 명세이면 개발을 시작 할 수 있나?“를 명시적으로 묻는 검토가 필요하다. 실용적인 방법은 LLM에게 초안을 작성하게 한 직후, 같은 세션에서 “이 명세에서 내가 아직 결정하지 않은 것은 무엇인가“를 다시 묻는 것이다. 이 한 단계가 초안의 구멍을 드러낸다.
빠진 결정을 채운 다음에는 한 번 더 검증한다. “이 명세에 오류, 비약, 일관성 손상이 있는지 재검토하라“는 지시를 같은 세션에서 던진다. 초기 작성자로서의 LLM과 검토자로서의 LLM은 같은 모델이지만 역할이 다르다. 역할이 바뀌면 LLM은 자신이 방금 쓴 것의 허점을 다른 눈으로 본다. 빠진 결정을 찾는 질문이 완전성을 점검한다면, 오류·비약·일관성을 묻는 질문은 내부 논리를 점검한다. 두 질문은 다른 층위에서 작동하므로 둘 다 필요하다.
11.1 미결정은 침묵이 아니라 기록이다
빠진 결정을 찾았을 때의 처리는 두 갈래다. 결정을 내릴 수 있으면 명세에 적는다. 아직 내릴 수 없으면 미결정 상태 자체를 명세에 남긴다. 이 두 번째 갈래가 자주 누락된다.
누락되는 이유는 인간의 직관이 “결정하지 않았다면 쓸 것이 없다“고 판단하기 때문이다. 일반 문서에서는 이 직관이 맞다. 그러나 바이브 코딩 환경에서 명세 항목의 상태는 세 가지다. 결정됨, 명시적 미결정, 침묵. 이 중 침묵은 중립이 아니다. LLM은 빈칸을 공백으로 두지 않고 자기 판단으로 채운다. 그 결과 실제로는 미결정이어야 할 사항이 LLM의 암묵적 선택에 의해 결정된 것처럼 코드에 박힌다. 다음 세션의 LLM은 코드만 보고 “이 결정은 이미 내려졌다“고 오인하거나, 반대로 같은 질문에 다른 답을 내리기도 한다. 어느 쪽이든 드리프트의 씨앗이다.
실무 규칙은 이렇다. 결정하지 않은 사항은 단순히 비워두지 않고 “미결정 — 이유: X, 재검토 시점: Y, 잠정 방침: Z” 같은 형태로 미결정 상태 자체를 명세에 기록한다. 잠정 방침까지 적는 이유는 그 사이에 LLM이 코드를 생성할 때 근거 없는 선택을 하지 않도록 경계를 긋기 위함이다. 잠정 방침이 없으면 LLM은 다시 자기 판단으로 공백을 메운다.
이 “미결정” 표시는 LLM이 제멋대로 해석하지 않도록 명시적으로 알려주는 한 가지 방법일 뿐이다. 다른 방법도 가능하다 — TODO 주석을 활용한 명세 내부 표시, 별도 미결정 추적 파일, 명세 메타데이터의 status 필드 등. 핵심 원리는 “침묵을 결정으로 오해하지 않도록 LLM에게 명시적으로 신호를 준다“는 것이며, 표기 형식은 팀이 선택할 수 있다.
이 규칙이 있으면 “빠진 결정 확인” 스킬이 찾아야 하는 것이 두 가지로 늘어난다. 첫째, 결정되지 않은 사항이 있는가. 둘째, 결정되지 않은 사항이 결정되지 않았다고 기록되어 있는가. 후자가 빠지면 침묵이 결정으로 오인되는 경로가 그대로 열린다.
경계 원칙과 연결하면 우선순위가 명확해진다. 경계를 넘는 식별자에서는 침묵이 특히 위험하다. LLM이 임의로 정한 API 엔드포인트명, 테이블명, 재시도 횟수 같은 상수 값이 외부 시스템에 노출된 뒤 바꾸려면 마이그레이션이 필요해진다. 경계 안의 구현 세부라면 LLM이 일관되게 리네임할 수 있으므로 침묵이 큰 해를 끼치지 않는다. 따라서 미결정 기록의 최소 범위는 경계를 넘는 사항이며, 이 범위에서는 “미결정임을 명시“의 비용이 “AI에게 맡기고 나중에 정리“의 비용보다 훨씬 작다.
11.2 반복 지시의 스킬화
이렇게 매 세션 반복되는 지시 — 빠진 결정 확인, 미결정의 기록 여부 점검, 오류·비약·일관성 검토, 명세-코드 드리프트 점검, 명세 집합 내부 충돌 점검 — 는 매번 손으로 입력할 것이 아니다. 반복되는 지시는 스킬로 승격시킨다. Claude Code의 skill 기능을 비롯해, 자주 쓰는 검토 프롬프트 묶음을 단축 명령으로 저장하면 세션마다의 마찰이 사라진다.
마찰이 사라지는 것이 핵심이다. 검토 지시가 길고 매번 재작성해야 한다면, 피곤한 날에는 생략된다. 생략된 검토가 누적되면 명세의 품질이 느리게 하락한다. 스킬화는 검토를 “의지의 문제“에서 “한 단어를 치는 문제“로 바꾼다. 마찰이 없어지면 검토는 예외가 아니라 규칙이 된다.
이것이 명세 주도 개발을 지속 가능하게 만드는 두 번째 축이다. 첫 번째 축이 문서의 외부 파일화라면, 두 번째 축은 검토 과정의 도구화다. 두 축이 없으면 명세 주도 개발은 개인의 의지력에 의존하는 규율이 되고, 의지력은 반드시 바닥난다.
12. 살아있는 명세: 변경과 집행
SDD는 개발 프로세스가 아니다. 워터폴, 애자일, V모델은 개발 프로세스 — 작업을 어떻게 나누고 언제 검증하는가에 대한 결정 — 다. SDD는 종류가 다르다. 명세와 코드 사이의 관계(명세가 선행하고 코드가 파생되며, 그 역방향은 없다)를 지시하는 원칙이지, 작업을 시간 축으로 어떻게 배치할지는 다루지 않는다. 따라서 SDD와 개발 프로세스는 직교 축이며, 충돌할 자리가 없다.
이 직교성 덕분에 SDD는 어느 프로세스와도 결합한다.
- 워터폴 방식의 SDD: 한 번의 큰 명세 → 한 번의 큰 코드 생성. 명세 단계가 길고 변경이 드물지만, 코드는 여전히 명세에서 파생된다.
- 애자일 방식의 SDD: 작은 단위 명세의 반복적 이터레이션. 매 스프린트마다 명세 → 코드의 짧은 사이클이 돈다.
- V모델 방식의 SDD: 각 명세 층(요구사항·아키텍처·상세 설계)에 대응하는 검증 층(수용 테스트·통합 테스트·단위 테스트)이 짝을 이룬다. 명세-코드 단방향은 각 짝 안에서 작동한다.
어떤 프로세스를 선택하든 SDD가 요구하는 것은 단 하나 — 코드는 명세에서 파생되며 그 역방향은 없다.
이 독립성이 “살아있는 명세“의 의미를 정확히 한다. 명세가 살아있다는 것은 반드시 빠르게 진화한다는 뜻이 아니라 변경 가능한 단일 소스로 유지된다는 뜻이다. 워터폴 방식의 SDD에서도 배포 이후 발견된 요구 변경은 먼저 명세를 갱신한 뒤 코드를 재생성한다. 변경의 빈도가 다를 뿐 단일 소스 원칙은 동일하게 작동한다.
그러나 “명세가 살아있다“는 선언만으로는 집행되지 않는다. 실제 현장에서 드리프트가 발생하는 지점은 두 가지다.
- 반영 누락: 명세가 바뀌었을 때 그 변경이 건드리는 모든 코드/테스트/배포 지점이 실제로 갱신됐는지 누가 확인하는가.
- 암묵적 결정: LLM이 명세에 없는 선택을 했을 때 (라이브러리 선택, 에러 처리 방식 등 명세가 침묵한 부분), 그 선택이 명세로 승격되지 않으면 다음 세션의 LLM은 같은 질문에 다른 답을 낼 수 있다. 앞 절의 “미결정은 침묵이 아니라 기록이다” 규칙이 이 지점의 예방책이다 — 침묵이어야 할 자리에 “미결정 — 잠정 방침” 형식의 표지를 미리 박아두면, LLM이 그 표지를 보고 암묵적 선택을 멈추거나 최소한 잠정 방침 안쪽에서 움직인다.
이 두 지점을 닫는 데 LLM이 LLM 이전 시대에는 없던 기여를 한다. 매 세션 시작 시 LLM에게 “현재 명세와 코드의 불일치 지점을 나열하라“고 물으면, 인간 단독 리뷰로는 놓치는 드리프트가 상당 비율로 드러난다. 다만 LLM의 검사도 완전하지 않다 — 실제 불일치를 놓치거나, 존재하지 않는 불일치를 거짓으로 보고하기도 한다. 따라서 이 메커니즘은 인간 리뷰를 대체하는 것이 아니라 보완하는 층위로 이해해야 한다. 그 한계 내에서도 이점은 분명하다 — 전통적 CI 게이팅보다 섬세하고, 인간 리뷰보다 검사 범위가 넓다. 명세 주도 개발의 집행 메커니즘은 이 질문을 매 세션의 시작 의례로 만들고, LLM 결과를 최종 판단이 아니라 인간 리뷰의 출발점으로 다루는 것이다.
명세와 코드가 불일치할 때 어느 쪽이 맞는가. 언제나 명세가 맞다. 명세가 단일 소스이기 때문이다. 불일치가 감지되면 해결 방향은 하나다. 명세를 현재 의도에 맞게 갱신하고, 코드를 재생성한다. 코드를 명세의 소스로 쓰는 역방향은 허용되지 않는다. 이것을 허용하는 순간 소스가 둘이 되고, 둘은 반드시 어긋난다.
명세와 명세가 충돌할 때도 같은 메커니즘으로 점검한다. 정책 명세와 수용 테스트가 어긋나거나, 공유 명세와 애플리케이션 고유 명세가 겹치는 지점에서 다르게 말하거나, 같은 용어가 두 문서에서 다르게 정의될 때. LLM에게 “이 명세 집합 안의 내부 모순을 나열하라“고 물으면 인간이 읽기로 놓치는 충돌이 드러난다. 이 지시를 스킬로 승격하면 검토가 세션마다 한 단어로 실행 가능해진다. 충돌이 감지되면 어느 쪽을 채택할지 결정하고 나머지 쪽을 그에 맞춰 갱신한다 — 부트스트랩 절의 “화해” 작업이 지속 개발에서 국소적으로 반복되는 형태다. 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. 적용 경계: SDD가 필요 없는 경우
방법론은 자기 한계를 아는 만큼만 신뢰받는다. SDD는 모든 개발에 필요하지 않으며, 적용하면 오버헤드가 순이익을 넘는 경우가 존재한다. 다만 바이브 코딩의 생성 속도 때문에 그 경계는 직관보다 훨씬 좁다.
SDD가 필요 없는 범주는 분명하다.
- 한 자리에서 끝날 코드. 몇 시간 안에 아이디어를 확인하고 버릴 스크립트, 주말 장난감, 한 세션 안에 완결되는 실험. 사용자가 자신 한 명이고, 맥락 소실 사이클이 도착하기 전에 프로젝트가 끝난다.
- 일회성 스크립트·ad-hoc 데이터 분석. 한 번 돌리고 결과만 취하는 작업. 재사용이 없으면 맥락 보존의 이익도 없다.
- 학습·연습 코드. 개발자가 새 기술을 익히는 목적 자체가 “맥락을 머리에 적재하는 것“이다. 외재화된 명세로 대체하면 LLM이 대신 학습하고 개발자는 명세 큐레이터 역할만 하게 된다. 학습 목적에서는 의도적 비효율이 이익이다.
이 범주의 공통점은 한 세션 안에 끝나는 작업이라는 점이다.
전환 기점은 시간이 아니라 맥락 소실이다. 도메인·언어·LLM 역량·프로젝트 성격에 따라 맥락이 새어나가는 속도는 크게 다르며, “하루”, “1주일” 같은 인간의 체감 단위는 방법론의 기준이 되기에 너무 거칠다. SDD 전환의 실제 기점은 맥락이 재현되지 않기 시작하는 순간이다. 이 신호는 인간 개발자가 작업 중 직접 감지하는 것이지 LLM이 자동 판단해 알려주는 것이 아니다 — 도구 보조가 들어오기 전까지는 개발자 본인의 자기 관찰이 유일한 탐지기다. 구체적으로 다음 신호 중 하나라도 잡히면 도메인이 무엇이든 전환 시점이다.
- 같은 결정을 LLM에게 두 번째로 설명하고 있다.
- 방금 전 생성된 코드의 동작을 자신이 확신하지 못한다.
- 어떤 예외 처리 규칙을 썼는지, 어떤 라이브러리를 골랐는지 기억나지 않는다.
여기에 더해 두 명 이상이 같은 코드베이스를 건드리는 순간 — 시간 축과 별개로 — 즉시 SDD가 필요해진다. 바이브가 개인적이기 때문이며, 이 조건은 첫날부터 성립한다.
전환의 방식은 처음부터 명세를 쓰는 것이 아니다. 바이브 코딩이 이미 쌓인 상황에서 빈 명세 문서를 열고 처음부터 쓰기 시작하면, 그 순간의 코드와 맞지 않는 명세가 나온다. 개발자의 머리에도 이미 완전한 맥락이 남아 있지 않기 때문이다. 대신 앞서 부트스트랩 절에서 다룬 코드→명세 역추출을 그대로 적용한다. 지금까지 생성된 코드를 LLM에게 주고 “데이터 스키마, 공개 API, 결정된 정책, 아직 결정되지 않은 사항을 추출해 명세 초안으로 정리하라“고 지시한다. 개발자는 초안을 검토·수정한 뒤, 그 시점부터 순방향 SDD로 전환한다. 부트스트랩 절이 “레거시 인수“를 주 예시로 들었지만, 실질적으로 이 기법을 가장 자주 필요로 하는 대상은 자신이 방금 바이브로 만든 코드다.
경계를 명시하는 이유는 방법론의 권위를 위해서가 아니라 적용 판단을 개발자에게 넘기기 위해서다. SDD를 모든 상황에 강요하는 담론은 한 자리에서 끝날 실험을 질식시키고, 역으로 SDD를 아예 도입하지 않는 담론은 맥락 소실이 시작된 이후에도 바이브 코딩을 이어가게 해 부채를 쌓는다. 규칙은 단순하다 — 맥락이 유지되는 동안은 바이브, 맥락이 새어나가기 시작하는 순간 코드→명세 역추출로 SDD에 진입. 처음부터 명세로 시작하지 않아도 되고, 놓친 전환점도 코드에서 복원할 수 있다. 관건은 맥락 소실의 신호를 놓치지 않는 것이다.
16. 결론
바이브 코딩은 생산성의 도약이다. 명세 주도 개발은 그 도약을 지속 가능하게 만드는 레이어다.
코드는 컴퓨터를 위한 언어다. 명세는 사람을 위한 언어이자, 다음 세션의 LLM을 위한 언어다. 바이브 코딩에서 잃는 것은 개발자의 맥락만이 아니다. LLM이 판단한 맥락까지 함께 잃는다. 그 맥락을 담는 장소가 명세다.
바이브는 본질적으로 개인적이다. 개인 개발자는 규모가 커질 때 SDD에 도달하지만, 팀은 작업이 누적되는 첫 지점부터 외재화된 명세 없이는 일관성이 유지되기 어렵다. 개인·커뮤니티·기업의 평행 수렴은 이 구조적 요구가 세 층위에서 동시에 드러난 결과로 보인다. 평행 수렴은 두 층위로 나뉜다 — 워크플로우 자체를 SDD로 설계한 명시적 프레임워크(GitHub spec-kit, Amazon Kiro)와, 짧은 세션 프롬프트로 시작해 커지면 명세 역할을 겸하는 세션 디렉티브 레이어(CLAUDE.md, .cursor/rules/ 등). 둘은 별개 범주라기보다 연속 스펙트럼이며, 작은 지시 파일 하나로 본격 명세 코퍼스를 대체할 수 있다는 오해는 피해야 한다.
바이브 코딩 환경에서 맥락은 여러 경로로 새어나간다 — 세션이 끝날 때, LLM이 코드를 수정할 때 보존 지시 없이 주석을 제거할 때, 결정되지 않은 사항이 기록되지 않아 LLM이 그 공백을 자기 판단으로 메울 때. 명세 파일은 이 누수들에 저항하는 장치다.
방법론은 성립하지만 집행은 미완이다. 수십~수백 문서 규모의 명세를 어떻게 선택적으로 LLM에 주입하느냐는 현재 자동화가 미성숙한 영역이며, 실무는 인간이 관련 명세를 식별·편집하고 파일을 명시 지정하는 수작업 큐레이션으로 버틴다. 이 한계를 인정하는 것이 방법론을 이상론이 아니라 실전에 맞게 유지하는 전제다.
명세는 의도와 경계를, 코드는 구현을 담당한다. 경계의 계약부는 자연어의 모호성을 피해 형식언어로 고정하고, 의도와 근거는 자연어로 남겨 다음 세션의 LLM이 이유를 재구성할 수 있게 한다. 명세가 선행하고 코드가 따른다. 이 단방향이 유지되고, 바이브의 개인성이 명세로 외재화되고, 명세 큐레이션이 규율로 운영되는 한, 명세 주도 개발은 바이브 코딩의 맥락을 지키고 그 부채를 걷어낸다.
그리고 다음 단계는 이미 윤곽이 잡혀 있다 — 지금까지 규율로 유지하던 외재화를 코딩 에이전트가 토론 청취를 통해 자동화하는 방향. 팀의 합의가 곧 명세로 승격되는 구조에 도달하면, SDD는 개인과 팀의 의지가 아니라 도구가 지지하는 층위로 내려앉는다. 구체 형태는 팀들이 합의해 나가게 될 것이며, 방향만 본 문서가 가리킨다.
이것은 공학적 제안이다. SDD는 LLM 기반 개발이라는 새로운 환경에 적응한 공학적 진화의 한 단계이며, 다음 단계가 이어질 것이다. LLM은 아직 초기이고 업계는 실험의 시작점에 있다. 본 문서가 제안하는 패턴들도 도구가 진화함에 따라 더 단순해지거나, 다른 형태로 흡수되거나, 일부 폐기될 수 있다. 그것이 공학의 정상적 작동 방식이다. 지금 시점에 이 제안이 유용한 출발점이 되기를 바란다.