본문으로 건너뛰기

계획 작성 (Writing Plans)

핵심 원칙: 계획의 각 작업은 이전 작업에 대한 맥락 없이도 신선한 엔지니어가 2~5분 안에 완료할 수 있을 만큼 충분히 구체적이어야 합니다.


왜 계획이 코드보다 먼저인가

코드는 설계 결정을 고정시킵니다. 코드를 작성하면 변경 비용이 높아집니다. 계획은 저렴하게 변경할 수 있습니다. 한 줄을 지우고 다시 쓰는 데는 1분이 걸립니다. 잘못 구현된 기능을 다시 작성하는 데는 몇 시간이 걸립니다.

계획은 또한 커뮤니케이션 문서입니다. 여러분과 AI 에이전트 모두 — 그리고 subagents로 작업하는 경우 — 동일한 계획을 사용합니다. 계획이 불분명하면, 실행은 발산합니다. 계획이 구체적이면, 실행은 수렴합니다.

Superpowers에서 계획은 Brainstorming에 대한 필수 후속 단계입니다. Brainstorming이 무엇을 만들지 정의하면, 계획 작성은 어떻게 만들지 정의합니다.


계획 문서 헤더

모든 계획은 표준화된 헤더로 시작합니다:

# 계획: [기능 이름]

**날짜:** [YYYY-MM-DD]
**Branch:** [branch 이름]
**작성자:** [에이전트 또는 개발자 이름]
**상태:** DRAFT | APPROVED | IN_PROGRESS | COMPLETE
**사양서 참조:** [brainstorming 문서 또는 요구사항에 대한 링크 또는 참조]

## 맥락
[이 계획이 해결하는 것에 대한 1~2문장 요약]

## 범위
**포함:** [이 계획이 다루는 것]
**제외:** [이 계획이 다루지 않는 것 — 명시적으로]

## 의존성
[이 계획이 의존하는 것 — 기존 기능, 외부 서비스, 이전에 완료되어야 하는 다른 계획]

헤더는 계획을 추적 가능하게 만듭니다. 실행 중에 계획을 찾는 subagent는 맥락에 대해 추측할 필요가 없습니다 — 맥락이 바로 거기에 있습니다.


작업 구조

계획의 각 작업은 표준 형식을 따릅니다:

### 작업 N: [작업 이름]

**상태:** TODO | IN_PROGRESS | DONE | BLOCKED
**예상 시간:** [2~5분]
**파일:** [수정할 정확한 파일 경로]

**해야 할 일:**
[명확한 산문으로 1~3문장. 이것을 처음 보는 엔지니어가 무엇을 해야 하는지 이해할 수 있어야 합니다.]

**정확한 코드:**
```[언어]
[실행 준비가 된 정확한 코드 스니펫 — 의사 코드나 "~와 비슷한 것"이 아님]

정확한 명령:

[실행할 정확한 셸 명령]

검증: [이 작업이 완료됐음을 어떻게 알 수 있는가 — 통과해야 하는 특정 테스트, 발생해야 하는 출력, 확인해야 하는 상태]

롤백: [이 작업의 변경 사항을 어떻게 되돌릴 수 있는가]


### 왜 이 형식인가

**정확한 코드:** "인증 미들웨어를 추가하세요"는 지침이 아닙니다. 작성할 정확한 코드가 있는 작업은 해석의 여지 없이 실행할 수 있습니다. Subagents에 특히 중요합니다 — 새로운 맥락으로 작업을 받습니다.

**정확한 명령:** 실행할 명령을 추측하는 subagent는 잘못된 명령을 실행할 수 있습니다. 계획에 정확한 명령이 있으면 이것이 제거됩니다.

**검증:** 완료의 정의가 없는 작업은 실행자가 알 수 없는 시점에 완료됩니다. 검증 기준은 "완료"를 관찰 가능하게 만듭니다.

**롤백:** 무언가 잘못되면 신속하게 되돌릴 수 있어야 합니다. 롤백 단계를 미리 생각하는 것은 위험한 변경도 식별합니다.

---

## 계획에서의 DRY

계획은 코드베이스를 따라야 합니다. DRY(Don't Repeat Yourself) 원칙이 계획에도 적용됩니다.

**나쁜 예:**
```markdown
### 작업 3: 이메일 서비스에 오류 처리 추가
[동일한 오류 처리 코드가 이미 작업 1과 작업 2에 포함됨]

좋은 예:

### 작업 3: 이메일 서비스에 오류 처리 추가
[작업 1에서 설정된 오류 처리 패턴 사용]

