Home [AI 기초] SDD (Spec-Driven Development) - 스펙 주도 개발 방법론
Post
Cancel

[AI 기초] SDD (Spec-Driven Development) - 스펙 주도 개발 방법론

1. SDD란 무엇인가

정의

SDD(Spec-Driven Development, 스펙 주도 개발)는 명세(Spec)를 완벽하게 작성하면, AI가 구현(Implementation)을 담당하는 개발 방법론.

  • 핵심 원리: 자연어 명세(Markdown)와 테스트 시나리오를 먼저 작성 → AI에게 구현 위임
  • 비유: PM(인간)과 시니어 개발자(AI)의 협업
    • 인간: 요구사항 정의서(PRD)와 인터페이스 설계 담당
    • AI: 명세 기반 코드 생성 담당

개발자 비유: Interface 우선 설계

Java에서 Interface를 먼저 설계하고 Implementation을 나중에 하는 것과 동일.

1
2
3
4
5
6
7
8
9
10
// Step 1: Interface 정의 (명세)
public interface UserService {
    Long createUser(String name, String email, UserRole role);
    User getUser(Long userId);
}

// Step 2: Implementation (구현) - AI가 담당
public class UserServiceImpl implements UserService {
    // AI가 명세 기반으로 자동 생성
}

SDD 구조:

  • Interface = spec.md (명세 문서)
  • Implementation = AI가 생성하는 코드
  • Javadoc = 상세한 요구사항

2. Why SDD? (기존 TDD와의 관계)

TDD vs SDD 비교

구분TDDSDD
순서테스트를 먼저 짜고 구현자연어 명세와 테스트 시나리오를 먼저 짜고, AI에게 구현 맡김
초점테스트 코드 작성비즈니스 로직과 아키텍처 설계
장점구현 디테일(Syntax)보다 비즈니스 로직과 아키텍처에 집중 가능 

TDD의 한계

TDD는 “무엇을 테스트할지”는 알려주지만, “어떤 구조로 짜야 할지”는 알려주지 않음.

1
2
3
4
5
6
7
8
9
10
11
12
// TDD: 테스트만 작성
@Test
void 사용자_생성_테스트() {
    UserCreateRequest request = new UserCreateRequest("김개발", "dev@email.com");
    Long userId = userService.createUser(request);
    assertThat(userId).isNotNull();
}

// ❌ AI가 봤을 때:
// "UserCreateRequest가 뭐지?"
// "userService는 어디 있지?"
// "어떤 필드들이 필요하지?"

SDD의 해결책

명세 문서로 구조를 먼저 정의 → AI가 정확히 이해하고 구현.

1
2
3
4
5
6
7
8
9
10
11
12
# spec.md
## Data Model: UserCreateRequest
- name: String (2-50자)
- email: String (이메일 형식)
- role: UserRole (Enum)

## Service Layer: UserService
- createUser(UserCreateRequest): Long
  - 유효성 검증
  - 이메일 중복 체크
  - DB 저장
  - ID 반환

✅ AI가 봤을 때:

  • “아, UserCreateRequest에는 name, email, role이 있구나”
  • “userService는 유효성 검증 → 중복 체크 → 저장 순서로 동작하는구나”
  • “바로 코드 짤 수 있어!”

3. SDD Workflow

1. Spec 작성

Markdown으로 기능 명세, 데이터 구조, 예외 처리 정의.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# User Management API Specification

## 1. Data Models

### User Entity
- id: Long (PK, Auto Increment)
- name: String (2-50자, NOT NULL)
- email: String (이메일 형식, UNIQUE, NOT NULL)
- role: Enum ["USER", "ADMIN"] (기본값: USER)
- createdAt: LocalDateTime (자동 생성)

### UserCreateRequest DTO
- name: String (2-50자, 필수)
- email: String (이메일 형식, 필수)
- role: String (선택, 기본값: "USER")

## 2. API Endpoints

