Debugging Workflow Skill
체계적인 디버깅 워크플로우 가이드
When to Use
- 버그 발생 시 체계적인 해결이 필요할 때
- 에러 원인 파악이 필요할 때
- 디버깅 프로세스 가이드가 필요할 때
🔍 디버깅 5단계 프로세스
┌─────────────────────────────────────────────────────────────┐
│ │
│ 디버깅 5단계 프로세스 │
│ │
│ 1️⃣ 재현 → 2️⃣ 격리 → 3️⃣ 분석 → 4️⃣ 수정 → 5️⃣ 검증 │
│ │
└─────────────────────────────────────────────────────────────┘
📋 Step 1: 재현 (Reproduce)
원칙
버그를 100% 재현할 수 있어야 디버깅 시작
재현 불가능한 버그는 로그 수집부터
체크리스트
[ ] 버그 발생 조건 기록
[ ] 재현 단계 문서화
[ ] 환경 정보 수집 (OS, 브라우저, 버전)
[ ] 입력 데이터 저장
[ ] 스크린샷/비디오 캡처
재현 템플릿
## 버그 재현 정보
**환경:**
- OS: Windows 11
- Browser: Chrome 120
- App Version: 1.2.3
**재현 단계:**
1. 로그인 페이지 접속
2. 이메일 입력: test@example.com
3. 비밀번호 입력: (빈 값)
4. 로그인 버튼 클릭
**예상 결과:** 비밀번호 필수 오류 표시
**실제 결과:** 페이지 크래시
📋 Step 2: 격리 (Isolate)
원칙
문제 범위를 좁혀서 정확한 위치 파악
이진 탐색으로 효율적 격리
격리 기법
1. 이진 탐색 (Binary Search)
// 코드의 절반씩 주석 처리하며 범위 좁히기
function processData(data) {
// === 전반부 ===
const validated = validate(data);
const transformed = transform(validated);
// === 후반부 (여기서 문제 발생?) ===
const enriched = enrich(transformed);
const result = format(enriched);
return result;
}
2. 로그 포인트
function suspiciousFunction(input) {
console.log('🔍 [1] input:', JSON.stringify(input));
const step1 = processStep1(input);
console.log('🔍 [2] after step1:', JSON.stringify(step1));
const step2 = processStep2(step1);
console.log('🔍 [3] after step2:', JSON.stringify(step2));
return step2;
}
📋 Step 3: 분석 (Analyze)
분석 도구
// Console
console.log() // 값 출력
console.table() // 배열/객체 테이블 표시
console.trace() // 호출 스택 출력
console.time() // 성능 측정
// Debugger
debugger; // 브레이크포인트 설정
일반적인 버그 패턴
// 1. Null/Undefined 접근
user.profile.name // user 또는 profile이 undefined
// 해결: Optional chaining
user?.profile?.name
// 2. 비동기 타이밍
setData(newData);
console.log(data); // 아직 이전 값!
// 해결: useEffect 또는 await
// 3. 클로저 문제
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // 모두 5 출력
}
// 해결: let 사용
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100); // 0,1,2,3,4
}
// 4. 참조 vs 복사
const copy = original; // 같은 참조!
copy.value = 'changed'; // original도 변경됨
// 해결: 깊은 복사
const copy = structuredClone(original);
📋 Step 4: 수정 (Fix)
원칙
1. 최소한의 변경으로 수정
2. 근본 원인 해결 (증상만 숨기지 않기)
3. 사이드 이펙트 고려
수정 패턴
// Before: 취약한 코드
function getUsername(user) {
return user.profile.name;
}
// After: 방어적 코드
function getUsername(user) {
if (!user?.profile?.name) {
return 'Unknown';
}
return user.profile.name;
}
📋 Step 5: 검증 (Verify)
체크리스트
[ ] 원래 버그가 수정되었는가?
[ ] 재현 단계로 다시 테스트
[ ] 관련 기능에 사이드 이펙트 없는가?
[ ] 엣지 케이스 테스트
[ ] 회귀 테스트 추가
회귀 테스트 작성
// 버그 재현 테스트 → 수정 후 통과해야 함
describe('Bug #123: Login crash with empty password', () => {
it('should show error message instead of crash', () => {
render(<LoginForm />);
fireEvent.change(screen.getByLabelText('Email'), {
target: { value: 'test@example.com' }
});
fireEvent.click(screen.getByText('Login'));
expect(screen.getByText('Password is required')).toBeInTheDocument();
});
});
🚫 디버깅 안티패턴
❌ 추측으로 수정하기 (확인 없이)
❌ 증상만 숨기기 (try-catch로 무시)
❌ 여러 곳 동시 수정 (원인 파악 어려움)
❌ 로그 남기고 삭제 안 함
❌ 테스트 없이 배포
📋 디버깅 체크리스트
시작 전:
[ ] 버그 재현 가능?
[ ] 환경 정보 수집?
분석 중:
[ ] 로그 추가했나?
[ ] 범위 격리했나?
[ ] 스택 트레이스 확인?
수정 후:
[ ] 원래 버그 해결?
[ ] 사이드 이펙트 없나?
[ ] 회귀 테스트 추가?