
"이 기능 구현해줘"에서 "이 스펙이 충분한지 봐줘"로 질문이 바뀌었다.
이제는 "이 스펙을 어떻게 작성하지?"로 질문이 한 단계 더 깊어졌다.
1탄에서는 왜 Spec-Driven Development를 도입하게 되었는지, 기존 개발 방식에서 느낀 문제의식을 공유했습니다. 이번 글에서는 어떻게 실제로 스펙을 작성하고 사용하는지, 프로젝트에 적용한 구체적인 방법을 공유합니다.
이 글은 교과서식 설명이 아니라, "내가 프로젝트에서 실제로 이렇게 쓰고 있다"는 실전 사례입니다. 아직 완벽하지 않고, 실수도 많지만, 그 과정을 솔직하게 기록했습니다.
Spec-Driven Development를 처음 접했을 때, 저는 "문서를 먼저 작성하는 개발 방식" 정도로 이해했습니다. 하지만 실제로는 그보다 훨씬 구조화된 프로세스였습니다.
Spec-Driven Development는:
입니다. 핵심은 "무엇을 만들까"보다 "무엇을 결정해야 할까"에 집중하는 것입니다.
Specify CLI는 Spec-Driven Development를 실천하기 위한 도구입니다. AI 코딩 에이전트(Cursor, Claude, Copilot 등)와 함께 사용할 수 있는 슬래시 명령어들을 제공합니다.
Specify CLI는 다음과 같은 핵심 명령어들을 제공합니다:
Core Commands (필수 명령어)
/speckit.constitution: 프로젝트의 개발 원칙과 가이드라인 정의/speckit.specify: 무엇을 만들 것인지 정의 (요구사항, 사용자 스토리)/speckit.plan: 선택한 기술 스택으로 기술적 구현 계획 수립/speckit.tasks: 구현을 위한 실행 가능한 태스크 목록 생성/speckit.implement: 계획에 따라 기능 구현 실행Optional Commands (선택 명령어)
/speckit.clarify: 명확하지 않은 영역을 명확히 함 (권장: /speckit.plan 전에 실행)/speckit.analyze: 아티팩트 간 일관성 및 커버리지 분석 (/speckit.tasks 후 실행)/speckit.checklist: 요구사항 완전성, 명확성, 일관성을 검증하는 체크리스트 생성프로젝트에 Specify CLI를 적용하려면 먼저 초기화해야 합니다:
# 기본 프로젝트 초기화
specify init my-project
# 특정 AI 에이전트와 함께 초기화 (예: Cursor)
specify init my-project --ai cursor-agent
# 현재 디렉토리에 초기화
specify init . --ai cursor-agent
초기화하면 .specify/ 디렉토리가 생성되고, AI 에이전트가 사용할 수 있는 슬래시 명령어들이 설정됩니다.
Spec-Driven Development는 다음과 같은 철학을 따릅니다:
이 철학은 단순히 "문서를 먼저 작성하는 것"이 아니라, 설계 사고를 구조화하는 것입니다.
처음에는 "설계"라고 하면 화면 디자인이나 코드 구조를 떠올렸습니다. 하지만 Spec-Driven Development에서 말하는 "설계"는 그보다 훨씬 앞선 단계입니다.
설계는 "무엇을 결정해야 할까"를 정리하는 것입니다.
예를 들어, "로그인 기능"을 만들 때:
설계는 결정 사항을 명확하게 정리하는 과정입니다.
가장 큰 변화는 질문의 방향이 바뀐 것입니다.
이전:
이후:
이런 질문들을 먼저 정리하고, 스펙에 기록합니다. 그러면 구현 단계에서 "아, 이건 어떻게 해야 하지?"라는 상황이 줄어듭니다.
스펙을 기준점으로 삼은 이유는 의사결정의 일관성을 유지하기 위해서입니다.
이전에는:
이후에는:
스펙이 의사결정의 기준점이 되었습니다.
Specify CLI는 명시적으로 "Epic → Feature → User Story" 구조를 강제하지는 않습니다. 하지만 프로젝트를 진행하면서 자연스럽게 이런 구조가 필요하다고 느꼈습니다.
Epic (에픽)
Feature (기능)
User Story (사용자 스토리)
이 구조를 선택한 이유는 우선순위를 명확하게 나눌 수 있기 때문입니다.
현재 진행 중인 Worship Flow 프로젝트에서 실제로 사용한 구조입니다.
"예배 준비를 위한 콘티 관리 시스템"
specs/001-auth-dashboard-editor/ 디렉토리로 관리:
### User Story 1 - Kakao 로그인 및 대시보드 콘티 생성 (Priority: P1)
Kakao 계정으로 로그인하고 자동 프로필 동기 후, 콘티를 제목/주제말씀/날짜로 생성하며
생성된 콘티 목록을 즉시 확인한다.
**Why this priority**: 인증과 기본 콘티 생성이 모든 이후 흐름의 전제이므로 최우선.
**Independent Test**: 신규 계정으로 로그인 → 콘티 생성 → 목록에 표시 여부만으로 독립 검증 가능.
**Acceptance Scenarios**:
1. **Given** Kakao 로그인 화면에서 동의 후, **When** 리디렉트되면,
**Then** 사용자 정보가 동기화되고 세션이 활성화된다.
2. **Given** 로그인된 상태, **When** 제목/주제말씀/날짜를 입력해 콘티를 생성하면,
**Then** 대시보드 목록 상단에 새 콘티가 보인다.
이렇게 구조화하면서, "지금 무엇을 구현해야 하는가"가 명확해졌습니다.
스펙을 작성하면서 가장 어려웠던 부분은 적정한 수준을 찾는 것이었습니다.
처음에는 모든 것을 상세하게 적으려고 했습니다:
### 로그인 기능
- Kakao OAuth 2.0 사용
- authorization code flow
- 서버 사이드 콜백에서 세션 토큰 발급
- 서명 쿠키 사용
- 클라이언트에서는 TanStack Query로 세션 상태 조회
- 세션 만료 시간: 7일
- 리프레시 토큰: 사용 안 함
- ...
이렇게 적으니 스펙이 너무 길어지고, 구현 세부사항이 스펙에 섞여 들어갔습니다. 스펙은 "무엇"을 정의해야 하는데, "어떻게"를 너무 자세히 적은 것입니다.
반대로 너무 추상적으로 적은 경우도 있었습니다:
### 로그인 기능
- 사용자가 로그인할 수 있어야 함
- 로그인 후 대시보드로 이동
이렇게 적으니 구현할 때마다 결정이 필요했습니다. "로그인 실패는 어떻게 처리하지?" "세션은 어떻게 관리하지?" 같은 질문들이 계속 생겼습니다.
적정한 스펙 수준을 찾기 위해 다음과 같은 기준을 세웠습니다:
"무엇"을 명확하게 정의
"왜"를 기록
research.md에 기록"어떻게"는 최소한만
plan.md에, 구현 세부사항은 코드에엣지 케이스는 명시
이렇게 나누면서, 스펙의 역할이 명확해졌습니다: 의사결정의 기준점.
AI를 사용할 때 가장 큰 변화는 명령어의 목적이 바뀐 것입니다.
"로그인 기능 구현해줘"
"대시보드 목록 컴포넌트 만들어줘"
"API 엔드포인트 작성해줘"
이런 명령은 "구현"을 요청하는 것입니다. AI가 코드를 생성하고, 우리는 그것을 검토하고 수정합니다.
"/speckit.specify 로그인 기능이 필요해요. Kakao OAuth를 사용하고,
실패 시 재시도 동선을 제공해야 해요."
"/speckit.clarify 이 스펙에서 로그인 실패 케이스가 충분히 다뤄졌는지 봐줘"
"/speckit.plan 이 스펙을 Next.js App Router로 구현할 계획을 세워줘"
이런 명령은 "설계"를 요청하는 것입니다. AI가 스펙을 생성하거나 검토하고, 우리는 그것을 바탕으로 결정을 내립니다.
프로젝트를 진행하면서 자주 사용하는 프롬프트 유형들:
"/speckit.specify {기능 설명}
요구사항:
- {요구사항 1}
- {요구사항 2}
제약사항:
- {제약사항 1}
- {제약사항 2}
"
목적: 자연어로 된 요구사항을 구조화된 스펙으로 변환
"/speckit.clarify 이 스펙에서 {특정 영역}이 충분히 명확한지 봐줘.
특히 {구체적 질문}에 대한 답이 있는지 확인해줘."
목적: 스펙의 모호한 부분을 명확히 함
"/speckit.plan 이 스펙을 {기술 스택}으로 구현할 계획을 세워줘.
특히 {고려사항}을 반영해줘."
목적: 스펙을 바탕으로 기술적 구현 계획 수립
"/speckit.tasks 이 계획을 바탕으로 구현 태스크를 생성해줘.
각 User Story가 독립적으로 테스트 가능하도록 나눠줘."
목적: 구현 계획을 실행 가능한 태스크로 분해
프롬프트를 작성하는 과정 자체가 사고를 정리하는 과정입니다.
예를 들어, "/speckit.specify 로그인 기능"이라고 입력하려면:
이런 질문들을 먼저 생각해야 합니다. 그리고 프롬프트를 작성하면서 자신의 사고가 정리됩니다.
특히 AI가 생성한 스펙을 검토할 때:
이런 질문들을 AI에게 물어보면서, 스펙의 품질을 높일 수 있었습니다.
가장 어려웠던 부분은 "스펙이 어느 정도면 충분한가?"를 판단하는 것이었습니다.
처음에는 "스펙이 완벽해야 구현을 시작할 수 있다"고 생각했습니다. 하지만 실제로는:
그래서 다음과 같은 기준을 세웠습니다:
핵심 User Story가 명확하게 정의되어 있다
주요 결정 사항이 기록되어 있다
research.md)data-model.md)contracts/openapi.yaml)엣지 케이스가 최소한 명시되어 있다
구현 계획(plan.md)과 태스크(tasks.md)가 있다
이 기준을 만족하면 구현을 시작했습니다.
"충분한 스펙"의 기준은 "구현 중간에 '어떻게 해야 하지?'라는 상황이 최소화되는 것"이었습니다.
처음에는 스펙이 부족해서:
이후에는 스펙이 충분해서:
이렇게 변화했습니다.
가장 인상 깊었던 경험은 일부러 구현을 멈추고 스펙을 다시 작성한 것입니다.
처음에는 "로그인 기능" 스펙을 작성하고 바로 구현을 시작했습니다. 하지만 구현 중간에:
이런 질문들이 계속 생겼습니다.
그래서 구현을 멈추고 스펙을 다시 작성했습니다:
이렇게 하니 구현이 훨씬 수월해졌습니다. "일부러 멈추는 것"이 오히려 빠르게 진행하는 방법이었습니다.
솔직히 말하면, Spec-Driven Development를 도입하면서 개발 속도가 느려졌습니다.
특히 처음에는 스펙 작성 자체가 익숙하지 않아서, "이렇게 적는 게 맞나?"라는 고민에 시간을 많이 썼습니다.
하지만 시간이 지나면서:
아직 혼자 진행하는 프로젝트라서 팀 확장 시 문제는 경험하지 못했지만, 예상되는 문제들:
스펙 작성 방식의 일관성
스펙과 코드의 동기화
학습 곡선
이런 문제들을 해결하기 위해서는:
이런 것들이 필요할 것 같습니다.
아직 완벽하지 않고, 여전히 답을 찾는 부분들:
스펙의 적정 수준
스펙과 코드의 동기화
스펙 작성 시간
이런 질문들에 대한 답은 아직 찾는 중입니다. 하지만 "완벽하지 않아도 기록하고 공유하는 것"이 중요하다고 생각합니다.
Spec-Driven Development를 실전에 적용하면서, 저는 "설계를 생각하는 습관"을 배우고 있습니다.
아직 완벽하지 않고, 실수도 많습니다. 하지만 이 과정 자체가 설계를 배우는 연습이었고, 이 연습이 주니어 개발자로서 성장하는 데 도움이 되고 있습니다.
특히 AI를 설계 파트너로 활용하면서, 프롬프트를 작성하는 과정이 사고를 정리하는 과정이 되었습니다. "이 기능 구현해줘"에서 "이 스펙이 충분한지 봐줘"로 질문이 바뀌면서, 개발 방식 자체가 변화했습니다.
다음 글에서는 스펙을 코드로 옮기는 과정과 변경 관리에 대해 더 자세히 공유하려고 합니다.