### POST /api/users - 사용자 생성
- Request Body: UserCreateRequest
- Response: 201 Created (UserResponse)
- Error Cases:
  - 400: 유효성 검증 실패
  - 409: 이메일 중복

## 3. Business Logic
1. Request Body 유효성 검증
2. 이메일 중복 확인
3. User Entity 생성 및 저장
4. UserResponse로 변환하여 반환

## 4. Exception Handling
- DuplicateEmailException: 이메일 중복 시
- UserNotFoundException: 사용자 없을 시
- ValidationException: 유효성 검증 실패 시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# User Management API Specification

## 1. Data Models

### User Entity
- id: Long (PK, Auto Increment)
- name: String (2-50자, NOT NULL)
- email: String (이메일 형식, UNIQUE, NOT NULL)
- role: Enum ["USER", "ADMIN"] (기본값: USER)
- createdAt: LocalDateTime (자동 생성)
- updatedAt: LocalDateTime (자동 업데이트)

### UserCreateRequest DTO
- name: String (2-50자, 필수)
- email: String (이메일 형식, 필수)
- role: String (선택, 기본값: "USER")

### UserResponse DTO
- userId: Long
- name: String
- email: String
- role: String
- createdAt: String (ISO8601)

---

## 2. API Endpoints

### POST /api/users - 사용자 생성

**Request:**
```json
{
  "name": "김개발",
  "email": "dev@example.com",
  "role": "USER"
}

Response: 201 Created

1
2
3
4
5
6
7
{
  "userId": 1,
  "name": "김개발",
  "email": "dev@example.com",
  "role": "USER",
  "createdAt": "2025-12-13T19:30:00+09:00"
}

Error Responses:

  • 400 Bad Request: 유효성 검증 실패
    1
    2
    3
    4
    5
    
    {
      "error": "VALIDATION_ERROR",
      "message": "이메일 형식이 올바르지 않습니다",
      "field": "email"
    }
    
  • 409 Conflict: 이메일 중복
    1
    2
    3
    4
    
    {
      "error": "DUPLICATE_EMAIL",
      "message": "이미 존재하는 이메일입니다"
    }
    

GET /api/users/{userId} - 사용자 조회

Response: 200 OK

1
2
3
4
5
6
7
{
  "userId": 1,
  "name": "김개발",
  "email": "dev@example.com",
  "role": "USER",
  "createdAt": "2025-12-13T19:30:00+09:00"
}

Error Responses:

  • 404 Not Found: 사용자 없음
    1
    2
    3
    4
    
    {
      "error": "USER_NOT_FOUND",
      "message": "사용자를 찾을 수 없습니다"
    }
    

3. Business Logic

사용자 생성 로직

  1. Request Body 유효성 검증
    • name: 2-50자 체크
    • email: 이메일 형식 체크
    • role: Enum 값 체크
  2. 이메일 중복 확인
    • 중복 시 409 에러 반환
  3. User Entity 생성 및 저장
  4. UserResponse로 변환하여 반환

사용자 조회 로직

  1. userId로 DB 조회
  2. 없으면 404 에러 반환
  3. UserResponse로 변환하여 반환

4. Exception Handling

Custom Exceptions

  • DuplicateEmailException: 이메일 중복 시
  • UserNotFoundException: 사용자 없을 시
  • ValidationException: 유효성 검증 실패 시

Global Exception Handler

  • 모든 예외를 JSON 형식으로 일관되게 반환
  • 500 에러는 상세 정보 숨김

5. Technology Stack

  • Spring Boot 3.2
  • Spring Data JPA
  • Hibernate Validator
  • H2 Database (개발용) ```

2. Prompting

AI에게 Spec을 주입하여 초안 코드 생성.

1
2
3
4
5
6
7
8
# Cursor나 ChatGPT에서
"@spec.md 파일을 읽고, 명세대로 Spring Boot User API를 구현해줘.

