요구사항부터 코드까지: 스펙 기반 개발 워크플로우

의도에서 검증된 코드로의 5단계

Page content

스펙 기반 개발(SDD, Spec-Driven Development)은 스펙이 문서화된 자료가 아니라 워크플로우일 때 비로소 빛을 발합니다. 핵심은 방대한 제품 요구사항 명세서를 작성하는 데 있지 않습니다.

핵심은 사람이든 AI 에이전트든 프로덕션 코드를 변경하기 전에, 모호성을 단계적으로 줄여나갈 수 있는 검토 가능한 산출물들의 시퀀스를 거치는 데 있습니다.

SDD가 개념적으로 무엇인지 모르는 분은 먼저 정의를, TDD 및 BDD와의 비교를, 그리고 스펙을 진실의 원천(Source of Truth)으로 대우해야 하는 이유를 살펴보기 위해 스펙 기반 개발이란 무엇인가?를 읽어보시길 권장합니다. 본 문서가 속한 앱 아키텍처 문서 클러스터는 운영 매뉴얼입니다. 이 글에서는 5단계 프로세스를 순서대로 살펴보고, 각 단계의 산출물이 반드시 포함해야 할 내용을 제시하며, AI 에이전트가 어떻게 개입하는지 설명하고, 오늘 바로 저장소에 적용할 수 있는 재사용 템플릿을 제공합니다.

스펙 기반 개발 워크플로우 – 요구사항, 디자인, 태스크, 구현, 검증

SDD는 문서가 아닌 워크플로우입니다

스펙 기반 개발에서 가장 흔한 실패 패턴은 스펙을 단순한 행정 처리( paperwork)로 여기는 것입니다. 팀이 긴 요구사항 문서를 작성한 뒤 위키에 보관하고, 기억과 채팅 트레드를 바탕으로 코딩을 시작하는 경우입니다. 스펙은 존재하지만 아무것도 주도하지 못합니다. 이는 ‘문서 형식주의’이며, 오히려 스펙이 없는 것보다 위험할 수 있습니다. 왜냐하면 이것이 근거 없는 자신감을 심어주기 때문입니다.

성공적인 SDD 워크플로우는 각 단계가 다음 단계로 넘어가기 전에 검토를 거치는 일련의 산출물을 생성합니다. 요구사항은 제품 측면의 모호성을 줄이고, 디자인은 기술적 모호성을 줄이며, 태스크는 실행의 모호성을 줄입니다. 구현은 명확한 목표를 향해 코드를 생성하고, 검증은 이러한 체인이 견고했음을 증명합니다. 어떤 단계에서 오류가 발견되면, 해당 산출물을 수정하고 그 지점부터 다시 실행해야 합니다. 3,000줄의 드리프트(차이)가 메인 브랜치에 이미 반영된 후에야 수정할 수는 없습니다.

flowchart LR A[Specify] --> B[Plan] B --> C[Tasks] C --> D[Implement] D --> E[Validate] E -->|drift found| A E -->|ship| F[Done]

이 워크플로우는 특정 도구에 종속되지 않습니다. Git 기반 마크다운 파일로 실행할 수도 있고, GitHub Spec Kit, OpenSpec와 같은 가벼운 변경 중심 CLI, Cursor의 플랜, Superpowers와 같은 강제적인 스킬 패키지, 혹은 단순한 텍스트 에디터와 엄격한 리뷰어와 함께 실행할 수 있습니다. 중요한 것은 도구의 브랜드가 아니라 순서와 리뷰 게이트입니다.

1단계 – 요구사항 명세화(Specify)

명세화 단계에서는 해결해야 할 문제가 무엇인지, 그리고 ‘완료’의 기준이 무엇인지에 답합니다. 이 단계에서는 의도적으로 ‘어떻게 구현할 것인가’를 제외합니다. 요구사항 스펙에서 “Redis 정렬 집합(sorted sets)을 사용하라"고 쓴 순간, 명세화를 멈추고 잘못된 문서 안에서 디자인을 시작하는 셈이 됩니다. 구현은 요구사항에서 제외하고 플랜에 적어넣으세요.

문제 진술과 사용자