계획이 DRY를 위반하면, 그것은 신호입니다: 공유 유틸리티로 추출해야 하는 반복 패턴이 있거나, 계획이 불필요하게 반복적입니다.


계획에서의 YAGNI

계획은 해결하는 특정 문제에 대한 것이어야 합니다. 미래에 필요할 수도 있는 기능을 위한 추가 작업은 포함되지 않습니다.

YAGNI 위반의 신호:

  • "나중에 필요할 수도 있으니까 ..."로 시작하는 작업
  • 현재 기능과 관련 없는 추상화 레이어 추가 작업
  • 현재 요구사항에 없는 구성 옵션

올바른 접근: 계획이 "나중에 필요할 수도 있는" 것들로 팽창하기 시작하면, 현재 사양서로 돌아가세요. 사양서에 있나요? 없다면, 계획에 없어야 합니다.


TDD 페어링

Superpowers 계획에서 모든 구현 작업에는 선행하는 테스트 작업이 있습니다. 이것은 계획 수준에서 TDD를 강제합니다:

### 작업 N: [동작]에 대한 실패 테스트 작성  ← RED
**파일:** tests/[기능].test.ts
...

### 작업 N+1: [동작] 구현  ← GREEN
**파일:** src/[기능].ts
...

Subagent가 선행 테스트 작업 없이 구현 작업(작업 N+1)을 받으면, 작업을 거부하고 BLOCKED로 보고해야 합니다. 선행 실패 테스트가 없는 구현 작업은 계획 결함입니다.


범위 관리

계획을 작성할 때 범위 관리는 지속적인 규율입니다.

범위 확장 신호

계획을 작성하는 동안 이것들이 나타나면 멈추세요:

  • "~할 때 ..."로 시작하는 작업 (현재 요구사항이 아닌 미래 시나리오)
  • 사양서에 없는 데이터 마이그레이션 또는 스키마 변경
  • 현재 기능에 필요하지 않은 기존 코드의 리팩토링
  • "좋은 기회이니까" 성능 최적화

범위 축소 신호

계획이 너무 작다고 생각된다면, 이것들을 고려하세요:

  • 계획이 brainstorming 사양서와 완전히 일치하는가?
  • 모든 엣지 케이스에 대한 작업이 있는가?
  • 오류 처리가 계획되어 있는가?
  • 모든 영향받는 테스트에 대한 작업이 있는가?

3단계 계획 검토

계획을 실행 전에 검토할 때, 세 가지 관심사를 별도로 평가하세요:

1단계: 완전성

  • 모든 사양서 요구사항이 계획에 있는가?
  • 모든 구현 작업에 선행 테스트 작업이 있는가?
  • 오류 케이스와 엣지 케이스가 처리되는가?
  • 검증 기준이 각 작업에 정의되어 있는가?

2단계: 범위 정렬

  • 계획의 모든 작업이 사양서에서 추적 가능한가?
  • 사양서에 없는 작업이 있는가?
  • 범위 "제외" 항목이 명시적으로 기술되어 있는가?

3단계: 작업 크기

  • 각 작업이 2~5분 이내에 완료 가능한가?
  • 작업이 더 작은 단계로 분해될 수 있는가?
  • 작업이 여러 파일에 걸쳐 있는가? (분해 신호)
  • 작업이 "그리고"를 포함하는가? (분해 신호)

예시 계획: 주문 배송 시 사용자 이메일 알림

# 계획: 주문 배송 시 사용자 이메일 알림

**날짜:** 2026-01-15
**Branch:** feature/order-shipped-email
**작성자:** Claude (brainstorming 세션 후)
**상태:** APPROVED
**사양서 참조:** brainstorm-order-notifications.md

## 맥락
주문이 배송됨으로 표시될 때 고객에게 이메일 알림을 보냅니다. 이미 존재하는 이메일 서비스를 사용합니다.

## 범위
**포함:** 배송 상태 변경 시 이메일 발송, 이메일 내용에 추적 번호 포함
**제외:** SMS 알림, 이메일 템플릿 사용자 정의, 알림 기본 설정 관리

## 의존성
- EmailService (src/services/email.ts에 존재)
- Order 모델 (src/models/order.ts에 존재)

---

### 작업 1: OrderService.markShipped에 대한 실패 테스트 작성

**상태:** TODO
**예상 시간:** 3분
**파일:** tests/services/order.service.test.ts

**해야 할 일:**
주문이 배송됨으로 표시될 때 EmailService.sendShippingConfirmation이 올바른 인수로 호출되는지 테스트를 작성합니다. 아직 존재하지 않으므로 테스트가 실패해야 합니다.

