JavaScript
JavaScript 디버깅 완벽 가이드
console.log를 넘어서 효과적으로 버그를 찾는 방법. Chrome DevTools 활용법을 중심으로 알아봅니다.
디버깅은 개발 시간의 상당 부분을 차지합니다. 효과적인 디버깅 기술을 익히면 개발 생산성이 크게 향상됩니다.
console 메서드 활용
console.log만 쓰고 있다면 다양한 메서드를 활용해보세요.
// 객체를 표 형태로 출력
const users = [
{ name: '홍길동', age: 25 },
{ name: '김영희', age: 30 }
];
console.table(users);
// 그룹으로 묶기
console.group('사용자 정보');
console.log('이름:', user.name);
console.log('나이:', user.age);
console.groupEnd();
// 조건부 로깅
console.assert(value > 0, '값은 0보다 커야 합니다');
// 실행 시간 측정
console.time('API 호출');
await fetchData();
console.timeEnd('API 호출');
// 경고와 에러
console.warn('곧 지원이 중단됩니다');
console.error('치명적인 오류!');
// 호출 스택 출력
console.trace('여기서 호출됨');
Chrome DevTools 활용
Sources 패널
코드 실행을 일시 중지하고 변수 값을 확인할 수 있습니다.
// 코드에서 직접 중단점 설정
function processData(data) {
debugger; // 여기서 실행이 멈춤
return data.map(item => item.value);
}
DevTools에서 중단점을 설정하는 것이 더 유연합니다.
- 클릭으로 중단점 설정/해제
- 조건부 중단점: 특정 조건일 때만 멈춤
- 로그 포인트: 코드 수정 없이 로그 출력
중단점 종류
일반 중단점: 해당 줄에서 항상 멈춤
조건부 중단점: user.id === 123 같은 조건 설정
DOM 중단점: 특정 DOM 요소가 변경될 때 멈춤
XHR/Fetch 중단점: 특정 URL 요청 시 멈춤
이벤트 리스너 중단점: 특정 이벤트 발생 시 멈춤
예외 중단점: 예외 발생 시 멈춤
Network 패널
API 요청을 상세히 분석합니다.
- 요청/응답 헤더 확인
- 요청 본문과 응답 데이터 확인
- 요청 시간 분석
- 요청 필터링 (XHR, JS, CSS 등)
Performance 패널
성능 문제를 분석합니다.
- 함수 실행 시간
- 렌더링 병목
- 메모리 사용량
흔한 버그 패턴
비동기 처리 버그
// 버그: items가 항상 빈 배열
async function loadData() {
let items = [];
data.forEach(async (item) => {
const result = await fetchItem(item.id);
items.push(result);
});
return items; // forEach는 await를 기다리지 않음
}
// 수정
async function loadData() {
const items = await Promise.all(
data.map(item => fetchItem(item.id))
);
return items;
}
클로저 관련 버그
// 버그: 모든 버튼이 5를 출력
for (var i = 0; i < 5; i++) {
buttons[i].onclick = function() {
console.log(i); // 항상 5
};
}
// 수정 1: let 사용
for (let i = 0; i < 5; i++) {
buttons[i].onclick = function() {
console.log(i);
};
}
// 수정 2: 클로저로 값 캡처
for (var i = 0; i < 5; i++) {
buttons[i].onclick = (function(index) {
return function() {
console.log(index);
};
})(i);
}
this 바인딩 버그
// 버그: this가 undefined
class Counter {
count = 0;
handleClick() {
this.count++; // 에러!
}
}
const counter = new Counter();
button.onclick = counter.handleClick;
// 수정 1: bind 사용
button.onclick = counter.handleClick.bind(counter);
// 수정 2: 화살표 함수로 정의
class Counter {
count = 0;
handleClick = () => {
this.count++;
};
}
React 디버깅
React DevTools
컴포넌트 트리, props, state를 시각적으로 확인합니다. 특정 컴포넌트의 props나 state를 직접 수정해볼 수도 있습니다.
리렌더링 원인 찾기
// 리렌더링 시 로그 출력
useEffect(() => {
console.log('컴포넌트 리렌더링됨');
});
// 어떤 의존성이 변경되었는지 확인
function useWhyDidUpdate(name, props) {
const previousProps = useRef();
useEffect(() => {
if (previousProps.current) {
const changedProps = Object.entries(props).filter(
([key, value]) => previousProps.current[key] !== value
);
if (changedProps.length > 0) {
console.log(`[${name}] 변경된 props:`, changedProps);
}
}
previousProps.current = props;
});
}
체계적인 디버깅 프로세스
- 버그 재현하기: 어떤 조건에서 발생하는지 파악
- 범위 좁히기: 문제가 있는 코드 영역 특정
- 가설 세우기: 원인이 무엇인지 추측
- 검증하기: 로그나 중단점으로 가설 확인
- 수정 후 테스트: 버그가 해결되었는지 확인
마무리
디버깅은 기술입니다. 연습할수록 늘어납니다. console.log 대신 DevTools의 중단점을 활용하고, 체계적인 접근 방식을 익히면 버그를 더 빠르게 찾을 수 있습니다.