평이한 언어로 문제를 서술하는 한 단락으로 시작하세요. 영향을 받는 사용자의 이름을 명시하고, 그 문제가 고통스러워지는 상황을 설명하세요. 좋은 문제 진술은 계획 회의에 참석하지 않은 리뷰어가 제안된 솔루션이 실제로 고통을 해결하는지 판단할 수 있게 해줍니다.

API 레이트 리미팅 기능을 위한 예시:

무료 티어 사용자는 무제한으로 요청을 보낼 수 있어 비용 급등과 유료 테넌트에 대한 ‘시끄러운 이웃(noisy-neighbor)’ 영향을 초래합니다. 플랫폼 운영자는 수동 개입 없이 강제 가능한 키별 제한이 필요합니다.

목표, 비목표, 수용 기준

목표(Goals)는 전달할 산출물을 설명합니다. 비목표(Non-goals)는 유혹적인 인접 영역이지만 명시적으로 하지 않을 작업을 설명합니다. 이들은 에이전트의 창의적 범위를 제한하며, AI 도구가 그렇지 않으면 ‘도움이 될 것’이라고 생각하여 범위를 무차별적으로 확장하는 것을 방지하는 데 필수적입니다.

섹션 좋은 예시 부족한 예시
목표 키별 제한을 초과한 요청은 HTTP 429로 거부 API를 빠르게 만들기
비목표 테넌트별 빌링 대시보드 모든 API 성능 개선
수용 기준 미인증 요청은 레이트 체크가 실행되기 전에 401을 수신 엔드포인트가 안전하다

수용 기준은 각 기준이 최소 하나의 테스트에 매핑될 만큼 정확해야 합니다. “엔드포인트가 안전하다"는 수용 기준이 될 수 없습니다. “미인증 요청은 HTTP 401을 수신한다"는 수용 기준입니다. 구체적인 기준을 작성할 수 없다면, 그 요구사항은 구현하기에 여전히 모호한 상태입니다.

미결 정류(Open questions)

아직 확정되지 않은 모든 결정을 나열하세요. 불분명한 질문은 실패의 징조가 아닙니다. 이는 명세화 단계가 제 기능을 하고 있음을 나타냅니다. 디자인 플랜을 작성하기 전에 이를 해결해야 합니다. 그렇지 않으면 모호성의 대가로 구현 재작업을 해야 할 것입니다.

최소 요구사항 템플릿:

## Problem
[한 단락: 누가 피해를 입는지, 왜 그런지, 고통을 유발하는 트리거는 무엇인지.]

## Users
- [주요 사용자 역할]
- [보조 사용자 역할]

## Goals
1. [측정 가능한 산출물]
2. [측정 가능한 산출물]

## Non-goals
- [명시적으로 범위에 포함되지 않음]
- [명시적으로 범위에 포함되지 않음]

## Acceptance criteria
- [ ] [검증 가능한 동작]
- [ ] [검증 가능한 동작]

## Open questions
- [ ] [플래닝을 막는 질문]

2단계 – 디자인 플랜 수립(Plan)

플랜 단계는 의도를 기술적 결정으로 번역합니다. 여기에는 Redis 정렬 집합을 비롯해 모듈 경계, 스키마 변경, API 계약, 마이그레이션 절차, 보안 제약 조건, 테스트 전략 등이 포함되어야 합니다. 플랜은 요구사항 스펙과 프로젝트의 기존 제약 조건(스택 선택, 결정 기록, AGENTS.md 또는 프로젝트 헌장에 저장된 규약 등)을 기반으로 파생됩니다.

아키텍처와 영향받는 모듈

변경될 모듈, 서비스, 또는 패키지의 이름을 명시하고 통합 패턴을 요약하세요. 기능이 서비스 경계를 넘어선다면, 양쪽 모두의 계약을 문서화하세요. 계약이 암묵적일 때 에이전트는 API를 환각(Hallucinate)합니다. 플랜에서 이를 명시적으로 만들면, 발명한 엔드포인트나 잘못된 응답 형식을 예방할 수 있습니다.

데이터 모델, API 계약, 마이그레이션