**정확한 코드:**
```typescript
it('주문이 배송됨으로 표시될 때 배송 확인 이메일을 보냅니다', async () => {
  const mockEmailService = { sendShippingConfirmation: jest.fn() };
  const orderService = new OrderService(mockEmailService);

  await orderService.markShipped('order-123', 'TRACK-456');

  expect(mockEmailService.sendShippingConfirmation).toHaveBeenCalledWith({
    orderId: 'order-123',
    trackingNumber: 'TRACK-456',
    customerEmail: 'customer@example.com'
  });
});

정확한 명령:

npm test -- --testPathPattern=order.service

검증: 테스트가 "sendShippingConfirmation is not a function" 또는 이와 유사한 오류로 실패함

롤백: 테스트 파일에서 이 테스트 케이스 삭제


작업 2: OrderService.markShipped에 이메일 알림 구현

상태: TODO 예상 시간: 4분 파일: src/services/order.service.ts

해야 할 일: markShipped 메서드가 EmailService.sendShippingConfirmation을 호출하도록 수정합니다. 고객 이메일은 주문 레코드에서 가져옵니다.

정확한 코드:

async markShipped(orderId: string, trackingNumber: string): Promise<void> {
  const order = await this.orderRepository.findById(orderId);
  await this.orderRepository.updateStatus(orderId, 'shipped');
  await this.emailService.sendShippingConfirmation({
    orderId,
    trackingNumber,
    customerEmail: order.customerEmail
  });
}

정확한 명령:

npm test -- --testPathPattern=order.service

검증: 작업 1의 테스트가 통과함

롤백: markShipped에서 emailService 호출 라인 삭제


작업 3: EmailService.sendShippingConfirmation에 대한 실패 테스트 작성

상태: TODO 예상 시간: 3분 파일: tests/services/email.service.test.ts

해야 할 일: sendShippingConfirmation이 올바른 제목과 본문으로 이메일을 보내는지 테스트합니다. 아직 존재하지 않으므로 실패해야 합니다.

정확한 코드:

it('추적 번호와 함께 배송 확인 이메일을 보냅니다', async () => {
  const mockMailer = { send: jest.fn() };
  const emailService = new EmailService(mockMailer);

  await emailService.sendShippingConfirmation({
    orderId: 'order-123',
    trackingNumber: 'TRACK-456',
    customerEmail: 'customer@example.com'
  });

  expect(mockMailer.send).toHaveBeenCalledWith({
    to: 'customer@example.com',
    subject: '주문 order-123이 배송됐습니다',
    body: expect.stringContaining('TRACK-456')
  });
});

정확한 명령:

npm test -- --testPathPattern=email.service

검증: 테스트가 "sendShippingConfirmation is not a function"으로 실패함

롤백: 테스트 파일에서 이 테스트 케이스 삭제


작업 4: EmailService.sendShippingConfirmation 구현

상태: TODO 예상 시간: 3분 파일: src/services/email.service.ts

해야 할 일: 배송 확인 이메일을 보내는 sendShippingConfirmation 메서드를 EmailService에 추가합니다.

정확한 코드:

async sendShippingConfirmation(params: {
  orderId: string;
  trackingNumber: string;
  customerEmail: string;
}): Promise<void> {
  await this.mailer.send({
    to: params.customerEmail,
    subject: `주문 ${params.orderId}이 배송됐습니다`,
    body: `안녕하세요! 주문이 배송됐습니다. 추적 번호: ${params.trackingNumber}`
  });
}

정확한 명령:

npm test -- --testPathPattern=email.service
npm test

검증: 작업 3의 테스트가 통과함; 전체 테스트 스위트가 통과함

롤백: EmailService에서 sendShippingConfirmation 메서드 삭제


---

## 계획 작성 시작하기

계획 작성 skill을 명시적으로 호출하려면:

> "설계에 동의했습니다. 구현 계획을 작성하세요."

또는:

> "이 사양서를 가지고 실행 가능한 계획을 작성해 주세요."

AI는 brainstorming의 승인된 사양서를 가져와 표준 형식을 따르는 번호가 매겨진 작업들로 변환합니다.

---

> **계획은 규율에 관한 것이 아닙니다. 명확성에 관한 것입니다.** 잘 작성된 계획은 실행을 예측 가능하게 만들고, 검토를 의미 있게 만들며, 디버깅을 구체적으로 만듭니다. 모호한 계획은 확신을 가지고 실행되지만 예측할 수 없는 결과를 냅니다.