명세 기반 개발: 코드가 틀렸다고 말할 수 있으려면 명세가 먼저 옳아야 한다
코드가 아닌 명세를 진실의 원천으로 삼는 명세 기반 개발(SDD)의 엄격성 수준, Design by Contract, 명세 기반 테스팅과 AI 시대의 적용 사례를 정리한다.
2026-08-14 · 최초 발행 2026-02-13
전통적 개발에서는 코드가 진실의 원천이다. 문서가 오래되거나 틀려도 실제로 실행되는 건 코드이므로 코드가 맞다. 명세 기반 개발(Spec-Driven Development, SDD)은 이 관계를 뒤집는다. 명세가 진실의 원천이고 코드는 명세로부터 생성되거나 검증되는 2차 산출물이다 — 코드가 명세를 벗어나면, 틀린 건 코드다.
문서가 부수적이던 시절에서 계약이 되기까지
초기 프로그래밍 시대에는 코드가 곧 문서였다. 1988년 Bertrand Meyer가 Design by Contract 개념을 제시하며 명세의 역할이 재조명됐고, Eiffel 언어는 사전조건·사후조건·불변식을 언어 수준에서 지원해 명세가 단순한 주석이 아니라 실행 가능한 계약이 될 수 있음을 보여줬다. 그러나 이 아이디어는 학문적 관심사에 머물렀다. 2010년대 애자일 개발과 TDD의 확산은 "작동하는 소프트웨어가 포괄적인 문서보다 우선한다"는 선언 아래 역설적으로 명세의 중요성을 낮췄고, 개발자들은 코드와 테스트만으로 충분하다고 믿었다.
이 흐름을 바꾼 건 2020년대 중반 AI 코딩 어시스턴트다. GitHub Copilot, Amazon CodeWhisperer, Claude Code 같은 도구가 자연어 명세를 이해해 코드를 생성하게 되면서, 명세는 단순히 인간을 위한 문서가 아니라 AI에게 정확한 지시를 전달하는 핵심 수단이 됐다. Thoughtworks는 SDD를 2025년 핵심 신규 AI 지원 엔지니어링 관행 중 하나로 꼽았고, Google Antigravity 같은 에이전틱 개발 플랫폼이 등장하며 실무 적용이 가속화되고 있다.
명세가 살아있는 문서가 될 때
명세 기반 개발은 잘 작성된 소프트웨어 요구사항 명세를 프롬프트로 사용해 AI 코딩 에이전트의 도움을 받아 실행 가능한 코드를 생성하는 개발 패러다임이다. 전통적으로 명세는 코드 작성 전에 쓰이고 잊혀지는 정적 문서였지만, SDD에서 명세는 코드와 함께 진화하는 살아있는 문서(living document)로, 엣지 케이스를 고민하고 팀 간 조율하며 신입 직원을 온보딩하는 적극적인 도구가 된다.
실행 가능한 계약(Executable Contracts)은 명세가 단순한 설명이 아니라 시스템이 따라야 할 검증 가능한 규칙임을 뜻한다. 명세는 AI에 의해 코드로 변환되고, 테스트로 검증되며, 실행 중에 모니터링된다. 명세가 명확하고 완전하면 시스템의 아키텍처는 자동으로 결정되고 모호함이 없어 드리프트가 발생하지 않는다는 것이 아키텍처의 결정론성(Architectural Determinism)이다. AI 코딩 에이전트는 글자 그대로 해석하는 매우 유능한 페어 프로그래머라, 모호한 지시에는 약하지만 명시적이고 상세한 지시에는 탁월하다. 그래서 좋은 명세는 명확성·완전성·구조화·검증 가능성을 갖춰야 하고, 구현 세부사항보다 인터페이스 계약을 먼저 정의하는 계약 우선 추론(Contract-First Reasoning)이 뒤따른다 — "무엇"을 먼저 명확히 하고 "어떻게"는 나중에 정한다.
워크플로우가 뒤집힌다
전통적 개발은 요구사항 → 코드 → 테스트 → (선택적) 문서 순이지만, SDD는 요구사항 → 명세 → AI 코드 생성 → 검증 → 명세 개선 순이다. 명세 작성이 코딩보다 앞서며 코드는 명세의 구체화다. 변경 관리도 다르다. 전통적 개발에서 요구사항이 바뀌면 코드를 고치고 테스트를 갱신하고 운이 좋으면 문서를 업데이트하지만, SDD에서는 명세를 수정하고 그로부터 코드를 재생성·재검증한다. 팀 협업의 중심도 코드 리뷰에서 명세 리뷰로 옮겨가고, 아키텍처 논의는 코드가 아니라 명세 레벨에서 이뤄진다. 품질 보증 접근도 코드를 쓴 뒤 버그를 찾는 사후 검증에서, 명세 단계에서 모순과 불완전성을 코드 작성 전에 해결하는 사전 검증으로 바뀐다.
Spec-First에서 Spec-as-Source까지
Spec-First(명세 우선)는 코드 작성 전에 명세를 쓰되 코드가 여전히 주요 산출물인 가장 낮은 수준으로, 명세가 가이드 역할만 해도 되는 첫 발걸음이다. API 설계에서 OpenAPI 스펙을 먼저 쓰고 그에 따라 구현하는 방식이 예다. Spec-Anchored(명세 고정)는 명세와 코드가 동등한 중요성을 가지는 중간 수준으로, 코드가 명세를 벗어나면 CI/CD 파이프라인에서 실패하고 계약 테스트(contract testing)로 지속 검증된다. GraphQL 스키마를 명세로 삼아 모든 쿼리·뮤테이션이 이를 준수하는지 자동 검증하는 방식이 여기 해당한다. Spec-as-Source(명세가 소스)는 명세가 유일한 진실의 원천이고 코드는 완전히 생성·검증되는 가장 높은 수준으로, 코드 직접 수정은 금지되고 변경은 항상 명세를 통해서만 이뤄진다. 도메인 특화 언어(DSL) 기반 시스템, 모델 주도 개발(MDD), 고도로 자동화된 AI 코드 생성 환경에 맞는다. 신규 프로젝트나 명확한 도메인 모델이 있다면 Spec-as-Source를, 레거시 시스템이나 복잡한 비즈니스 로직이라면 Spec-Anchored를, SDD를 처음 도입하는 팀이라면 Spec-First로 시작해 점진적으로 수준을 높이는 것이 현실적이다.
Design by Contract: 계약이라는 은유
Design by Contract(DbC)는 SDD의 이론적 토대다. 소프트웨어 요소들이 상호 의무와 이익을 기반으로 협력한다는 은유로, "클라이언트"와 "공급자"가 "계약"에 합의한다.
계약은 의미론적으로 Hoare 삼중항(Hoare triple)과 동등하며 의무를 형식화한다. 사전조건(Preconditions)은 함수·메서드 호출 전 만족돼야 하는 클라이언트 책임의 요구사항으로 "배열 인덱스는 0 이상", "입금액은 양수"처럼 위반 시 클라이언트 버그를 뜻한다. 사후조건(Postconditions)은 함수 완료 후 공급자가 보장해야 하는 상태로 "반환값은 정렬된 버전임", "계좌 잔액이 금액만큼 증가했음"처럼 위반 시 공급자 버그를 뜻한다. 불변식(Invariants)은 클래스 인스턴스가 생애주기 동안 유지해야 하는 제약으로 "스택 크기는 항상 0 이상", "은행 계좌의 총 자산과 총 부채는 항상 균형"이 예이며 위반 시 내부 일관성 오류를 뜻한다. 계약 위반이 발생하면 즉시 명확하게 실패해(Fail Hard) 문제 원인을 조기에 구체적으로 감지하는 것이 디버깅 이점이고, Meyer는 어설션을 설계 단계부터 먼저 작성할 것을 주장했다 — 인터페이스를 명확히 하고 책임을 분명히 하며 테스트 가능성을 높이기 위해서다.
명세를 코드처럼 다루는 법
포트-어댑터 패턴(헥사고날 아키텍처)은 SDD에 이상적인 구조다. 핵심 로직은 격리되고 어댑터가 API·GraphQL·MCP 도구·RAG 등을 통해 기능을 노출하므로, 핵심 로직의 명세는 안정적이고 어댑터는 변경에 유연하게 대응한다. 작업 분해(Task Decomposition)는 구현을 독립적으로 구현·테스트·리뷰 가능한 개별 작업으로 나누는 것으로, AI는 작은 작업을 큰 작업보다 더 정확히 수행하기 때문에 AI 코드 생성에서 특히 효과적이다. 명세 유지 관리(Specification Maintenance)는 명세를 코드처럼 버전 관리·변경 추적·리뷰 프로세스의 대상으로 삼는 것이다. GitHub Spec Kit 같은 도구는 SDD를 git 워크플로우에 통합해 업데이트된 명세 없이는 머지를 게이트하거나 명세 항목으로부터 체크리스트를 생성한다. 인간 감독(Human Oversight)은 SDD가 인간을 루프에서 빼는 게 아니라 최상위 추상화 레이어로 옮기는 것임을 뜻한다 — 개발자는 구현 세부사항이 아니라 아키텍처 결정·명세 설계·계약 정의에 집중한다. CI/CD 통합은 명세와 코드의 일치를 자동 검증하고 계약 테스트를 실행하며 불변식을 검사해, 명세 위반이 빌드 실패로 이어지게 한다. 좋은 명세는 명확성·완전성·일관성·검증 가능성을 갖춰야 하고, AI를 위한 명세는 "사용자 친화적이어야 함" 같은 모호한 표현 대신 "클릭 3회 이내에 모든 기능에 접근 가능해야 함" 같은 측정 가능한 기준이 필요하다.
명세로부터 테스트를 뽑아낸다
명세 기반 테스팅은 테스트 케이스가 소프트웨어 명세를 기반으로 설계되는 블랙박스 테스팅의 한 유형으로, 요구사항이나 사용자 기대에 따라 시스템의 외부 기능·동작을 검증한다. 동등 분할(Equivalence Partitioning)은 입력 도메인을 유사하게 동작할 것으로 기대되는 클래스로 나눠 각 클래스에서 대표 값 하나만 테스트한다. 경계값 분석(Boundary Value Analysis)은 입력 범위의 하한·상한 근처에서 오류를 감지하도록 설계되며, 나이 필드가 0-120을 허용한다면 -1·0·1·119·120·121을 테스트하는 식이다. 결정 테이블(Decision Tables)은 조건과 그에 해당하는 액션으로 구성돼 복잡한 비즈니스 규칙을 체계적으로 검증한다. 상태 전이 테스팅(State Transition Testing)은 이벤트 트리거에 따라 상태가 바뀌는 상태 머신을 확인하는 방식으로, ATM의 "카드 대기 → PIN 입력 → 거래 선택 → 거래 처리" 전이가 예다. 유즈 케이스 테스팅(Use Case Testing)은 실제 사용자 관점에서 다양한 시나리오를 식별해 엔드투엔드 기능을 검증한다. 이런 테스트는 요구사항에 직접 기반하므로 정확하고, 명시된 요구사항이 모두 테스트됐는지 확인해 포괄적인 커버리지를 준다.
AI를 위한 명세는 다르게 써야 한다
AI 에이전트를 위한 좋은 명세는 명확한 컨텍스트(프로젝트 개요·기술 스택·아키텍처 패턴), 구체적 요구사항(모호한 용어 대신 측정 가능한 기준), 예제와 시나리오(입력-출력 예제·엣지 케이스), 제약사항(성능·보안·호환성), 검증 기준(테스트 케이스·허용 기준)을 갖춰야 한다. 명세를 프롬프트로 쓰는 방식에서 효과적인 구조는 역할 정의("당신은 백엔드 개발자입니다") → 명세 제시(요구사항·제약사항·예제) → 작업 지시("다음 명세에 따라 함수를 구현하세요") → 출력 형식("테스트 케이스를 포함해 응답하세요")로 이어진다. 반복적 개선(Iterative Refinement)은 생성된 코드를 명세와 비교해 검증하고, 불일치를 명세에 대한 피드백으로 바꾸고, 명세를 개선한 뒤 재생성하는 사이클을 명세와 코드가 수렴할 때까지 반복하는 것이다.
비결정론성 관리(Managing Non-Determinism)는 AI의 중요한 도전 과제다. LLM으로부터의 코드 생성은 결정론적이지 않아 동일한 명세가 다른 코드를 낳을 수 있어 업그레이드·유지 관리에 문제를 일으키므로, 명세의 구체성 최대화·생성된 코드의 버전 관리·포괄적인 테스트 스위트·계약 테스트로 완화한다. Google Antigravity 같은 플랫폼이 도입한 에이전트 우선 환경(Agent-First Environment)은 특정 워크플로우를 강제하는 대신 모델 자체가 프로세스를 주도하게 하며, 개발자는 명세만 제공하고 AI 에이전트가 작업 분해·코드 생성·테스트·통합을 자율 수행하는 Spec-as-Source의 극단적 형태다.
명세를 다루는 도구들
GitHub Spec Kit은 Microsoft가 제공하는, SDD를 Git 워크플로우에 통합하는 도구로 명세를 마크다운으로 작성해 저장소에 포함하고 PR에서 변경을 리뷰하며 체크리스트를 자동 생성한다. Google Antigravity는 모델 자체가 워크플로우를 주도하는 Agent-First 환경으로, 개발자가 고수준 명세를 주면 작업 분해·의존성 분석·코드 생성·테스트·통합을 자동화해 명세 드리프트와 할루시네이션 완화를 겨냥한다. Kiro는 자연어 명세를 구조화된 포맷으로 바꾸고 AI 코드 생성을 통합하며 명세와 코드의 일치를 검증하는, 명세 작성·검증·유지 관리의 전체 라이프사이클을 지원하는 도구다. Tessl은 명세를 실행 가능한 모델로 바꿔 코드를 생성하는 모델 주도 개발(MDD) 플랫폼으로 형식성이 높은 도메인에 특히 효과적이다. OpenAPI Specification은 REST API의 엔드포인트·요청/응답 형식·파라미터·에러 코드를 YAML·JSON으로 정의해 서버 스텁·클라이언트 SDK·문서를 자동 생성하는, Spec-Anchored SDD를 널리 보급시킨 표준이다. GraphQL Schema는 타입·쿼리·뮤테이션·구독을 정의하는 계약으로 작동하며 변경은 버전 관리되고 호환성이 검증된다. Hypothesis(Python)·QuickCheck(Haskell)·fast-check(JavaScript) 같은 Property-Based Testing 도구는 명세를 속성(properties)으로 표현하면 다양한 입력을 자동 생성해 검증한다.
Design by Contract부터 AI 프롬프트까지, 코드로 보는 명세
Design by Contract를 코드에 어설션으로 구현한 은행 계좌 예제는 명세(계약)가 런타임에 검증되는 방식을 보여준다.
class BankAccount:
"""은행 계좌 클래스
불변식:
- 잔액은 항상 0 이상이어야 함 (오버드래프트 불허)
"""
def __init__(self, initial_balance: float):
"""
사전조건: initial_balance >= 0
사후조건: self.balance == initial_balance
"""
assert initial_balance >= 0, "초기 잔액은 음수일 수 없음"
self.balance = initial_balance
self._check_invariants()
def deposit(self, amount: float):
"""
사전조건: amount > 0
사후조건: self.balance == old(self.balance) + amount
"""
old_balance = self.balance
assert amount > 0, "입금액은 양수여야 함"
self.balance += amount
assert self.balance == old_balance + amount, "잔액 증가 실패"
self._check_invariants()
def withdraw(self, amount: float):
"""
사전조건:
- amount > 0
- amount <= self.balance
사후조건: self.balance == old(self.balance) - amount
"""
old_balance = self.balance
assert amount > 0, "출금액은 양수여야 함"
assert amount <= self.balance, "잔액 부족"
self.balance -= amount
assert self.balance == old_balance - amount, "잔액 감소 실패"
self._check_invariants()
def _check_invariants(self):
"""불변식 검증"""
assert self.balance >= 0, "잔액은 음수일 수 없음 (불변식 위반)"
명세로부터 API를 생성하는 OpenAPI 예제는 도구(예: openapi-generator)가 이 파일 하나로 서버 스텁·클라이언트 SDK·문서를 자동 생성하는 Spec-Anchored 방식을 보여준다.
# user-api-spec.yaml
openapi: 3.0.0
info:
title: User Management API
version: 1.0.0
paths:
/users:
get:
summary: 모든 사용자 조회
responses:
"200":
description: 성공
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
post:
summary: 새 사용자 생성
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserRequest"
responses:
"201":
description: 생성됨
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: 잘못된 요청
components:
schemas:
User:
type: object
required:
- id
- email
- name
properties:
id:
type: integer
format: int64
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
CreateUserRequest:
type: object
required:
- email
- name
properties:
email:
type: string
format: email
name:
type: string
minLength: 1
maxLength: 100
AI를 위한 상세 명세는 컨텍스트·요구사항·제약사항·예제·테스트 케이스를 모두 갖춰야 AI가 요구사항을 충족하는 코드를 생성할 가능성이 높아진다.
## 기능 명세: 사용자 등록
### 컨텍스트
- 프레임워크: FastAPI (Python 3.11)
- 데이터베이스: PostgreSQL with SQLAlchemy ORM
- 인증: JWT 토큰
### 요구사항
1. POST /api/v1/users/register 엔드포인트 구현
2. 요청 바디: { "email": string, "password": string, "name": string }
3. 이메일 중복 검사 (409 Conflict 반환)
4. 비밀번호 해싱 (bcrypt, 최소 8자, 대소문자+숫자+특수문자 포함)
5. 데이터베이스에 사용자 저장
6. 201 Created 응답, 바디: { "id": int, "email": string, "name": string }
### 제약사항
- 응답 시간: 평균 200ms 이하
- 동시 요청 처리: 100 req/s
- 보안: OWASP Top 10 준수
### 예제
**성공 케이스:**
입력: {"email": "user@example.com", "password": "Secur3P@ss", "name": "John Doe"}
출력: 201 Created, {"id": 1, "email": "user@example.com", "name": "John Doe"}
**실패 케이스 1 (중복 이메일):**
입력: {"email": "existing@example.com", "password": "Secur3P@ss", "name": "Jane"}
출력: 409 Conflict, {"detail": "Email already registered"}
**실패 케이스 2 (약한 비밀번호):**
입력: {"email": "user@example.com", "password": "weak", "name": "John"}
출력: 400 Bad Request, {"detail": "Password must be at least 8 characters..."}
### 테스트 케이스
1. 유효한 입력으로 사용자 등록 성공 확인
2. 중복 이메일로 등록 시도 시 409 반환 확인
3. 약한 비밀번호로 등록 시도 시 400 반환 확인
4. 비밀번호가 해싱되어 저장되는지 확인 (평문 저장 불가)
5. 100 req/s 부하 테스트 통과 확인
얻는 것과 치러야 할 것
조기 오류 발견은 명세 단계에서 모순·불완전성·모호함을 코드 작성 전에 잡아 수정 비용을 낮춘다. 팀 간 조율 향상은 명세가 개발자·QA·제품 매니저·고객이 공유하는 공통 언어가 되게 하고, 온보딩 가속화는 신입 개발자가 코드를 파헤치지 않고도 명세로 시스템의 의도와 계약을 파악하게 한다. AI 코드 생성 향상은 명확하고 상세한 명세가 더 정확한 코드를 낳아 반복 개선 사이클을 단축시키고, 아키텍처 일관성은 명세가 아키텍처를 정의하므로 드리프트를 막는다.
도전 과제도 그만큼 뚜렷하다. 비결정론적 코드 생성은 동일한 명세가 다른 코드를 낳아 업그레이드·유지 관리·재현 가능성에 문제를 일으키고, 명세 드리프트(Spec Drift)는 시간이 지나며 코드와 명세가 어긋나는 현상으로 자동 검증 도구와 코드 변경 프로세스에 명세 업데이트를 포함시켜야 막을 수 있다. 초기 투자는 명세 작성 학습 곡선을 요구하지만 장기적으로는 디버깅·리팩토링 시간을 아껴 회수된다. 할루시네이션은 AI가 명세에 없는 기능을 추가하거나 요구사항을 잘못 해석하는 현상으로 포괄적인 테스트와 명세 준수 검증이 필수적이며, 새로운 엔지니어링 규율 요구는 개발자가 명세 작성·DbC 원칙·형식적 방법에 익숙해져야 함을 뜻한다.
Sources
- Spec Driven Development: When Architecture Becomes Executable - InfoQ
- Diving Into Spec-Driven Development With GitHub Spec Kit - Microsoft for Developers
- Spec-driven development: Unpacking one of 2025's key new AI-assisted engineering practices | Thoughtworks United States
- How Google Antigravity is changing spec-driven development | by Giovanni Galloro | Google Cloud - Community | Jan, 2026 | Medium
- Hands-On Spec-Driven Development Workshop Tickets, Thu, Jan 22, 2026 at 9:00 AM | Eventbrite
- Is This the Future of Software Development? (2026 Predictions)
- Spec-driven development
- Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl
- [2602.00180] Spec-Driven Development:From Code to Contract in the Age of AI Coding Assistants
- Specification-Driven Development // SDD | by evoailabs | Medium
- A Practical Guide to Spec-Driven Development - Zencoder Docs
- AddyOsmani.com - How to write a good spec for AI agents
- spec-kit/spec-driven.md at main · github/spec-kit
- Beyond Vibe-Coding: A Practical Guide to Spec-Driven Development | Scalable Path
- Chamber 🏰 of Tech Secrets #54: Spec-driven Development
- Design by contract - Wikipedia
- Design By Contract - an overview | ScienceDirect Topics
- Design by Contract and Assertions
- Design by Contract Introduction - Eiffel Software - The Home of EiffelStudio
- 22c181: Formal Methods in Software Engineering Spring'11 1
- Incorporating Verified Design by Contract into PSP
- Design by Contract for statecharts — Sismic 1.6.11 documentation
- Design by Contract: A Missing Link .. - Eiffel Liberty
- (PDF) COMBINING FORMAL SPECIFICATIONS WITH DESIGN BY CONTRACT 1
- How to implement design by contract in Python | LabEx
- Lost Pixel Blog - Specification Based Testing - Ultimate Guide with Examples (2024)
- What is Test Case Specification? | BrowserStack
- What is black-box, Specification-based, also known as behavioral testing techniques?
- What is Specification Based Testing Technique?
- Specification Based Testing |Professionalqa.com
- What is Specification Based Testing Technique in Software Testing?
- 2.1 Specification-based testing · GitBook
- Specification-Based Test Design Techniques as per ISTQB Advanced Level Syllabus - Software Testing Genius
- Specification Based Testing Techniques
- (PDF) Approaches to Specification-Based Testing