스키마 변경, 새 테이블 또는 필드, 인덱스 요구사항, 하위 호환성 규칙을 문서화하세요. HTTP API의 경우, 메서드, 경로, 요청 형상, 응답 형상, 에러 코드를 작성하세요. 이벤트의 경우, 토픽 이름, 페이로드 스키마, 전달 시맨틱을 작성하세요. 데이터 모델이 변경되면 마이그레이션 단계와 롤백 노트를 포함하세요.

보안, 관찰 가능성, 테스트 전략

보안 제약 조건은 코드 리뷰에서의 추후 생각(Afterthought)이 아니라 플랜에 속해야 합니다. 인증 요구사항, 인가 규칙, 입력 검증 경계, 로그에 절대 나타나서는 안 되는 데이터를 기록하세요. 관찰 가능성(Observability)은 프로덕션에서 기능이 정상 작동하는지 확인하기 위해 필요한 메트릭, 로그, 또는 트레이스를 커버해야 합니다.

테스트 전략은 수용 기준과 연결되어야 합니다. 어떤 기준은 유닛 테스트, 어떤 기준은 통합 테스트, 어떤 기준은 수동 검증을 필요로 하는지 식별하세요. Go에서의 유닛 테스트 또는 Python에서의 유닛 테스트를 사용한다면, 추가할 것으로 예상되는 패키지 및 테스트 파일 이름을 명시하세요. 테스트 전략이 없는 플랜은 프로덕션에서 발견될 수밖에 없는 결함으로 배포될 운명을 지닙니다.

flowchart TB subgraph plan [Design plan contents] R[Requirements spec] C[Project constitution / ADRs] R --> D[Architecture decisions] C --> D D --> M[Data model and migrations] D --> A[API contracts] D --> S[Security constraints] D --> T[Test strategy] end

3단계 – 구현 태스크 분해(Break Down)

태스크 단계는 플랜을 독립적으로 구현, 리뷰, 검증할 수 있을 만큼 작은 슬라이스로 분해합니다. 이것이 에이전트 지원 개발을 리뷰 가능하게 만듭니다. 거대한 하나의 Diff가 아니라, 각 태스크가 명명된 요구사항에 매핑되는 집중적인 변경의 시퀀스를 얻게 됩니다.

태스크 크기 설정과 의존성

좋은 태스크는 제한된 범위의 파일만 건드리며, 하나의 에이전트 세션에서 완료되고, 검증 단계로 끝납니다. 태스크는 의존성을 명시적으로 선언해야 합니다. 마이그레이션 태스크는 새 스키마를 읽는 코드보다 먼저 실행됩니다. 공용 라이브러리 변경은 이를 소비하는 코드보다 먼저 실행되며, 인증 미들웨어 변경은 새 동작에 의존하는 엔드포인트보다 먼저 실행됩니다.

flowchart TD T1[Task 1 -- schema migration] --> T2[Task 2 -- repository layer] T2 --> T3[Task 3 -- HTTP handler] T2 --> T4[Task 4 -- metrics instrumentation] T3 --> T5[Task 5 -- integration tests] T4 --> T5

파일, 검증, 리뷰 체크포인트

각 태스크는 변경될 것으로 예상되는 파일, 충족하는 수용 기준, 완료 여부를 검증하는 방법을 나열해야 합니다. 검증은 테스트 명령어, curl 예시, 또는 복사해서 붙여넣을 수 있는 단계별로 설명된 수동 체크일 수 있습니다. 모든 태스크는 인간 리뷰 체크포인트에서 끝납니다. 리뷰어는 다음 태스크가 시작되기 전에 Diff가 태스크 설명과 일치하는지 확인합니다.

최소 태스크 항목 예시:

### Task 3 -- Add rate-limit middleware

**Depends on:** Task 1 (schema), Task 2 (repository)
**Files:** middleware/ratelimit.go, middleware/ratelimit_test.go, server.go
**Satisfies:** AC-2 (429 over limit), AC-3 (limit headers in response)
**Validate:** `go test ./middleware/...` passes; curl over limit returns 429 with Retry-After
**Review checkpoint:** Confirm middleware runs after auth, before handler

