# systematic-debugging > 버그, 테스트 실패, 예상치 못한 동작 발생 시 수정 전 반드시 사용. 4단계 근본 원인 분석 프로세스. - Author: kwak - Repository: insightflo/ohmycoupon - Version: 20260125215545 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-06 - Source: https://github.com/insightflo/ohmycoupon - Web: https://mule.run/skillshub/@@insightflo/ohmycoupon~systematic-debugging:20260125215545 --- --- name: systematic-debugging description: 버그, 테스트 실패, 예상치 못한 동작 발생 시 수정 전 반드시 사용. 4단계 근본 원인 분석 프로세스. --- # Systematic Debugging (체계적 디버깅) ## 개요 무작위 수정은 시간 낭비이고 새로운 버그를 만든다. 빠른 패치는 근본 문제를 가린다. **핵심 원칙:** 수정 전 반드시 근본 원인을 찾는다. 증상 수정은 실패다. **이 프로세스의 문자를 위반하는 것은 디버깅의 정신을 위반하는 것이다.** --- ## Iron Law (철칙) ``` ⛔ 근본 원인 조사 없이 수정 금지 ``` Phase 1을 완료하지 않았다면, 수정을 제안할 수 없다. --- ## 언제 사용하는가 **모든 기술적 문제에 사용:** - 테스트 실패 - 프로덕션 버그 - 예상치 못한 동작 - 성능 문제 - 빌드 실패 - 통합 이슈 **특히 다음 상황에서 반드시 사용:** - 시간 압박 (긴급 상황에서 추측이 유혹적) - "빠른 수정 하나"가 명백해 보일 때 - 이미 여러 번 수정을 시도했을 때 - 이전 수정이 작동하지 않았을 때 - 문제를 완전히 이해하지 못했을 때 **건너뛰면 안 되는 경우:** - 문제가 단순해 보여도 (단순한 버그도 근본 원인이 있다) - 급해도 (체계적 접근이 삽질보다 빠르다) - 관리자가 지금 당장 고치라고 해도 --- ## 4단계 프로세스 각 단계를 완료해야 다음 단계로 진행할 수 있다. ### Phase 1: 근본 원인 조사 (Root Cause Investigation) **수정 시도 전 필수 수행:** #### 1.1 에러 메시지 꼼꼼히 읽기 - 에러나 경고를 건너뛰지 않는다 - 정확한 해결책이 포함된 경우가 많다 - 스택 트레이스 전체를 읽는다 - 라인 번호, 파일 경로, 에러 코드를 기록한다 #### 1.2 일관되게 재현하기 - 안정적으로 트리거할 수 있는가? - 정확한 단계는 무엇인가? - 매번 발생하는가? - 재현 불가 → 데이터 수집 더 하기, 추측 금지 #### 1.3 최근 변경사항 확인 - 이 문제를 일으킬 수 있는 변경은? - `git diff`, 최근 커밋 - 새 의존성, 설정 변경 - 환경 차이 #### 1.4 다중 컴포넌트 시스템에서 증거 수집 **시스템이 여러 컴포넌트로 구성될 때 (CI → 빌드 → 서명, API → 서비스 → DB):** ```bash # 수정 제안 전, 진단 계측 추가: # Layer 1: 워크플로우 echo "=== 워크플로우에서 사용 가능한 시크릿: ===" echo "API_KEY: ${API_KEY:+SET}${API_KEY:-UNSET}" # Layer 2: 빌드 스크립트 echo "=== 빌드 스크립트의 환경 변수: ===" env | grep API_KEY || echo "API_KEY not in environment" # Layer 3: 실제 호출 echo "=== API 호출 직전 상태: ===" curl -v "$API_ENDPOINT" ``` **각 컴포넌트 경계에서:** - 컴포넌트에 들어오는 데이터 로그 - 컴포넌트에서 나가는 데이터 로그 - 환경/설정 전파 확인 - 각 레이어의 상태 체크 #### 1.5 데이터 흐름 추적 **에러가 콜 스택 깊숙이 있을 때:** 참조: `references/root-cause-tracing.md` **빠른 버전:** - 잘못된 값의 출처는 어디인가? - 이 값을 전달한 호출자는 누구인가? - 소스를 찾을 때까지 계속 역추적 - 증상이 아닌 소스에서 수정 --- ### Phase 2: 패턴 분석 (Pattern Analysis) **수정 전 패턴 찾기:** #### 2.1 동작하는 예시 찾기 - 같은 코드베이스에서 유사하게 동작하는 코드 찾기 - 깨진 것과 비슷한데 동작하는 것은? #### 2.2 레퍼런스와 비교 - 패턴 구현 중이라면, 레퍼런스 구현을 완전히 읽기 - 대충 읽지 말고 모든 줄을 읽기 - 적용 전 패턴을 완전히 이해 #### 2.3 차이점 식별 - 동작하는 것과 깨진 것의 차이는? - 아무리 작아도 모든 차이 나열 - "그건 상관없겠지" 가정 금지 #### 2.4 종속성 이해 - 이 코드가 필요로 하는 다른 컴포넌트는? - 어떤 설정, 구성, 환경이 필요한가? - 어떤 가정을 하고 있는가? --- ### Phase 3: 가설 및 테스트 (Hypothesis and Testing) **과학적 방법:** #### 3.1 단일 가설 수립 - 명확하게 진술: "X가 근본 원인이라고 생각한다. 왜냐하면 Y이기 때문이다" - 적어둔다 - 모호하지 않고 구체적으로 #### 3.2 최소한으로 테스트 - 가설을 테스트하기 위한 가능한 가장 작은 변경 - 한 번에 하나의 변수만 - 여러 가지를 한꺼번에 수정하지 않는다 #### 3.3 계속하기 전 검증 - 작동했나? Yes → Phase 4 - 작동 안 했나? 새 가설 수립 - 위에 추가 수정을 쌓지 않는다 #### 3.4 모를 때 - "X를 이해하지 못하겠다"라고 말한다 - 아는 척하지 않는다 - 도움을 요청한다 - 더 조사한다 --- ### Phase 4: 구현 (Implementation) **증상이 아닌 근본 원인 수정:** #### 4.1 실패하는 테스트 케이스 작성 - 가장 단순한 재현 - 가능하면 자동화된 테스트 - 프레임워크 없으면 일회성 테스트 스크립트 - 수정 전 반드시 있어야 함 - TDD 스킬 참조: `superpowers:test-driven-development` #### 4.2 단일 수정 구현 - 식별된 근본 원인 해결 - 한 번에 하나의 변경만 - "이왕 하는 김에" 개선 금지 - 리팩토링 묶어서 하지 않기 #### 4.3 수정 검증 - 테스트가 이제 통과하나? - 다른 테스트가 깨지지 않았나? - 문제가 실제로 해결됐나? #### 4.4 수정이 작동하지 않으면 - **멈춘다** - 세어본다: 몇 번의 수정을 시도했나? - 3번 미만: Phase 1로 돌아가서 새 정보로 재분석 - **3번 이상: 멈추고 아키텍처 질문 (4.5단계)** - 아키텍처 논의 없이 수정 #4 시도 금지 #### 4.5 3회 이상 수정 실패: 아키텍처 질문 **아키텍처 문제를 나타내는 패턴:** - 각 수정이 다른 곳의 새로운 공유 상태/커플링/문제를 드러냄 - 수정에 "대규모 리팩토링"이 필요 - 각 수정이 다른 곳에서 새 증상을 만듦 **멈추고 기본을 질문:** - 이 패턴이 근본적으로 건전한가? - "관성으로 고수"하고 있는 건 아닌가? - 증상 수정 계속 vs 아키텍처 리팩토링? **더 많은 수정 시도 전에 사용자와 논의** 이것은 실패한 가설이 아니다 - 이것은 잘못된 아키텍처다. --- ## Red Flags - 멈추고 프로세스 따르기 이런 생각이 든다면: - "일단 빠른 수정하고, 나중에 조사하자" - "X를 바꿔보고 작동하는지 보자" - "여러 변경 추가하고, 테스트 돌리자" - "테스트 건너뛰고, 수동으로 확인하자" - "아마 X일 거야, 그거 고치자" - "완전히 이해는 못하지만 이게 작동할 수도" - "패턴은 X라고 하지만 다르게 적용하겠어" - "주요 문제들: [조사 없이 수정 나열]" - 데이터 흐름 추적 전에 솔루션 제안 - **"한 번 더 수정 시도" (이미 2회 이상 시도 후)** - **각 수정이 다른 곳에서 새 문제 드러냄** **이 모든 것의 의미: 멈춘다. Phase 1로 돌아간다.** **3회 이상 수정 실패 시:** 아키텍처 질문 (Phase 4.5 참조) --- ## 사용자 시그널: 잘못하고 있다는 표시 **이런 리디렉션 주시:** - "그거 안 일어나는 거 아냐?" - 확인 없이 가정함 - "우리한테 보여줄 건가요...?" - 증거 수집을 추가했어야 함 - "추측 그만해" - 이해 없이 수정 제안 중 - "이거 깊이 생각해봐" - 증상이 아닌 기본을 질문해야 함 - "막혔어?" (좌절) - 접근 방식이 작동 안 함 **이걸 보면:** 멈춘다. Phase 1로 돌아간다. --- ## 흔한 합리화 | 변명 | 현실 | |------|------| | "문제가 단순해서 프로세스 불필요" | 단순한 문제도 근본 원인이 있다. 프로세스는 단순한 버그에 빠르다. | | "긴급해서 프로세스 할 시간 없어" | 체계적 디버깅이 추측-확인 삽질보다 빠르다. | | "이것부터 해보고 조사하자" | 첫 수정이 패턴을 정한다. 처음부터 제대로. | | "수정 확인 후 테스트 작성할게" | 테스트 안 된 수정은 안 남는다. 테스트 먼저가 증명한다. | | "여러 수정 한꺼번에가 시간 절약" | 뭐가 작동했는지 분리 불가. 새 버그 원인. | | "레퍼런스 너무 길어, 패턴 적용할게" | 부분적 이해는 버그 보장. 완전히 읽기. | | "문제 보인다, 고치겠다" | 증상 보임 ≠ 근본 원인 이해. | | "한 번 더 수정 시도" (2회 이상 실패 후) | 3회 이상 실패 = 아키텍처 문제. 패턴 질문, 다시 수정 아님. | --- ## 빠른 참조 | 단계 | 핵심 활동 | 성공 기준 | |------|----------|----------| | **1. 근본 원인** | 에러 읽기, 재현, 변경 확인, 증거 수집 | 무엇이 왜인지 이해 | | **2. 패턴** | 동작 예시 찾기, 비교 | 차이점 식별 | | **3. 가설** | 이론 수립, 최소 테스트 | 확인 또는 새 가설 | | **4. 구현** | 테스트 작성, 수정, 검증 | 버그 해결, 테스트 통과 | --- ## "근본 원인 없음" 발견 시 체계적 조사 결과 문제가 정말로 환경, 타이밍 의존, 외부적임이 밝혀지면: 1. 프로세스를 완료했다 2. 조사 내용 문서화 3. 적절한 처리 구현 (재시도, 타임아웃, 에러 메시지) 4. 향후 조사를 위한 모니터링/로깅 추가 **하지만:** "근본 원인 없음" 케이스의 95%는 불완전한 조사다. --- ## 지원 기법 이 기법들은 체계적 디버깅의 일부이며 references 디렉토리에서 사용 가능: - **`root-cause-tracing.md`** - 콜 스택을 역추적하여 원래 트리거 찾기 - **`defense-in-depth.md`** - 근본 원인 발견 후 여러 레이어에 검증 추가 **관련 스킬:** - **verification-before-completion** - 성공 주장 전 수정이 작동했는지 검증 --- ## 실제 영향 디버깅 세션에서: - 체계적 접근: 수정까지 15-30분 - 무작위 수정 접근: 삽질에 2-3시간 - 첫 시도 수정률: 95% vs 40% - 새 버그 도입: 거의 없음 vs 흔함