(해당 글은 SDD 기반 개발 경험을 정리 후, Claude AI를 통해 요청하여 작성된 글입니다.)
들어가며
Claude Code 같은 AI 에이전트를 본격적으로 쓰기 시작한 지 몇 달 됐는데요, 솔직히 처음엔 신세계였습니다. "이 API 만들어줘" 하면 몇 초 만에 컨트롤러부터 서비스, 레포지토리까지 한 번에 뽑아주니까요.
근데 한 두 달 지나니까 묘한 피로감이 쌓이더라고요. 머릿속에 그리던 그림과 살짝 어긋난 결과가 나오면, "아니 그게 아니라…" 하고 다시 시키게 됩니다. 그러다 보면 어느 순간 내가 무엇을 원했는지가 점점 흐릿해집니다. AI 가 짠 코드 위에 또 AI 가 코드를 덧칠하는 식이 되어버리거든요.
그래서 요즘 자주 보이는 SDD(Spec-Driven Development) 라는 접근에 호기심이 생겼습니다. 한 줄로 요약하면 "코드 짜기 전에 명세부터 명확히 글로 정리한다" 인데요. 너무 당연한 얘기 같지만, 막상 AI 에이전트 시대에 이걸 실천하는 게 생각보다 어렵습니다.
가볍게 검증해보려고 익숙한 도메인으로 시도해봤습니다. Spring Boot 기반 할일 관리 API. 이 글은 그 한 시간 반의 기록입니다.
SDD 가 뭐냐면
기존 AI 코딩 워크플로우는 이런 모양입니다.
프롬프트 → (블랙박스) → 코드SDD 는 이걸 풀어헤칩니다.
사양(spec) → 계획(plan) → 태스크(tasks) → 구현(code)
↑ ↑ ↑ ↑
사람 검토 사람 검토 사람 검토 사람 검토각 단계마다 사람이 검토할 수 있는 마크다운 산출물이 남고, 다음 단계는 그 산출물을 근거로 진행됩니다. "AI 야 알아서 해줘"의 반대편에 있는 접근이라고 보면 됩니다.
GitHub 이 만든 Spec Kit 이라는 도구가 이 흐름을 도와줍니다. Claude Code 와 통합되면 슬래시 커맨드 / Skill 형태로 동작해서 꽤 매끄럽습니다.
환경 셋업
# 1. Spec Kit 설치 (uv 사용)
uv tool install specify-cli
# 2. 빈 프로젝트에서 초기화
mkdir todo-api && cd todo-api
git init
specify init --here --integration claude초기화하면 두 가지가 생깁니다.
.specify/— 템플릿, 스크립트, 메모리(원칙).claude/skills/speckit-*— Claude Code 가 호출할 명령들
첫 함정 — 명령어 이름이 다릅니다
처음에 /specify, /plan 으로 호출했더니 안 먹어서 한참 헤맸습니다. 알고 보니 Spec Kit 0.8.x 부터 Claude 통합은 Skills 형태가 기본이고, 그래서 명령어 이름이 전부 speckit- 접두사가 붙은 형태로 바뀌었더라고요.
/specify → /speckit-specify
/plan → /speckit-plan
/tasks → /speckit-tasks
...작은 함정이지만 미리 알면 좋습니다.
워크플로우 한눈에
| 단계 | 명령 | 산출물 | 핵심 질문 |
|---|---|---|---|
| 0 | /speckit-constitution |
.specify/memory/constitution.md |
우리 팀의 원칙은? |
| 1 | /speckit-specify |
specs/NNN-name/spec.md |
무엇을 만들 건가? |
| 2 | /speckit-clarify |
spec 보강 | 뭘 빠뜨렸지? |
| 3 | /speckit-plan |
specs/NNN-name/plan.md |
어떻게 만들 건가? |
| 4 | /speckit-tasks |
specs/NNN-name/tasks.md |
일을 어떻게 쪼갤까? |
| 5 | /speckit-implement |
실제 소스 코드 | (실행) |
실습 — 할일 관리 API 만들기
4-1. Constitution: 프로젝트 원칙 박기
가장 먼저 프로젝트의 번복하기 어려운 큰 결정들을 박아둡니다. 이 원칙들이 이후 모든 단계의 판단 기준이 됩니다.
제가 입력한 프롬프트는 이랬습니다.
/speckit-constitution
이 프로젝트의 원칙입니다.
1. 런타임/언어
- Java 21, Spring Boot 3.2
- 빌드: Gradle (Groovy DSL)
2. 데이터
- 운영 DB: MySQL 8
- 로컬/테스트: H2
- 스키마 마이그레이션: Flyway 만 사용. JPA ddl-auto 는 절대 금지
3. 테스트
- JUnit 5 + AssertJ
- 통합 테스트는 Testcontainers 로 실제 MySQL 띄워서 검증
- 도메인 계층은 100% 단위 테스트
4. 의존성
- 추가 라이브러리는 PR 리뷰 필수
- Lombok 은 사용 (이미 팀 표준)
5. 아키텍처
- Hexagonal-lite: domain 계층은 Spring/JPA 어노테이션 직접 사용 금지
- 패키지: com.example.todo.{web, application, domain, infra}처음엔 이것보다 훨씬 더 많이 적었습니다. "PR 제목은 conventional commits, 메서드 이름은 동사로 시작…" 같은 디테일까지요. 적다가 손을 멈췄습니다. 이건 코딩 컨벤션이지 constitution 이 아니다 싶었거든요.
그래서 다시 정리했습니다. constitution 에는 "지키지 않으면 프로젝트의 정체성이 흔들리는 것"만 적어야겠더라고요. 코드 스타일 같은 건 별도 가이드 문서가 맞습니다.
생성된 .specify/memory/constitution.md 일부를 옮겨오면 이렇게 됩니다.
# Project Constitution
## Core Principles
### Principle 1: Migration via Flyway Only
JPA `ddl-auto` MUST be set to `validate` in all environments.
Schema changes MUST be applied through Flyway migration scripts under
`src/main/resources/db/migration/`.
**Rationale**: 운영 사고의 단골 원인. 스키마 변경 이력을 추적 가능한 단일
경로로 강제한다.
### Principle 2: Hexagonal-Lite Layering
Classes under `com.example.todo.domain` MUST NOT import any class from
`org.springframework.*` or `jakarta.persistence.*`.
**Rationale**: 도메인을 프레임워크로부터 분리해 단위 테스트 속도와
명세 가독성을 확보한다.
...특히 좋았던 건 각 원칙마다 Rationale(근거) 가 자동으로 붙어 있다는 점입니다. 나중에 누가 "왜 이렇게 했죠?" 라고 물으면 보여주기만 하면 됩니다.
4-2. Specify: 자연어로 사양 만들기
이제 무엇을 만들지 자연어로 설명합니다. 한 번에 너무 욕심내지 말고 핵심 기능 + 명확한 제약 정도로 적는 게 좋더라고요.
/speckit-specify
사내 팀용 할일 관리 REST API 를 만듭니다.
대상 사용자
- 인증된 사내 사용자 (게이트웨이가 X-USER-ID 헤더를 보장한다고 가정)
기능
- 할 일 추가 (POST /todos)
- 내 할 일 목록 조회 (GET /todos) — 페이지네이션
- 할 일 단건 조회 (GET /todos/{id})
- 할 일 상태 변경 (PATCH /todos/{id}) — 완료 여부 변경
- 할 일 삭제 (DELETE /todos/{id}) — hard delete
비기능 요구
- 응답은 모두 JSON, REST 컨벤션
- 페이지네이션: 기본 size 20, 최대 100
- 같은 사용자가 동일 텍스트로 5분 이내 재요청 시 409 Conflict
- 다른 사용자의 todo 는 절대 조회/수정 불가 (403)30초쯤 지나니까 specs/001-todo-api/spec.md 가 만들어졌습니다. 내용이 꽤 길어서 다 옮길 수는 없고, 인상적이었던 부분만 발췌하면 이런 식이었습니다.
# Feature Specification: Personal Todo Management API
## User Scenarios & Testing
### Primary User Story
인증된 사내 사용자는 자신의 할 일을 시스템에 등록하고, 진행 상황을
관리하며, 완료된 항목을 정리할 수 있다. 다른 사용자의 데이터는
어떤 방식으로도 접근할 수 없다.
### Acceptance Scenarios
- AS-1: 유효한 텍스트로 POST /todos 요청 → 201 Created + Location 헤더
- AS-2: 동일 사용자가 동일 텍스트로 5분 이내 재요청 → 409 Conflict
- AS-3: 다른 사용자의 todo 에 PATCH 요청 → 403 Forbidden
- AS-4: 페이지 size 가 100 초과 → 400 Bad Request
- AS-5: 존재하지 않는 id 로 조회 → 404 Not Found
## Functional Requirements
- FR-001: POST /todos 는 텍스트 필드 필수, 1~200자
[NEEDS CLARIFICATION: 최대 길이 200자가 적절한지]
- FR-002: 응답 본문은 항상 { "id", "text", "completed", "createdAt", "updatedAt" }
- FR-003: 페이지네이션은 page (0-based), size 쿼리 파라미터
- FR-004: 중복 판단은 (userId, text, 최근 5분) 으로 결정
[NEEDS CLARIFICATION: 텍스트 정규화 규칙 - 공백/대소문자 처리]
- ...
## Edge Cases
- 매우 긴 텍스트 (200자 초과)
- 페이지 size 가 음수
- 동시성: 같은 사용자가 동일 요청을 정확히 동시에 두 번 보낼 때
- DB 가 일시적으로 응답 안 할 때
- ...
## Key Entities
### Todo
- id: Long (DB 자동생성)
- userId: String (X-USER-ID 헤더 값)
- text: String (1~200자)
- completed: Boolean (기본 false)
- createdAt: Instant
- updatedAt: Instant여기서 신기했던 건 제가 명시적으로 안 적은 엣지 케이스를 알아서 떠올려준다는 점이었습니다. "동시에 두 번 요청이 오면?" 같은 거요. 그리고 모호한 부분에는 [NEEDS CLARIFICATION] 마커를 박아둡니다.
4-3. Clarify: 빈틈 채우기
이게 SDD 의 진짜 매력이라고 느낀 단계입니다.
/speckit-clarify명령은 9가지 관점(기능 범위 / 데이터 모델 / UX 흐름 / 비기능 / 외부 연동 / 엣지 케이스 / 제약 / 용어 / 완료 기준)으로 spec 을 자동 스캔해서, 영향이 큰 빈틈에 대해 최대 5개 질문을 던집니다.
실제로 받은 첫 질문은 이거였습니다.
Q1: 중복 판단 시 텍스트 정규화 규칙은?
Recommended: B — 일반적인 사용자 의도 매칭에 충분, 과하지 않음
Option Description A 완전 일치만 (공백·대소문자 그대로 비교) B 앞뒤 공백 제거 + 다중 공백 단일화 후 비교 C 위 + 소문자 변환 후 비교 D 위 + 한국어 정규화(NFC) 까지 Short 직접 입력
저는 그냥 yes 라고 답했습니다 (추천안 수락). 그러자 spec 의 FR-004 가 자동으로 이렇게 갱신됐습니다.
- FR-004: 중복 판단은 (userId, text, 최근 5분) 으로 결정
- [NEEDS CLARIFICATION: 텍스트 정규화 규칙 - 공백/대소문자 처리]
+ FR-004: 중복 판단은 (userId, normalizedText, 최근 5분) 으로 결정한다.
+ FR-004a: normalizedText = text.trim() 후 연속 공백을 단일 공백으로 치환한 값.
+ FR-004b: 대소문자는 구분한다 ("Buy milk" 와 "buy milk" 는 다른 todo).그리고 spec 맨 위에 ## Clarifications 섹션이 만들어져서 Q&A 가 자동 기록됩니다.
## Clarifications
### Session 2026-05-18
- Q: 중복 판단 시 텍스트 정규화 규칙? → A: trim + 다중공백 단일화, 대소문자 구분
- Q: 동시성 보장 메커니즘? → A: DB unique constraint + 409 Conflict 매핑
- Q: 페이지 정렬 기본값? → A: createdAt DESC
- Q: 완료된 항목 조회 필터 제공? → A: ?status=open|done|all (기본 all)
- Q: 삭제 후 재추가는 허용? → A: 5분 윈도우는 미삭제 항목만 카운트가장 좋았던 건요, 제가 직접 spec 을 쓸 때는 "정렬 기본값" 같은 건 아예 떠올리지도 못했다는 점입니다. 자기 글에 익숙해지면 빈틈이 안 보이는데, 도구가 9가지 관점으로 강제로 훑어주니까 "내가 무엇을 빠뜨렸는지 모르는 부분" 이 드러납니다.
이거 한 번 겪어보고 나서는, 다음에 PR 올릴 때도 "spec 부터 짚고 clarify 한 번 돌렸나?" 가 자문이 되더라고요.
4-4. Plan: HOW 의 영역
이제 어떻게 만들지를 정합니다.
/speckit-plan
구현 환경은 다음과 같이 갑니다.
- Spring Boot 3.2 + Spring Web MVC (WebFlux 아님)
- 영속성: Spring Data JPA + Hibernate, MySQL 8
- 마이그레이션: Flyway
- 검증: Jakarta Bean Validation (@Valid + @NotBlank, @Size 등)
- 멱등성/중복 차단: DB unique constraint (user_id, normalized_text, window_start)
- 통합 테스트: Testcontainers + MySQL생성된 plan.md 의 일부를 옮기면 이렇습니다.
# Implementation Plan: Personal Todo Management API
## Technology Stack
- Java 21, Spring Boot 3.2.5
- Build: Gradle (Groovy DSL)
- Web: Spring Web MVC, Jackson
- ORM: Spring Data JPA, Hibernate
- DB: MySQL 8 (prod), H2 (local devtools), Testcontainers MySQL (test)
- Migration: Flyway
- Validation: Jakarta Bean Validation
- Test: JUnit 5, AssertJ, MockMvc, Testcontainers
## Architecture (Hexagonal-Lite)com.example.todo
├── web # @RestController, DTO, exception handler
├── application # Use case service (orchestration)
├── domain # POJO 도메인 + 포트 인터페이스
└── infra
└── persistence # JPA 엔티티 + Repository 구현 (도메인 ↔ 엔티티 변환)
도메인 계층(`domain`)은 Spring 과 JPA 어노테이션을 직접 import 하지 않는다.
JPA 엔티티는 `infra.persistence` 에 별도 클래스로 둔다.
## Data Schema (Flyway V1)
```sql
CREATE TABLE todo (
id BIGINT NOT NULL AUTO_INCREMENT,
user_id VARCHAR(64) NOT NULL,
text VARCHAR(200) NOT NULL,
normalized_text VARCHAR(200) NOT NULL,
completed BOOLEAN NOT NULL DEFAULT FALSE,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NOT NULL,
PRIMARY KEY (id),
INDEX idx_todo_user_created (user_id, created_at DESC)
);
CREATE TABLE todo_dedup_window (
user_id VARCHAR(64) NOT NULL,
normalized_text VARCHAR(200) NOT NULL,
window_start_5m DATETIME(6) NOT NULL,
todo_id BIGINT NOT NULL,
PRIMARY KEY (user_id, normalized_text, window_start_5m)
);API Endpoints
| Method | Path | Body | Response |
|---|---|---|---|
| POST | /todos | { text } | 201, Location, TodoDto |
| GET | /todos | (q: page, size, status) | 200, Page |
| GET | /todos/{id} | - | 200, TodoDto |
| PATCH | /todos/{id} | { completed } | 200, TodoDto |
| DELETE | /todos/{id} | - | 204 |
Constitution Check
- ✅ Flyway only: ddl-auto=validate, V1 마이그레이션 동반
- ✅ Hexagonal-lite: domain 에 Spring 의존성 없음, JPA 엔티티는 infra 에 위치
- ✅ Test: 통합 테스트는 Testcontainers MySQL 사용
- ✅ Validation: web 계층에서 @Valid 로 일관 처리
여기서 ## Constitution Check 섹션이 인상적이었습니다. 각 결정이 constitution 의 어떤 원칙과 부합하는지를 자동으로 검증합니다. 만약 제가 "엔티티에서 직접 @Entity 쓰고 도메인 따로 안 두자" 라고 적었다면 "Hexagonal-Lite 원칙 위반" 이라고 잡아냈을 거예요.
4-5. Tasks: 일을 잘게 쪼개기
/speckit-taskstasks.md 가 만들어집니다. 의존성과 검증 방법까지 같이 적어줍니다.
# Tasks: Personal Todo Management API
## T001: 프로젝트 셋업
- Gradle 멀티모듈 아닌 단일 모듈로 생성
- 의존성: spring-boot-starter-web, -data-jpa, -validation, flyway-core,
mysql-connector-j, h2 (testRuntimeOnly), testcontainers-mysql
- application.yml: ddl-auto=validate, flyway.enabled=true
- 검증: `./gradlew bootRun` 정상 기동
## T002: Flyway 마이그레이션 V1 [의존: T001]
- src/main/resources/db/migration/V1__init.sql 작성
- todo, todo_dedup_window 테이블
- 검증: 부팅 시 Flyway 가 V1 적용 + 재기동 시 idempotent
## T003: 도메인 모델 [의존: T001]
- domain.Todo (POJO, Lombok 사용)
- domain.TodoRepository (port 인터페이스)
- domain.TodoNormalizer (정적 함수)
- 단위 테스트: TodoNormalizerTest (trim/다중공백/대소문자 케이스)
- 검증: domain 패키지에 spring/jakarta import 0건 (테스트로 강제)
## T004: JPA 어댑터 [의존: T002, T003]
- infra.persistence.TodoEntity (@Entity)
- infra.persistence.TodoJpaRepository (Spring Data)
- infra.persistence.TodoRepositoryAdapter (port 구현, Entity ↔ Domain 변환)
- 검증: @DataJpaTest 로 round-trip 테스트
## T005: 애플리케이션 서비스 [의존: T003]
- application.TodoService
- add(userId, text): 정규화 → dedup 체크 → 저장
- list(userId, page, size, status): 페이지 조회
- patch(userId, id, completed): 소유자 검증 → 갱신
- delete(userId, id): 소유자 검증 → 삭제
- 단위 테스트: in-memory fake repository 사용
- 검증: 모든 시나리오 (AS-1 ~ AS-5) 커버
## T006: REST 컨트롤러 [의존: T005]
- web.TodoController
- web.TodoDto, AddTodoRequest, PatchTodoRequest
- web.GlobalExceptionHandler (DuplicateTodoException → 409 매핑 등)
- @WebMvcTest 로 컨트롤러 단위 테스트
- 검증: 모든 엔드포인트 상태코드 정확
## T007: 통합 테스트 [의존: T006]
- Testcontainers MySQL 로 전체 시나리오 검증
- 동시성 케이스: 같은 요청 동시 2건 → 1건만 성공, 1건은 409
- 검증: @SpringBootTest 로 전 시나리오 통과태스크가 7개로 깔끔하게 나뉘었고, [의존: TNNN] 표시 덕분에 어디서 멈춰도 되는지가 명확합니다.
4-6. Implement: 드디어 코드
/speckit-implement명령은 tasks.md 를 위에서부터 순서대로 실행합니다. 각 태스크가 끝날 때마다 체크박스가 갱신되고, 검증 단계가 통과해야 다음으로 넘어갑니다.
T003 의 결과로 만들어진 TodoNormalizer.java 는 이렇게 나왔습니다.
package com.example.todo.domain;
public final class TodoNormalizer {
private TodoNormalizer() {}
public static String normalize(String text) {
if (text == null) {
throw new IllegalArgumentException("text must not be null");
}
// FR-004a: trim + 다중 공백 단일화. 대소문자는 보존 (FR-004b).
return text.trim().replaceAll("\\s+", " ");
}
}T005 의 TodoService#add 일부.
@Transactional
public Todo add(String userId, String rawText) {
String text = rawText.trim();
if (text.isEmpty() || text.length() > 200) {
throw new InvalidTodoTextException(text);
}
String normalized = TodoNormalizer.normalize(text);
Instant now = clock.instant();
Instant windowStart = truncateToFiveMinute(now);
// FR-004: 5분 윈도우 안에서 동일 (userId, normalized) 가 있으면 거부.
boolean duplicate = repository.existsInDedupWindow(userId, normalized, windowStart);
if (duplicate) {
throw new DuplicateTodoException(userId, normalized);
}
Todo todo = Todo.newOne(userId, text, normalized, now);
return repository.save(todo, windowStart);
}200자 제한, 정규화 규칙, 5분 dedup 윈도우 — spec/clarify 단계에서 결정된 규칙들이 코드에 그대로 녹아 있습니다. 제가 "200자 체크 넣어줘" 라고 한 번도 말하지 않았는데도요.
중간에 멈춘 적도 있었습니다. T006 에서 만들어진 exception handler 가 너무 장황해서 "예외 메시지 본문은 RFC 7807 Problem Details 형식으로 통일해줘" 라고 추가 지시를 했더니, 그 태스크만 다시 작업하고 다른 코드는 건드리지 않았습니다. 이게 SDD 의 단계 분리 덕분이라는 게 체감되더라고요.
5. 결과 정리
- 소스 파일: 18개
- 코드 라인 수: 약 720줄 (테스트 포함)
- 테스트: 단위 14개 + 통합 6개 = 20개, 전부 통과
- 처음부터 끝까지 걸린 시간: 약 1시간 25분
순수하게 "Claude Code 야 할일 API 만들어줘" 로 했다면 아마 20분이면 끝났을 작업입니다. 그래서 대략 4배 느렸습니다. 하지만 결과물의 성격이 달랐어요. 산출물에 spec/plan/tasks 가 함께 따라왔고, 각 결정의 근거가 문서로 남았고, 도메인-인프라 분리 같은 원칙이 처음부터 지켜졌습니다.
6. 직접 해보고 느낀 점
좋았던 점
1. "AI 가 또 이상한 거 만들었네" 가 없어집니다.
원하던 것과 어긋난 결과가 한 번도 없었습니다. spec 안에 있는 것만, spec 안에 있는 방식대로 만들어지더라고요. 처음엔 "이게 진짜 되네?" 싶었습니다.
2. 단계마다 끊을 수 있습니다.
spec 이 마음에 안 들면 거기서 끊고 다시 짤 수 있고, plan 이 어색하면 거기서 잡을 수 있습니다. 코드까지 가서야 "어 이게 아닌데" 가 되는 일이 없습니다.
3. clarify 가 신박합니다.
직접 spec 을 쓸 때는 잘 안 떠오르는 빈틈을 강제로 짚어줍니다. "페이지 기본 정렬 뭐로 할래?" 같은 거, 저 혼자 spec 썼으면 분명히 빠뜨렸을 거예요.
4. 인수인계 문서가 공짜로 따라옵니다.
spec/plan/tasks 가 그대로 PR 디스크립션이자 설계 문서가 됩니다. 6개월 뒤에 "왜 이렇게 짰지?" 의 답이 git blame 이 아니라 spec.md 에 있어요.
5. Constitution Check 가 가드레일 역할을 합니다.
plan 단계에서 자동으로 원칙 위반을 체크합니다. 예를 들어 제가 "엔티티에 도메인 로직 다 넣자" 라고 하면 Hexagonal 원칙 위반이라고 잡아냅니다. 사람이 코드 리뷰로 잡던 걸 도구가 먼저 해주는 셈이죠.
아쉬운 점
1. 오버헤드가 큽니다.
spec → plan → tasks → implement 전체를 도는 게 부담입니다. 20분이면 끝낼 일이 1시간 반이 됩니다. 작은 변경에 매번 다 돌리는 건 좀 그렇더라고요.
2. template 이 너무 빡빡합니다.
기본 template 의 모든 섹션을 다 채워야 할 것 같은 압박감이 있습니다. 실제로는 "관련 없으면 통째로 삭제" 가 권장인데, 처음엔 그걸 모르니까요.
3. clarify 가 가끔 너무 친절합니다.
1인용 토이 프로젝트에서까지 "외부 API 인증 방식은?", "관측성 SLO 는?" 같은 걸 물어봅니다. 도메인이 단순할수록 clarify 의 가성비가 떨어집니다.
4. 로컬 상태 충돌이 생길 수 있습니다.
두 터미널에서 같은 프로젝트의 다른 feature 를 병렬 작업하면 .specify/feature.json 이 꼬일 수 있습니다. 워크트리로 분리해야 안전합니다.
7. 그래서 언제 쓰면 좋을까
| 상황 | 가성비 | 한 줄 평 |
|---|---|---|
| 1인 단발 스크립트 | ✗ | spec 보다 코드가 짧을 수 있음 |
| 팀 협업, 인수인계 가능성 있음 | ✓ | 문서가 공짜로 따라옴 |
| 규칙이 많은 도메인 (정산·포인트·등급) | ✓✓ | 사람이 놓치는 엣지 케이스를 도구가 잡음 |
| 프로토타입 / 탐색 | ✗ | 어차피 버릴 거 |
| 회사·감사 대상 프로젝트 | ✓✓ | 의사결정 근거가 문서로 남음 |
| 신규 입사자 온보딩 프로젝트 | ✓ | spec 부터 읽으면 학습 곡선이 완만 |
제 결론은 이렇습니다. 도메인 규칙이 복잡할수록, 협업 인원이 많을수록, 인수인계 가능성이 높을수록 SDD 의 가성비가 좋아집니다. 반대로 혼자 빠르게 검증만 해보는 단계에선 부담이 더 큽니다.
저는 멤버십 포인트 관련 배치 도메인을 다루는데, 이쪽처럼 규칙이 빽빽하고 잘못되면 돈이 걸리는 영역에는 진짜 잘 맞을 것 같다는 인상을 받았습니다.
8. 직접 겪고 얻은 팁
- template 은 그냥 파일입니다.
.specify/templates/*.md를 우리 팀에 맞게 자유롭게 잘라 쓰세요. 모든 섹션을 채우려고 하지 마세요. - constitution 은 "번복 불가"인 것만. 코드 스타일, PR 컨벤션 같은 건 별도 가이드가 맞습니다.
- spec 을 직접 수정해도 됩니다. 단, 큰 변경을 했다면
/speckit-analyze로 plan / tasks 와의 정합성을 한 번 검사하세요. - clarify 답변은 그냥 "yes" 로도 됩니다. 추천안이 합리적이면 굳이 모든 옵션을 꼼꼼히 고를 필요 없습니다.
- 워크트리 단위로 feature 를 격리하세요. 병렬 작업 가능. 단 같은 feature 의 단계를 두 터미널에서 동시에 진행하는 건 금지(
.specify/feature.json충돌).
마치며
SDD 는 "AI 야 알아서 해줘" 의 반대편에 있는 접근입니다. 더 느리고, 더 번거롭습니다. 하지만 한 번 끝까지 돌려본 후엔, "코드를 잘 짜는 AI" 보다 "무엇을 만들지 함께 정리하는 AI" 가 진짜 필요했던 거였구나 싶었습니다.
당장 모든 작업을 SDD 로 바꿀 생각은 없습니다. 5분짜리 스크립트엔 너무 무거우니까요. 하지만 다음에 도메인이 복잡한 기능을 짤 일이 생기면 분명 다시 꺼낼 것 같습니다.
긴 글 읽어주셔서 감사합니다. 직접 해보고 싶은 분들에겐 가벼운 마음으로 한 번 끝까지 돌려보시기를 추천합니다. 한 시간 반이면 충분합니다.
관련 링크
- GitHub Spec Kit: https://github.com/github/spec-kit
- Claude Code: https://www.anthropic.com/claude-code
'AI' 카테고리의 다른 글
| [AI] LLM + spec-kit 조합의 한계 - 요구사항 누락을 AI 하네스로 줄인 방법 (0) | 2026.05.26 |
|---|