생성된 태스크의 폭발에 주의하세요. AI 에이전트는 몇 초 만에 50개짜리 플랜을 생성할 수 있습니다. 이러한 태스크의 대부분은 중복되거나 효율적인 리뷰가 불가능할 정도로 세분화되어 있습니다. 중형 규모의 기능에 유용한 태스크 목록은 50개가 아닌 보통 5개에서 15개 항목을 포함합니다.

4단계 – 태스크 하나씩 구현(Implement)

구현은 의도적으로 좁게 설정됩니다. 하나의 태스크를 선택하고, 에이전트에게 해당 태스크에만 필요한 컨텍스트를 제공한 뒤, 검증이 통과할 때까지 멈추세요. 태스크 간 컨텍스트 리셋은 버그가 아니라 기능입니다. 이전 가정들이 후속 작업을 오염시키는 것을 방지하고 Diff를 리뷰 가능하게 유지합니다.

스펙 스택으로부터 제약 조건 적용

구현 에이전트는 요구사항 스펙, 디자인 플랜, 현재 태스크 설명, 프로젝트 수준의 제약 조건을 읽어야 합니다. 제약 조건은 대부분의 팀이 건너뛰는 최고 ROI(투자 대비 효과) 섹션입니다. 이는 에이전트에게 무엇을 하지 말아야 하는지를 알려줍니다 – 관련 없는 모듈을 리팩토링하지 말 것, 이 기능 외의 공개 API 시그니처를 변경하지 말 것, 플랜을 업데이트하지 않고 새 의존성을 도입하지 말 것 등입니다.

현실이 다를 때 플랜 업데이트

구현 과정에서 예상치 못한 일이 드러납니다. 라이브러리가 가정한 동작을 지원하지 않거나, 마이그레이션이 예상보다 오래 걸리거나, 수용 기준에 누락된 엣지 케이스가 존재할 수 있습니다. 그런 일이 발생하면, 계속하기 전에 스펙을 업데이트하세요. 요구사항이나 플랜을 수정하고, 빠른 리뷰를 받은 뒤, 수정된 산출물을 바탕으로 구현을 재개하세요. 스펙에서 조용히 이탈한 코드는 드리프트가 영구화되는 경로입니다.

sequenceDiagram participant H as Human reviewer participant A as AI agent participant S as Spec artifacts H->>S: Approve task N A->>S: Read task + plan + constraints A->>A: Implement task N A->>A: Run task validation A->>H: Submit diff for review H->>H: Review diff against task alt drift or surprise H->>S: Update spec/plan H->>A: Re-run with corrected context else approved H->>S: Mark task N complete H->>A: Proceed to task N+1 end

5단계 – 스펙에 대한 검증(Validate)

검증은 SDD가 그 가치를 인정받는 곳입니다. 이 없이는 스펙은 그저 계획 운동에 불과합니다. 이것이 있으면, 스펙은 배포된 코드와 대조해 볼 수 있는 계약이 됩니다.

자동화된 체크

CI에서 전체 테스트 스위트, 린트, 타입 체크를 실행하세요. 실용적인 시작점이 필요하다면 GitHub Actions 치트시트의 패턴을 사용하여 파이프라인에 연결하세요. 자동화된 체크는 회귀를 잡아냅니다. 그러나 올바르게 구축된 잘못된 기능을 잡아내지는 못하므로, 수용 기준 리뷰가 여전히 중요합니다.

수용 기준과 수동 리뷰

요구사항 스펙의 각 수용 기준을 점검하세요. 각 항목을 충족, 실패, 지연으로 표시하고 그 이유를 적으세요. 수동 리뷰는 UX 문제, 보안 결함, 그리고 테스트가 flawed(결함이 있는) 스펙에 맞춰 작성되었기 때문에 놓친 잘못된 동작을 잡아냅니다.

스펙-코드 Diff

최종 검증 단계는 구현물을 디자인 플랜과 비교합니다. 변경된 파일들이 플랜이 예측한 파일들과 일치했습니까? 코드 내의 아키텍처 결정이 기록된 결정과 일치합니까? Diff에 있는 예상치 못한 파일은 신호입니다 – 플랜이 불완전했거나, 에이전트가 이탈했음을 의미합니다. 둘 다 머지 전에 관심을 기울여야 합니다. AI 개발에서 스펙, 테스트, 코드 동기화 유지하기는 이 일회성 Diff 리뷰를 반복 가능한 추적 테이블과 CI 체크 세트로 전환하여, 누군가 살펴볼 때만 드디어 발견되는 것이 아니라 모든 PR에서 드리프트를 잡아내게 합니다.

