JavaScript · 3장 코드 품질과 도구
디버거로 코드를 멈추고 들여다보기
버그를 찾을 때 console.log를 여기저기 넣어 보는 건 누구나 해요. 하지만 어디에 넣어야 할지 모를 때는 코드를 원하는 줄에서 멈추고, 그 순간의 변수 값을 직접 들여다보는 게 훨씬 빨라요. 브라우저에 들어 있는 디버거 쓰는 법을 정리했어요.
01. console.log만으로는 부족할 때
아래 함수는 점수의 평균을 구하려는 건데 NaN이 나와요. 원인을 찾으려면 반복이 도는 동안 i와 total이 어떻게 바뀌는지 봐야 해요.
function average(scores) {
let total = 0;
for (let i = 0; i <= scores.length; i++) {
total += scores[i];
}
return total / scores.length;
}
console.log(average([80, 90, 70])); // NaN
console.log로 찾으려면 의심 가는 곳마다 출력을 넣고, 다시 실행하고, 결과를 보고, 또 넣어야 해요. 다 고친 뒤에는 넣은 출력을 지우는 것도 일이에요. 디버거는 코드를 고치지 않고도 원하는 줄에서 실행을 멈추고, 그 순간의 모든 변수를 보여 줘요.
02. 중단점 걸기
개발자 도구(여는 법)의 Sources 패널을 열고 왼쪽에서 파일을 고른 뒤, 멈추고 싶은 줄의 줄 번호를 클릭하면 파란 표시가 생겨요. 이게 중단점(breakpoint)이에요. 페이지를 다시 실행하면 코드가 그 줄에 닿는 순간 멈춰요. Firefox는 같은 기능이 Debugger 패널에 있어요.
코드 안에 debugger;라고 적어도 같은 효과가 나요. 개발자 도구가 열려 있을 때만 멈추고, 닫혀 있으면 아무 일도 하지 않아요.
for (let i = 0; i <= scores.length; i++) {
debugger; // 개발자 도구가 열려 있으면 여기서 멈춰요
total += scores[i];
}
멈춘 동안 오른쪽에 세 가지 정보가 보여요.
| 창 | 보여 주는 것 |
|---|---|
| Scope | 지금 줄에서 보이는 변수와 값 (함수 안 · 바깥 · 전역) |
| Watch | 직접 적어 둔 식의 값. 한 줄씩 진행할 때마다 다시 계산돼요 (예: scores[i]) |
| Call Stack | 어떤 함수가 어떤 함수를 불러서 여기까지 왔는지 |
03. 한 줄씩 실행하기
멈춘 뒤에는 버튼이나 단축키로 한 줄씩 앞으로 나아가며 값이 바뀌는 걸 지켜봐요. 아래 그림은 average([80, 90, 70])를 한 바퀴씩 진행할 때 Scope와 Watch에 보이는 값이에요.
| 동작 | 단축키 (Windows · Linux / macOS) | 하는 일 |
|---|---|---|
| 계속 실행 | F8 / F8 | 다음 중단점까지 실행해요 |
| 다음 줄 (Step over) | F10 / F10 | 지금 줄을 실행하고 다음 줄에서 멈춰요. 함수를 불러도 그 안으로 들어가지 않아요 |
| 안으로 (Step into) | F11 / F11 | 지금 줄이 부르는 함수 안으로 들어가요 |
| 밖으로 (Step out) | Shift+F11 | 지금 함수를 끝까지 실행하고 부른 쪽으로 돌아가요 |
그림처럼 진행하면 범인이 바로 보여요. 배열의 마지막 번호는 length - 1인데 조건이 <=라서 한 번 더 돌았고, 없는 칸 scores[3](undefined)을 더해서 NaN이 됐어요. i < scores.length로 고치면 80이 나와요.
04. 조건부 중단점과 로그포인트
반복이 1000번 도는데 문제가 997번째에서만 생긴다면, 매번 멈추는 중단점은 고역이에요. 줄 번호를 오른쪽 클릭하면 두 가지 특별한 중단점을 걸 수 있어요.
- 조건부 중단점: 식을 적어 두면 그 식이 참일 때만 멈춰요. 예:
i === scores.length,Number.isNaN(total) - 로그포인트: 멈추지 않고 메시지만 콘솔에 남겨요. 예:
'i', i, 'total', total. 코드에console.log를 넣었다 지울 필요가 없어요.
이 밖에도 오류가 나는 순간 자동으로 멈추게 하는 설정(예외에서 멈추기)과, 버튼 클릭 같은 이벤트가 일어날 때 멈추는 중단점도 있어요. 화면 요소와 이벤트는 2부에서 다뤄요.
가장 큰 값을 찾는 함수인데, 음수만 넣으면 틀린 답이 나와요. return max; 줄에 중단점을 걸었다고 생각하고, 반복이 도는 동안 max가 어떻게 변하는지 적어 보세요. 그다음 -2가 나오게 고쳐 보세요.
function findMax(numbers) {
let max = 0;
for (const n of numbers) {
if (n > max) max = n;
}
return max;
}
console.log(findMax([-5, -2, -9])); // -2가 나와야 해요
해답 보기
max는 처음 0에서 한 번도 바뀌지 않아요. -5, -2, -9 모두 0보다 작아서 n > max가 늘 거짓이거든요. 출발값 0이 "가장 큰 값 후보"로 끼어든 게 원인이에요. 첫 번째 값에서 출발하거나, 어떤 수보다도 작은 -Infinity에서 출발해요.
let max = -Infinity; // 또는 let max = numbers[0];
05. 실전 포인트
- 언제 쓰나요? 값이 어디서 이상해지는지 모를 때, 함수가 어떤 순서로 불리는지 알고 싶을 때 디버거를 써요. Node.js 코드도 VS Code의 디버거(JavaScript Debug Terminal 등)로 같은 방식으로 멈출 수 있어요.
- 대신 쓰는 것: 어디가 문제인지 이미 짐작이 가면
console.log한 줄이 빠를 때도 있어요. 다만 넣었다 지우는 일이 반복되면 로그포인트로 바꿔요. 서비스에서 사용자에게만 생기는 오류는 디버거로 볼 수 없으니, 오류 수집 도구(로그)를 써요. - 흔한 실수:
debugger;나 디버깅용console.log를 지우지 않고 커밋하는 것이에요. ESLint의 추천 규칙(no-debugger)이debugger;를 찾아 줘요(ESLint).
정리
- Sources 패널에서 줄 번호를 클릭하거나 코드에
debugger;를 적으면 그 줄에서 멈춰요. - 멈춘 동안 Scope · Watch · Call Stack으로 변수 값과 호출 순서를 봐요.
F10은 다음 줄,F11은 함수 안으로,Shift+F11은 밖으로,F8은 계속 실행이에요.- 조건부 중단점은 조건이 참일 때만 멈추고, 로그포인트는 코드를 고치지 않고 값을 남겨요.