요구사항:
1. 모든 DTO에 validation 어노테이션 추가
2. Controller에 Swagger 문서화 어노테이션 추가
3. 예외 처리를 GlobalExceptionHandler로 통일
4. 테스트 코드도 함께 작성"

AI 생성 결과:

  • User.java (Entity)
  • UserCreateRequest.java (DTO)
  • UserController.java (Controller)
  • UserService.java (Service)
  • UserRepository.java (Repository)
  • GlobalExceptionHandler.java (예외 처리)
  • UserControllerTest.java (테스트)

3. Review & Refine

생성된 코드 리뷰 및 테스트. (인간의 역할 = Code Reviewer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@SpringBootTest
class UserServiceTest {
    
    @Test
    void 사용자_생성_성공() {
        UserCreateRequest request = new UserCreateRequest(
            "김개발", "dev@example.com", "USER"
        );
        UserResponse response = userService.createUser(request);
        
        assertThat(response.getUserId()).isNotNull();
        assertThat(response.getName()).isEqualTo("김개발");
    }
    
    @Test
    void 이메일_중복_시_예외_발생() {
        userService.createUser(
            new UserCreateRequest("김개발", "dev@example.com", "USER")
        );
        
        assertThatThrownBy(() -> 
            userService.createUser(
                new UserCreateRequest("박개발", "dev@example.com", "USER")
            )
        ).isInstanceOf(DuplicateEmailException.class);
    }
}

4. Iterate

버그 발생 시 코드가 아닌 Spec(프롬프트)을 수정하여 재생성.

1
2
3
4
5
6
7
# 문제 발생 시
1. 코드를 직접 수정하지 않음
2. spec.md에서 누락된 요구사항 추가
3. AI에게 재생성 요청
4. 테스트로 검증

→ Spec이 Single Source of Truth 역할

4. SDD vs TDD 비교

구분TDDSDD
초점테스트 코드명세/스펙 문서
문서테스트 코드spec.md
AI 활용어려움최적화
구조 정의
비즈니스 로직 집중보통높음

함께 사용: SDD + TDD

1
2
3
4
5
6
7
// 조합: SDD + TDD
1. spec.md 작성 (전체 구조 정의)
2. AI로 코드 생성
3. TDD로 테스트 작성  검증
4. 리팩토링

 "명세 → 구현 → 검증" 완벽한 흐름

5. SDD 실전 활용

Use Case 1: 팀 협업

1
2
3
4
5
6
7
8
9
10
# Before SDD
PM: "사용자 관리 기능 만들어주세요"
개발자: "알겠습니다" (막연함)
→ 2주 후 "이거 아닌데..." (요구사항 불일치)

# After SDD
PM: [spec.md 작성]
개발자: [spec.md 검토 후 승인]
개발자: AI에게 spec.md 전달 → 코드 생성
→ 1일 후 "완벽하게 맞아요!" (정확한 구현)

Use Case 2: 레거시 리팩토링

1
2
3
4
5
6
7
// SDD 프로세스
1. 레거시 코드 분석  spec.md 작성 (역공학)
2. spec.md 검토  개선
3. AI에게 spec.md 전달  깔끔한 코드 생성
4. TDD로 동일한 동작 검증

 안전한 리팩토링

Use Case 3: API 버전 관리

1
2
3
4
5
6
7
8
9
10
11
12
# v1/spec.md
## POST /api/v1/users
- name: String
- email: String

# v2/spec.md
## POST /api/v2/users
- name: String
- email: String
- phone: String (추가됨)

→ 스펙 문서로 버전별 차이 명확히 관리

6. SDD 실전 팁

Tip 1: 스펙 문서는 ‘살아있는 문서’로 관리

1
2
3
4
5
6
7
8
# Git으로 스펙 버전 관리
git add spec.md
git commit -m "feat: User API 스펙 추가"

# 코드와 함께 리뷰
- spec.md: 명세 검토
- implementation: 코드 검토
→ 명세와 코드가 일치하는지 확인

Tip 2: OpenAPI/Swagger 형식 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# swagger.yaml (더 구조화된 스펙)
openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
paths:
  /api/users:
    post:
      summary: 사용자 생성
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserCreateRequest'

AI는 구조화된 형식을 더 잘 이해함.

Tip 3: 도메인별로 스펙 파일 분리

1
2
3
4
5
specs/
  ├── user-spec.md        # 사용자 도메인
  ├── product-spec.md     # 상품 도메인
  ├── order-spec.md      # 주문 도메인
  └── common-spec.md     # 공통 사양 (에러 응답, 페이징 등)

Tip 4: 예제를 풍부하게

1
2
3
4
5
6
7
8
9
## 좋은 스펙 예시

### POST /api/users
- Request 예시:
  ```json
  {
    "name": "김개발",
    "email": "dev@example.com"
  }
  • Response 예시:
    1
    2
    3
    4
    
    {
      "userId": 1,
      "createdAt": "2025-12-13T19:30:00+09:00"
    }
    

→ AI는 예시를 보고 패턴을 파악함 (Few-Shot Learning)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
------

### 7. SDD의 한계와 주의사항

#### 한계 1: 스펙 작성이 어려울 수 있음

```java
// 해결책: 점진적 스펙 작성
1. 초안 작성 (30% 완성도)
2. AI에게 "이 스펙을 개선해줘" 요청
3. 검토 후 확정
4. 코드 생성

→ AI가 스펙 작성도 도와줄 수 있음

한계 2: 복잡한 비즈니스 로직은 스펙으로 표현하기 어려움

1
2
3
4
5
6
7
8
9
10
11
12
13
# Bad: 너무 복잡
"사용자의 최근 3개월 주문 내역을 분석하여,
구매 패턴이 유사한 다른 사용자들의 관심 상품 중,
현재 사용자가 보지 않은 상품을 추천 점수 순으로 정렬하되,
재고가 있는 상품만 최대 10개까지..."

# Good: 단계별로 분리
## 1단계: 사용자 주문 내역 조회
## 2단계: 유사 사용자 찾기 (별도 스펙)
## 3단계: 추천 상품 계산 (별도 스펙)
## 4단계: 정렬 및 필터링

→ 복잡한 로직은 작은 단위로 분해

주의사항: 스펙과 코드의 불일치

1
2
3
4
5
6
7
8
9
// 문제 상황
1. spec.md 작성
2. AI로 코드 생성
3. 코드 수정 (스펙 업데이트  )
4. 시간이 지나면... 스펙과 코드가 달라짐

// 해결책: Spec을 Single Source of Truth로 유지
- 코드 수정  반드시 spec.md도 함께 업데이트
- Git으로 스펙과 코드를 함께 관리

8. 핵심 정리

SDD의 핵심 철학:

  1. “기획서가 곧 코드다” - 명확한 스펙은 그 자체로 구현 가능
  2. “AI는 실행자, 개발자는 설계자” - 역할의 변화
  3. “스펙 작성이 곧 개발 역량” - 무엇을 만들지 정의하는 능력
  4. “생산성 향상” - 단순 작업은 AI에게, 창의적 설계는 개발자에게

생산성 비교:

기존 방식SDD + AI
코드 작성에 60분스펙 작성 10분 + AI 생성 5분
요구사항 불일치 자주 발생스펙 검토로 사전 방지
AI와 10번 왕복 대화한 번에 정확한 코드 생성
레거시 코드 파악 어려움스펙 문서로 명확히 이해

결론: “코딩을 잘하는 개발자”보다 “스펙을 잘 쓰는 개발자”가 더 생산적인 시대.


참고 자료

  • OpenAPI Specification: https://swagger.io/specification/
  • REST API Design Best Practices: https://restfulapi.net/
  • Specification by Example (Book): https://www.manning.com/books/specification-by-example
Contents

[AI 실전] System Prompting과 자동화 - .cursorrules 설정

[AI 기초] MCP (Model Context Protocol) - JDBC 비유와 표준 인터페이스