검증 레이어 잡아내는 대상
유닛 및 통합 테스트 범위 내 회귀 및 잘못된 로직
린트 및 타입 체크 스타일 이슈 및 타입 에러
수용 기준 점검 스펙에 맞춰 구축된 잘못된 동작
스펙-코드 Diff 아키텍처 드리프트 및 범위 팽창

워크플로우에서 AI 에이전트의 역할

AI 에이전트는 각 단계의 가속기이지, 리뷰를 대체하는 것이 아닙니다. 생산적인 패턴은 초안 작성, 리뷰, 정제, 진행입니다. 에이전트에게 문제 설명으로부터 요구사항 스펙 초안을 작성하도록 요청한 뒤, 목표, 비목표, 수용 기준이 맞을 때까지 의도를 편집하세요. 에이전트에게 승인된 요구사항으로부터 디자인 플랜 초안을 작성하도록 요청한 뒤, 코드가 존재하기 전에 아키텍처 결정을 검토하세요. 에이전트에게 태스크 슬라이스를 하나씩 구현하도록 요청하고, 다음 태스크가 시작되기 전에 각 Diff를 승인하세요.

flowchart LR subgraph human [Human owns] H1[Intent and priorities] H2[Architecture approval] H3[Diff review at checkpoints] H4[Final acceptance] end subgraph agent [Agent accelerates] A1[Draft requirements] A2[Draft design plan] A3[Generate task list] A4[Implement task slices] A5[Draft tests] end H1 --> A1 --> H1 A1 --> A2 --> H2 H2 --> A3 --> A4 --> H3 H3 --> A4 A4 --> A5 --> H4

에이전트는 첫 번째 초안과 보일러플레이트 테스트를 생성하는 데 특히 유용합니다. 인간은 잘못된 목표, 위험한 아키텍처, 미묘한 범위 팽창을 잡는 데 특히 유용합니다. 워크플로우는 한쪽이 건너뛰어지면 실패합니다 – 에이전트가 스펙 없이 구현하거나, 인간이 코드를 대조하여 검증조차 하지 않은 채 스펙을 작성할 때 말입니다.

이 워크플로우 아티클은 의도적으로 도구 중립적입니다. 도구 특화 실행 가이드 – 에디터 설정, 슬래시 명령어, 에이전트 설정 – 는 AI 개발 도구 클러스터에 속합니다. 프로세스의 핵심은 문서화 관행 아래에 있으며, 벤더보다 산출물이 더 중요하기 때문입니다.

스펙 기반 개발을 죽이는 일반적인 실수

검증 없이 거대한 스펙 작성. 프로토타입이나 스파이크(Spike) 없이 쓰인 30쪽짜리 요구사항 문서는 SDD가 아니라 워터폴 형식주의입니다. 다음 단계에 대한 모호성을 제거하는 최소한의 스펙을 작성한 뒤, 가정을 조기에 검증하세요. 모든 기능이 5단계 루프 전체를 필요로 하는 것은 아닙니다 – 스펙 기반 개발 vs 바이브 코딩에서는 더 가벼운 구조가 충분한 시점을 설명합니다.

모호한 수용 기준. “빠른”, “깔끔한”, “사용자가 친근한"과 같은 형용사는 수용 기준이 아닙니다. 측정 가능한 동작으로 교체하세요. 테스트할 수 없다면, AI 에이전트를 사용할 때 특히 신뢰할 수 있게 구현할 수 없습니다.

비목표 누락. 비목표가 없으면, 에이전트는 기본적으로 범위를 확장합니다. 캐싱 레이어를 추가하고, 인접 모듈을 리팩토링하며, 요청하지 않은 의존성을 도입합니다. 비목표는 사전에 ‘아니오’라고 말하는 방법입니다.

