Usecase: 팀이 결제 시스템 프로덕션 버그를 디버깅하기
⚠️ 참고: 이것은 사용 방법을 설명하기 위한 가상의 usecase입니다. 실제 AI 응답은 아래 예시와 다를 수 있습니다.
결제 버그가 프로덕션에 올라갔을 때, 자연스러운 반응은 가능한 빨리 수정하는 것입니다. Superpowers는 올바른 순간에 속도를 늦추고 제대로 수정하는 방법을 가르쳐줍니다 -- 체계적으로, 증거를 기반으로, 리뷰를 거쳐서.
배경
2-3명의 개발자로 구성된 소규모 팀에서 e-commerce 시스템을 관리하고 있다고 상상해보세요. 어느 날, 팀이 고객으로부터 보고를 받습니다:
"20% 할인 상품을 구매하고 10% 바우처를 사용했는데 가격이 맞지 않습니다. 기대했던 가격보다 더 많이 청구되었습니다."
확인 결과:
- 상품: 티셔츠 정가 500,000원, 20% 할인
- 바우처: SAVE10, 10% 추가 할인
- 기대값: 500,000 x 0.8 = 400,000 -> 400,000 x 0.9 = 360,000원
- 실제값: 시스템이 계산한 금액 350,000원
10,000원 차이. 고객이 적게 청구되었지만, 로직 오류는 고가 상품이나 더 큰 바우처에서 훨씬 큰 차이를 만들 수 있습니다.
기술 스택: Node.js, TypeScript, calculateFinalPrice() 함수
배울 수 있는 것
이 usecase를 통해 다음을 실습합니다:
- Systematic Debugging 4 Phases -- 추측이 아닌 체계적인 디버깅 프로세스 (자세히 보기: 디버깅 & 검증)
- TDD Fix -- 수정 전에 버그를 재현하는 테스트를 먼저 작성 (자세히 보기: TDD)
- 양방향 Code Review -- 보내는 사람은 컨텍스트를 준비하고, 리뷰하는 사람은 꼼꼼히 읽은 후 피드백 (자세히 보기: Code Review)
- Verification Gate -- 명확한 증거가 있을 때만 "수정 완료" 선언 (자세히 보기: 디버깅 & 검증)
단계별 실습
1단계: Phase 1 -- 근본 원인 조사
코드를 수정하기 전에, 문제를 완전히 이해해야 합니다. 추측은 허용되지 않습니다.
1.1. 고객의 버그 보고서 읽기
고객 피드백:
"티셔츠를 500,000원에 구매했는데, 상품 페이지에 20% 할인이라고 되어 있었습니다. SAVE10 바우처로 추가 10% 할인을 적용했습니다. 360,000원을 낼 것으로 기대했지만 시스템이 350,000원을 계산했습니다."
🧑 사용자가 AI에게 고객 버그 보고서 내용을 제공합니다. 정확한 숫자를 기록합니다 — 재해석하지 않고, 원본 데이터로 작업합니다. 예시 프롬프트:
"고객이 결제 버그를 보고했습니다: 티셔츠 500,000원 20% 할인 구매, SAVE10 바우처 10% 할인 사용, 360,000원 기대했지만 시스템이 350,000원을 계산했습니다. 체계적인 프로세스로 디버깅을 도와주세요."
🤖 AI가 보고서를 받고, 구체적인 숫자 (정가, 할인 %, 바우처, 기대값 vs 실제값)를 기록합니다. AI는 원인을 바로 추측하지 않고 체계적인 조사 프로세스를 시작합니다.
1.2. 오류 재현
🤖 AI가 src/utils/pricing.ts 파일을 읽습니다 — calculateFinalPrice() 함수는 Product (discountPercent 포함)와 Voucher (선택적, discountPercent 포함)를 받아 할인 후 가격을 반환합니다. 언뜻 보면 로직이 맞아 보입니다: 상품 할인을 먼저 적용하고, 그 다음 바우처를 적용합니다. 하지만 이것은 AI가 실행되고 있다고 생각하는 코드입니다. AI는 프로덕션의 실제 코드를 계속 확인합니다.
🧑 사용자가 AI가 코드를 읽는 것을 관찰하고, 확인해야 할 올바른 파일/함수인지 확인합니다. AI가 잘못된 파일을 읽고 있다면, 방향을 수정합니다.
1.3. 최근 변경사항 확인
🤖 AI가 git log를 실행하여 최근 커밋을 확인합니다:
git log --oneline -10
결과:
a1b2c3d feat: add loyalty points calculation
f4e5d6a refactor: simplify discount calculation <-- 바로 이것
b7c8d9e fix: update shipping fee for remote areas
...
🤖 AI가 "refactor: simplify discount calculation"이라는 메시지의 커밋 f4e5d6a가 매우 의심스럽다고 인식하고, diff를 확인합니다:
git diff f4e5d6a~1..f4e5d6a -- src/utils/pricing.ts
🧑 사용자가 AI와 함께 git log 결과를 봅니다. 추가 컨텍스트가 있다면 (예: "그 커밋은 새 개발자가 지난주에 merge한 것입니다"), AI에게 제공하여 조사 범위를 좁힙니다.
1.4. 실제 코드 (오류 코드) 발견
🤖 AI가 커밋 f4e5d6a의 diff를 확인하고 프로덕션의 실제 코드가 변경되었음을 발견합니다: 할인을 순차적으로 적용하는 대신 (가격 x 0.8 후 x 0.9), 리팩토링한 사람이 퍼센트를 합산 (20% + 10% = 30%)한 후 한 번에 적용 (가격 x 0.7)했습니다. AI가 발견 내용을 사용자에게 보고합니다.
🧑 사용자가 AI의 분석을 읽고 확인합니다: "맞습니다, 올바른 비즈니스 로직은 순차적 적용입니다." 비즈니스 규칙이 다르다면 (예: 합산이 맞는 경우), 명확하게 말하여 AI가 조사 방향을 조정하도록 합니다.
문제: 이것은 로직 오류입니다 — "단순화"가 계산 결과를 변경했습니다.
1.5. 데이터 흐름 추적
🤖 AI가 두 가지 방법으로 계산 흐름을 추적하여 사용자에게 명확하게 제시합니다:
오류 코드 (합산):
totalDiscount = 20 + 10 = 30
price = 500,000 * (1 - 30/100)
price = 500,000 * 0.7
price = 350,000원
올바른 로직 (순차):
price = 500,000 * (1 - 20/100) = 500,000 * 0.8 = 400,000
price = 400,000 * (1 - 10/100) = 400,000 * 0.9 = 360,000원
차이: 360,000 - 350,000 = 10,000원. 🤖 AI가 경고합니다: 더 비싼 상품이나 더 큰 바우처의 경우 차이는 훨씬 커질 것입니다.
🧑 사용자가 AI의 계산이 맞는지 확인하고 결론에 동의합니다. 이것은 중요한 체크포인트입니다 — 다음 단계로 진행하기 전에 양쪽이 원인에 대해 일치해야 합니다.
참고: 이 단계에서는 조사만 합니다, 아직 아무것도 수정하지 않습니다. 이론보다 증거가 먼저입니다.
2단계: Phase 2 -- 패턴 분석
버그를 재현한 후, 패턴을 찾아봅니다.
2.1. 정상 작동하는 사례 찾기
🤖 AI가 여러 다른 경우를 자동으로 확인하여 버그가 언제 나타나고 언제 나타나지 않는지 파악합니다:
경우 1: 상품 할인만 (바우처 없음)
Input: product = { price: 500000, discountPercent: 20 }, no voucher
Code: totalDiscount = 20, price = 500000 * 0.8 = 400,000
Result: 400,000원 -- 정확
경우 2: 바우처만 (상품 할인 없음)
Input: product = { price: 300000, discountPercent: 0 }, voucher = { discountPercent: 10 }
Code: totalDiscount = 0 + 10 = 10, price = 300000 * 0.9 = 270,000
Result: 270,000원 -- 정확
경우 3: 두 할인 동시 적용
Input: product = { price: 500000, discountPercent: 20 }, voucher = { discountPercent: 10 }
Code: totalDiscount = 20 + 10 = 30, price = 500000 * 0.7 = 350,000
Result: 350,000원 -- 오류 (기대값 360,000원)
🧑 사용자가 AI가 나열한 경우들을 검토합니다. AI가 아직 고려하지 않은 특별한 경우가 있다면 (예: "시스템에는 VIP 고객을 위한 세 번째 할인 유형도 있습니다"), 추가 분석을 위해 보충합니다.
2.2. 비교 및 패턴 결론
🤖 AI가 결과를 종합하고 패턴을 도출합니다: 버그는 두 할인이 동시에 적용될 때만 발생합니다. AI가 수학적 이유를 설명합니다:
- 합산:
price * (1 - (a + b) / 100) - 순차:
price * (1 - a/100) * (1 - b/100)
이 두 수식은 수학적으로 동등하지 않습니다 (a 또는 b가 0인 경우를 제외).
🧑 사용자가 AI가 제시한 패턴이 시스템의 실제 동작과 일치하는지 확인합니다. 다른 고객의 추가 버그 보고서가 있다면, AI에게 제공하여 교차 검증합니다.
3단계: Phase 3 -- 가설 수립 및 검증
3.1. 명확한 가설 진술
🤖 AI가 명확하고 구조화된 가설을 진술합니다:
가설:
calculateFinalPrice()함수가 할인 퍼센트를 합산 (20% + 10% = 30%)하는 대신 순차적으로 적용 (가격 x 0.8 x 0.9)해야 하기 때문에 버그가 발생합니다. 커밋f4e5d6a("refactor: simplify discount calculation")가 순차 적용에서 합산으로 로직을 리팩토링했습니다.
🧑 사용자가 가설을 읽고 합리적인지 평가합니다. 동의하면 AI가 검증을 진행하도록 확인합니다. 동의하지 않거나 다른 가설이 있다면, 제시합니다. 가설을 종이에 적거나 코멘트에 기록합니다 — 가설이 머릿속에만 존재하지 않도록 합니다.
3.2. 한 번에 하나의 변수 확인
🤖 AI가 calculateFinalPrice() 함수에 console.log를 추가하여 각 계산 단계를 추적한 후, 고객 보고서의 입력값으로 실행합니다:
[DEBUG] Initial price: 500000
[DEBUG] Product discount: 20
[DEBUG] Voucher discount: 10
[DEBUG] Total discount (합산): 30
[DEBUG] Final price: 350000
🤖 AI가 디버그 결과를 사용자에게 제시하고, 어떤 라인이 가설을 증명하는지 명확히 가리킵니다.
🧑 사용자가 디버그 출력을 보고 고객 보고서의 데이터와 대조합니다. 결과가 보고된 오류 동작과 일치하는지 확인합니다.
3.3. 구체적인 숫자로 가설 확인
🤖 AI가 여러 입력 세트로 비교 표를 만들어 버그의 영향 범위를 증명합니다:
| 경우 | 합산 (버그) | 순차 (정확) | 차이 |
|---|---|---|---|
| 500k, 20% + 10% 바우처 | 350,000 | 360,000 | 10,000 |
| 1,000k, 30% + 15% 바우처 | 550,000 | 595,000 | 45,000 |
| 2,000k, 50% + 20% 바우처 | 600,000 | 800,000 | 200,000 |
차이는 상품 가격과 할인율에 따라 증가합니다. 🤖 AI가 결론을 내립니다: 가설이 확인되었습니다.
🧑 사용자가 비교 표를 리뷰하고, 실제 데이터를 기반으로 버그의 심각도를 평가합니다 (예: "우리의 가장 비싼 상품은 500만 원이니, 최대 차이는..."). 수정 우선순위를 결정합니다.
4단계: Phase 4 -- TDD Fix
이제 정확한 원인을 알고 있습니다. 코드 수정에 뛰어드는 대신, 올바른 TDD 프로세스를 따릅니다: 테스트를 먼저 작성하고, 코드를 그 다음에 수정합니다.
🤖 AI가 행동하기 전에 질문합니다: "가설이 확인되었습니다: 퍼센트 합산 대신 순차 적용으로 인한 버그입니다. TDD 프로세스에 따라 수정을 진행할까요 — 버그를 재현하는 테스트를 먼저 작성하고, 그 다음 코드를 수정하는 방식으로?"
🧑 사용자가 확인합니다: "동의합니다, 진행하세요."
RED: 버그를 재현하는 실패 테스트 작성
🤖 AI가 src/utils/__tests__/pricing.test.ts 파일을 5개 테스트 케이스로 생성합니다:
- 상품 할인과 바우처를 순차적으로 적용 (기대값 360,000)
- 상품 할인만 (기대값 400,000)
- 할인 없는 상품에 바우처만 (기대값 270,000)
- 할인 없음 (기대값 200,000)
- 소수점이 있는 가격의 반올림 (기대값 263,500)
🤖 AI가 npm test -- --grep "calculateFinalPrice" 실행 → 2 FAIL, 3 PASS. 테스트 1이 예상대로 실패합니다: 기대값 360,000이지만 실제값 350,000. 테스트 5도 합산 로직의 잘못된 계산으로 실패합니다.
테스트 2, 3, 4는 하나의 할인 유형만 있어서 합산과 순차가 같은 결과를 내기 때문에 통과합니다.
🧑 사용자가 테스트 케이스 목록을 리뷰합니다 — 시스템의 특수한 경우가 빠져 있다면 보충합니다 (예: "VIP 고객이 5% 추가 할인을 받는 경우도 있습니다").
GREEN: 모든 테스트가 통과하도록 함수 수정
🤖 AI가 src/utils/pricing.ts를 수정합니다 — 할인을 순차적으로 적용하는 로직을 복원합니다: 상품 할인 먼저, 그 다음 바우처 할인 (이미 할인된 가격에 적용). 테스트 재실행 → 5/5 PASS.
🧑 사용자가 수정된 코드의 diff를 읽고, 로직이 시스템의 비즈니스 규칙과 일치하는지 확인합니다.
REFACTOR: Guard Clause 추가
🤖 AI가 src/utils/pricing.ts에 edge case 검증을 추가합니다: 가격이 음수가 아닌지, 할인이 0-100 범위인지. 테스트 파일에 guard clause용 테스트 케이스 2개를 추가합니다. 전체 테스트 스위트 재실행 → 모두 통과, regression 없음.
🧑 사용자가 guard clause를 리뷰합니다 — 검증 한도가 비즈니스 규정에 적합한지 확인합니다 (예: 할인이 100%까지 가능한지, 최대 80%인지?).
5단계: 양방향 Code Review
버그가 수정되고 테스트가 통과했습니다. 하지만 merge 전에 code review가 필요합니다.
Dev A (수정한 사람)가 review request 전송
🧑 사용자가 AI에게 PR 생성을 요청합니다. 🤖 AI가 완전한 구조화된 정보로 pull request를 생성합니다:
## WHAT
상품 할인과 바우처 할인을 동시에 적용할 때 가격이 잘못 계산되는 버그 수정.
## ROOT CAUSE
커밋 f4e5d6a ("refactor: simplify discount calculation")가 순차 적용에서
퍼센트 합산으로 로직을 변경.
합산: 500000 * (1 - (20+10)/100) = 350,000 (오류)
순차: 500000 * 0.8 * 0.9 = 360,000 (정확)
## PLAN
- 할인을 순차적으로 적용하는 로직 복원
- 5개 테스트 케이스 추가: 양쪽 할인, 상품만, 바우처만,
할인 없음, 반올림
- input validation을 위한 guard clause 추가
## CHANGES
- src/utils/pricing.ts: calculateFinalPrice() 함수 수정
- src/utils/__tests__/pricing.test.ts: 7개 테스트 케이스 추가
## BASE_SHA -> HEAD_SHA
f4e5d6a -> g7h8i9j
참고: Dev A가 충분한 컨텍스트를 제공하여 Dev B가 효율적으로 리뷰할 수 있게 합니다. 보내는 사람에게는 리뷰하는 사람이 문제를 이해할 수 있도록 도울 책임이 있습니다.
Dev B (리뷰어)가 프로세스에 따라 리뷰
Dev B (또는 Dev B의 AI)는 서둘러 피드백하지 않습니다. 대신 5단계 프로세스를 수행합니다:
1. READ -- 전체 PR description과 diff를 읽기
2. UNDERSTAND -- 문제와 수정 방법 이해
Dev B가 직접 다시 계산합니다:
합산: 500000 * (1 - 30/100) = 500000 * 0.7 = 350,000
순차: 500000 * 0.8 * 0.9 = 400000 * 0.9 = 360,000
확인: Dev A의 계산이 맞습니다.
3. VERIFY -- 코드와 테스트 확인
Dev B가 수정된 코드를 읽고, 각 테스트 케이스를 읽고, 각 테스트 케이스가 의미 있고 정확한지 확인합니다.
4. EVALUATE -- Edge case 평가
Dev B가 질문합니다:
- 총 할인이 100%를 초과하면? (guard clause로 처리됨)
- price = 0이면? (0 * 무엇이든 = 0, ok)
- voucher.discountPercent = 0이면? (price * 1 = price, ok)
- 다른 함수에 영향이 있는가? (calculateFinalPrice의 caller 확인)
5. RESPOND -- 구체적이고 건설적인 피드백
Dev B는 다음과 같이 작성하지 않습니다:
"Looks good! LGTM"
Dev B는 다음과 같이 작성합니다:
## Review: Fix calculateFinalPrice
### 확인 완료
- 순차 적용 로직이 맞습니다. 3개의 다른 입력 세트로 직접 계산을 확인했습니다.
- 테스트 케이스가 기본적인 경우를 충분히 커버합니다.
- input validation을 위한 guard clause가 적절합니다.
### 제안
1. voucher.discountPercent = 0인 경우의 테스트 케이스를 추가하여
가격에 영향을 주지 않는 것을 확인해야 합니다.
2. promotion.ts 파일의 `applyPromotion()` 함수를 확인해야 합니다 --
이 함수도 calculateFinalPrice()를 호출하고 있으며, 유사한
로직이 수정이 필요할 수 있습니다.
3. 왜 합산이 아닌 순차 적용인지 설명하는 코멘트를 추가하여,
다른 사람이 다시 "simplify"하는 것을 방지해야 합니다.
### 결론
위 3가지 제안과 함께 Approve. 제안 2는 merge 전에 확인해야 합니다.
Dev A 응답
Dev A가 피드백을 읽고 처리합니다:
- voucher.discountPercent = 0 테스트 케이스 추가 -- 완료
applyPromotion()함수 확인 -- 이 함수는calculateFinalPrice()만 호출하고 별도의 할인 로직이 없음을 확인 -- 수정 불필요- 설명 코멘트 추가 -- 완료
Dev A가 calculateFinalPrice() 함수에 설명 코멘트를 추가합니다 — 합산이 아닌 순차 적용의 이유를 명확히 기재하여, 향후 다른 사람이 다시 "simplify"하는 것을 방지합니다. 새 커밋을 push하고 3가지 피드백 모두 처리했음을 reply로 확인합니다.
6단계: Verification Gate
버그가 수정되었다고 선언하기 전에, 5단계 검증 프로세스를 적용합니다.
🧑 사용자가 요청합니다: "merge 전에 전체 검증을 실행해주세요."
1. 결정 -- 어떤 테스트를 실행할 것인가
🤖 AI가 실행해야 할 테스트 목록을 나열합니다:
- calculateFinalPrice 유닛 테스트: 7개 테스트 케이스
- checkout 흐름 통합 테스트: 3개 테스트 케이스
- Regression 테스트 스위트: 전체
🧑 사용자가 필요한 경우 추가합니다 (예: "checkout 페이지 e2e 테스트도 실행해주세요").
2. 실행 -- 테스트 수행
🤖 AI가 전체 테스트를 실행합니다:
# Unit tests
npm test -- --grep "calculateFinalPrice"
# Integration tests
npm test -- --grep "checkout"
# Full regression
npm test
3. 읽기 -- 결과를 한 줄씩 확인
🤖 AI가 결과를 읽고 제시합니다:
Test Suites: 24 passed, 24 total
Tests: 187 passed, 187 total
Snapshots: 0 total
Time: 12.34s
187개 테스트 모두 통과. Regression 없음.
4. 확인 -- 결과 대조
🤖 AI가 확인 체크리스트를 종합합니다:
- calculateFinalPrice 7개 테스트 케이스: PASS
- checkout 흐름 통합 테스트 3개: PASS
- 총 187개 테스트: PASS
- skip되거나 pending된 테스트 없음
🧑 사용자가 스테이징 환경에서 고객 보고서의 정확한 입력값으로 수동 확인합니다:
- 상품 500,000원, 20% 할인, 바우처 10%
- 결과: 360,000원 -- 정확
5. 선언 -- 증거가 있는 결론
🤖 AI가 증거를 기반으로 결론을 내립니다:
버그가 수정되었습니다.
calculateFinalPrice()함수가 할인 합산에서 순차 적용으로 수정되었습니다. 7개 유닛 테스트, 3개 통합 테스트, 스테이징에서의 수동 확인으로 검증되었습니다. Regression 없음. 프로덕션 배포 준비 완료.
🧑 사용자가 최종 확인하고 merge/배포를 승인합니다.
결과
Superpowers 프로세스를 적용한 후, 팀은 다음을 달성했습니다:
- 버그가 제대로 수정됨 -- 임시 핫픽스가 아닌 체계적이고 증거가 있는 수정
- 7개 테스트 케이스 추가 -- 이 버그가 향후 재발하는 것을 방지
- 팀이 디버깅 프로세스를 학습 -- 반응 모드 ("최대한 빨리 수정")에서 체계적 모드 ("최대한 정확하게 수정")로
- 코드에 코멘트 추가 -- 다음 사람이 읽으면 왜 순차 로직이어야 하는지 이해할 수 있으며, 아무도 다시 "simplify"할 수 없음
총 소요 시간: 전체 프로세스에 약 2-3시간. "빠른 수정"보다 오래 걸린 것처럼 보이지만:
- 버그가 재발할 가능성 없음
- 급한 수정으로 인한 추가 버그 발생 가능성 없음
- 향후 자신 있게 리팩토링할 수 있는 테스트 확보
핵심 포인트
1. 디버깅할 때 절대 추측하지 마세요
4단계 체계적 프로세스를 따르세요: 조사 -> 패턴 분석 -> 가설 -> TDD Fix. 각 단계는 다음 단계로 넘어가기 전에 구체적인 산출물이 있습니다.
2. git log는 여러분의 친구입니다
커밋 "refactor: simplify discount calculation"이 바로 원인이었습니다. 원인을 추측하기 전에 git log --oneline -10과 git diff를 실행하는 습관을 기르면 잘못된 방향으로 디버깅하는 시간을 절약할 수 있습니다.
3. 수정 전에 버그를 재현하는 테스트를 작성하세요
테스트는 버그 존재의 증거입니다. 테스트가 실패하면 (RED), 증거가 있습니다. 수정 후 테스트가 통과하면 (GREEN), 버그가 수정되었다는 증거가 있습니다. 테스트 없음 = 증거 없음.
4. 양방향 Code review
- 보내는 사람: 충분한 컨텍스트를 준비 (WHAT, ROOT CAUSE, PLAN, CHANGES). 리뷰하는 사람이 문제를 빠르게 이해할 수 있도록 돕습니다.
- 리뷰하는 사람: 피드백하기 전에 꼼꼼히 읽습니다. 실제로 확인하지 않았으면 "LGTM"을 쓰지 않습니다. 구체적이고 건설적인 피드백을 합니다.
5. Evidence before claims
"수정 완료"는 명확한 검증 증거가 있을 때만 선언합니다: 테스트 통과, regression 없음, 수동 확인 성공. 증거 없는 선언은 보장이 없는 약속입니다.
참고
- 디버깅 & 검증 -- 4단계 디버깅 프로세스 전체
- TDD -- RED-GREEN-REFACTOR 사이클
- Code Review -- 양방향 리뷰 프로세스
- 철칙 -- Superpowers의 불가침 규칙들