본문으로 건너뛰기
기술스택 · stack/javascript/디버거로 코드를 멈추고 들여다보기

JavaScript · 3장 코드 품질과 도구

디버거로 코드를 멈추고 들여다보기

버그를 찾을 때 console.log를 여기저기 넣어 보는 건 누구나 해요. 하지만 어디에 넣어야 할지 모를 때는 코드를 원하는 줄에서 멈추고, 그 순간의 변수 값을 직접 들여다보는 게 훨씬 빨라요. 브라우저에 들어 있는 디버거 쓰는 법을 정리했어요.

#3.1 · Chrome · Edge 개발자 도구 기준

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에 보이는 값이에요.

①total += scores[i] 줄에 멈춰 한 바퀴씩 진행해요. i가 0 · 1 · 2일 때 total은 80 · 170 · 240이 되고, i가 3이 되자 scores[3]이 undefined라서 total이 NaN으로 바뀌어요.
동작단축키 (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은 계속 실행이에요.
  • 조건부 중단점은 조건이 참일 때만 멈추고, 로그포인트는 코드를 고치지 않고 값을 남겨요.
SIMPLE · 기술스택 / JavaScript — 3장 코드 품질과 도구