디자인 단계에 테스트 계획 누락. 구현 후에만 작성된 테스트는 의도했던 것이 아니라 구축된 것을 확인하는 경향이 있습니다. 플랜은 첫 번째 프로덕션 파일이 변경되기 전에, 어떤 수용 기준이 어떤 테스트 유형에 매핑되는지 명시해야 합니다.

단계 경계에서 리뷰 건너뛰기. 스펙은 플랜 전에, 플랜은 태스크 전에, 태스크는 구현 전에 리뷰되어야 합니다. 각 게이트는 비용이 적게 듭니다. 대규모 머지 후 드리프트를 수정하는 것은 비용이 큽니다.

생성된 태스크의 폭발 허용. AI가 생성한 50개 항목의 태스크 목록을 일정표가 아닌 첫 번째 초안으로 취급하세요. 중복된 항목을 병합하고, 과도하게 큰 항목을 분할하며, 요구사항에 매핑되지 않는 태스크를 삭제하세요.

거절된 조사 기록 삭제. 2단계 리뷰에서 특정 방향이 구축할 가치가 없다고 결론 내렸을 때, 반사적으로 스펙을 삭제하고 넘어가려 합니다. 이는 추론 과정을 지우고, 같은 아이디어가 다음 분기에 제로에서 다시 조사되도록 만듭니다(누가 – 사람이나 에이전트 – 다시 이를 마주하든). 거절된 결정을 승인된 결정과 동일한 엄밀성으로 기록하는 것은 비교할 수 없을 정도로 저렴하며, OpenSpec 거절된 제안: 결정 기억 규약은 재제안하기 전에 에이전트가 이전 결정을 검색하게 하는 지시문을 포함하여 이를 수행하는 구체적인 방법을 다룹니다.

SDD는 각 단계가 모호성을 줄일 때 작동합니다. 서류 작업만 생성할 때 실패합니다.

재사용 템플릿

이것들을 저장소에 복사하여 적응시키세요. 스펙을 기능 브랜치와 함께 저장하고, 풀 리퀘스트에서 리뷰하며, 에이전트와 인간이 동일한 소스를 읽도록 버전 관리에 보관하세요.

요구사항 템플릿

# Feature -- [name]

## Problem
## Users
## Goals
## Non-goals
## Acceptance criteria
## Open questions

디자인 템플릿

# Design -- [feature name]

## Summary
## Affected modules
## Data model changes
## API contracts
## Migrations
## Security
## Observability
## Test strategy
## Risks and mitigations

태스크 목록 템플릿

# Tasks -- [feature name]

## Task 1 -- [title]
Depends on:
Files:
Satisfies:
Validate:
Review checkpoint:

## Task 2 -- [title]
...

검증 체크리스트

# Validation -- [feature name]

## Automated
- [ ] All tests pass
- [ ] Lint clean
- [ ] Type check clean

## Acceptance criteria
- [ ] AC-1 --
- [ ] AC-2 --

## Spec-to-code
- [ ] Changed files match plan
- [ ] No undocumented architectural changes
- [ ] Spec updated if implementation differed

결론

스펙 기반 개발은 더 많은 문서를 작성하는 것에 대해 말하는 것이 아닙니다. 이는 각 단계에서 리뷰 게이트를 두면서 명세화, 플랜, 태스크, 구현, 검증을 순차적으로 거치는 것입니다. 각 단계는 다음 행위자 – 사람이나 에이전트 – 에게 이전 단계보다 추측이 덜 남도록 남겨야 합니다.

작게 시작하세요. 중간 규모의 기능 하나에 전체 워크플로우를 실행해보세요. 산출물을 저장소 내 마크다운으로 유지하세요. 현실이 이탈할 때 스펙을 업데이트하세요. 머지 전에 검증하세요. 체인이 작동하면, 드리프트가 줄어들고, 리뷰 가능 Diff가 작아지며, 세션 리셋과 팀 인수를 견디는 내구성 있는 의도의 기록을 얻게 됩니다.

체인이 서류 작업이 될 때, 범위를 줄이세요 – 리뷰를 줄이는 것이 아닙니다. 검증된 2쪽짜리 스펙은 아무도 읽지 않는 30쪽짜리 스펙보다 낫습니다.

유용한 링크

구독하기

시스템, 인프라, AI 엔지니어링에 관한 새 글을 받아보세요.