Skip to content

디버깅

디버깅은 프로그램의 오류를 찾고 해결하기 위해 실행 흐름을 분석하는 과정입니다. 브레이크포인트를 설정하고 코드 실행을 단계별로 확인하며, 변수 값과 함수 호출 흐름을 추적하는 등의 방법을 사용합니다.

디버깅 맛보기

간단하게 디버깅을 해볼까요?

아래 함수는 인수로 전달된 두 숫자의 합을 계산하고 반환하는 함수입니다.

function add(a, b) {
return a + b;
}

문제 발견하기

아래 input에는 숫자가 미리 입력되어 있습니다. 결과 버튼을 누르면 두 값을 add 함수로 더해 결과를 확인할 수 있습니다. 결과 버튼을 눌러볼까요?

+ =

예상한 값이 나왔나요? 결과가 나오지 않았다면 왜 그런지 디버깅을 해봅시다.

중단점 (Breakpoints) 지정하기

디버깅의 시작은 중단점을 지정하는 것부터 시작합니다. 중단점은 코드 실행을 일시적으로 멈추는 지점을 의미합니다. 중단점을 설정하면 해당 코드 라인에서 실행이 멈추고, 개발자는 변수 값과 호출 스택 등을 확인할 수 있습니다.

개발자도구를 열어 콘솔에 1 + 1 = 11 메시지가 있는지 확인하고, 해당 메시지의 오른쪽에 있는 02-debugging.js:3을 클릭해 소스 패널로 이동합니다.

소스 패널에 02-debugging.js 파일이 열리면, add 함수의 console.log를 호출하는 코드 왼쪽에 있는 라인 번호를 클릭해 브레이크포인트를 설정합니다. 라인 번호가 파란색으로 체크되면 브레이크포인트가 설정된 것입니다.

코드를 실행해 중단점에서 멈추기

이제 코드를 실행해 중단점에서 멈춰봅시다. 결과 버튼을 누르면 아까 중단점을 지정한 라인이 하이라이트 되며, 코드 실행이 중단됩니다. 화면 내 다양한 표시를 통해 코드가 멈췄음을 인지할 수 있습니다.

문제 찾기

값 확인하기

멈춘 지점에서 add 함수에 매개변수 ab, 그리고 ab의 합인 result의 값을 확인해봅시다. 우측 Scope 영역의 Local에서 각각의 값을 확인할 수 있습니다. 또한 중단점에서 코드 실행이 멈추면, 개발자는 다양한 정보를 확인할 수 있습니다. Scope 패널에서 ab의 값을 확인해봅시다. ab의 값이 문자열로 표시되는 것을 확인할 수 있습니다.

진짜 문자열인지 확인해볼까요? esc를 눌러 소스패널 아래로 드로어를 열고 콘솔을 열어봅니다. 그리고 콘솔의 입력 영역에 typeof atypeof b를 입력해 값의 타입을 확인해봅시다.

이를 통해 ab가 문자열로 전달되었기 때문에, + 연산자가 문자열을 더하는 연산을 해 결과가 원하는 대로 나오지 않았음을 확인할 수 있습니다.

콜스택 확인하기

매개변수에 문자열이 담겼있다는 말은 함수 호출 시 문자열이 전달되었다는 것을 의미합니다. 실제 어떻게 호출이 되는지를 확인하기 위해 Call Stack 패널을 확인해봅시다. Call Stack 패널은 현재 실행 중인 함수와 호출된 함수들의 흐름을 보여줍니다.

우측의 Call Stack 영역에서 add 함수 바로 밑에 있는 (anonymous)(익명 함수를 의미)를 클릭해봅니다. add 함수를 호출한 함수가 이 (anonymous)함수 입니다.

이제 익명함수가 add 함수를 호출하던 시점의 스코프가 보입니다. 코드와 함께 이 스코프를 보면 알 수 있는 건, add 함수 호출 시 넘기는 인수는 input 엘리먼트의 value 값임을 알 수 있습니다.

우리가 원하는건 숫자들의 합이기에 이 value 값들을 숫자로 적절하게 변환 후 add 함수에 전달하면 될 것 입니다.

디버깅 종료하기

이제 문제를 파악했으니 resume을 눌러 코드 실행을 마저 진행시켜 디버깅을 종료합니다.


중단점(Breakpoints)을 지정하는 다양한 방법

크롬 개발자 도구에서는 다양한 방식으로 중단점을 설정할 수 있습니다

라인 브레이크포인트(Line Breakpoints)

특정 코드 줄에서 실행을 멈춥니다. 코드 에디터에서 라인 숫자를 클릭해 지정합니다.

한 줄에 함수 실행문이 여러 개 있다면 각 실행문마다 중단점을 지정할 수 있습니다.

함수 선언의 가장 끝 라인에 중단점을 지정하면 함수가 종료되기 직전에 중단되며, 스코프에서 함수의 반환값을 확인할 수 있습니다.

샘플 코드에서 8번째 라인을 클릭해 중단점을 지정한 후, 함수를 실행해봅시다.

debugger 키워드

코드에 작성하는 debugger 키워드는 개발자 도구에서 라인 브레이크포인트를 지정하는 것과 같은 효과를 가집니다. 추가적으로 개발자도구 UI에서 라인 브레이크포인트를 지정할 필요 없이 코드에 debugger 키워드를 작성하면 해당 라인에서 코드 실행이 중단됩니다.

조건부 브레이크포인트(Conditional Breakpoints)

라인 브레이크포인트의 하나로 특정 조건이 충족될 때만 실행을 멈춥니다. 브레이크포인트를 설정한 후, 마우스 우클릭 > Edit breakpoint을 클릭해 조건을 입력합니다.

Logpoints

Logpoints는 코드 실행을 중지시키지는 않는 대신 코드 실행 중에 콘솔에 로그를 출력하는 데 사용됩니다.

예외 브레이크포인트(Exception Breakpoints)

예외가 발생하는 순간 코드 실행을 멈춥니다. 예외 브레이크포인트는 예외가 발생하는 순간 코드 실행을 멈추는 데 사용됩니다.

예외는 caught와 uncaught로 나뉩니다. caught 예외는 try catch 블록에서 처리되는 예외이며, uncaught 예외는 처리되지 않은 예외입니다.

XHR/페치 브레이크포인트(XHR/Fetch Breakpoints)

XMLHttpRequest(XHR) 또는 fetch 요청이 발생할 때 코드를 중단시킬 수 있습니다.

모든 XHR 또는 fetch 요청마다 멈추게하거나 특정 url로 요청이 발생할 때만 중단시킬 수 있습니다.

특정 url로 요청이 발생할 때만 중단시키려면, + 아이콘을 누르고 url 조건을 입력합니다.

DOM 브레이크포인트(DOM Breakpoints)

특정 DOM 요소가 변경될 때 실행을 멈춥니다.

다음 3가지 변경에 대해 중단점을 설정할 수 있습니다.

  • subtree modifications: 요소의 자식 요소가 변경될 때
  • attribute modifications: 요소의 속성이 변경될 때
  • node removal: 요소가 제거될 때

Event Listener Breakpoints

특정 이벤트가 발생했을 때 실행되는 이벤트 리스너 코드에서 일시중지하고 싶을 때 이벤트 리스너 중단점을 사용합니다.

CSP Violation Breakpoints

content security policy(CSP) 위반이 발생했을 때 실행을 중지합니다.

특정 라인에서 절대 멈추지 않게 하기

특정 라인에서 코드 실행이 멈추지 않도록 설정할 수 있습니다. exception 브레이크포인트가 설정되어 있을 때, 특정 예외는 중단이 되지 않도록 하고 싶을 때 등에 사용합니다.

중단점을 삭제, 수정, 비활성화하기

브레이크포인트들에 대해 삭제 또는 비활성화를 할 수 있습니다.

라인 브레이크포인트는 라인 번호나 함수 호출문 왼쪽에 있는 브레이크포인트 아이콘을 클릭하거나 우클릭 해 삭제 또는 비활성화를 할 수 있습니다.

또는 우측 Breakpoints 영역에서 각 브레이크포인트를 삭제, 수정, 비활성화를 할 수 있습니다.


디버깅 중에 확인할 수 있는 정보들

디버깅 중에는 다양한 정보를 확인할 수 있습니다.

Console

콘솔 패널 또는 드로어의 콘솔 탭을 통해 코드 실행 중에 변수 값을 확인하거나 코드를 실행할 수 있습니다.

Scope 패널

현재 실행 중인 코드의 스코프를 확인할 수 있습니다.

Call Stack 패널

현재 실행 중인 함수의 호출 스택을 확인할 수 있습니다.

코드를 단계별로 실행하기

중단점을 활용해 코드 실행을 멈춘 후, 개발자는 코드 실행 흐름을 단계별로 제어할 수 있습니다. 크롬 개발자 도구에서는 다음과 같은 Step 기능을 제공합니다:

디버깅 화면 내 버튼들

  • Resume script Execution: 자바스크립트의 실행을 재개합니다.
  • Step over next function call: 현재 줄을 실행하되, 함수 내부로 들어가지 않고 다음 줄로 이동합니다.
  • Step into next function call: 현재 줄을 실행하며, 함수 호출이 있을 경우 해당 함수 내부로 이동합니다.
  • Step out of current function: 현재 함수 실행을 마치고 호출한 상위 함수로 돌아갑니다.
  • Step: 다음 줄로 이동합니다.
  • Deactivate breakpoints: 모든 브레이크포인트를 비활성화합니다.

실습

지금까지 배운 내용을 바탕으로 디버깅을 실습해보겠습니다. 아래 calculate 함수는 인수로 두 숫자와 연산자를 입력받아 두 숫자의 연산 결과를 반환하는 함수입니다.

소스 패널 > 네비게이터 > Snippets 탭에서 New snippet을 누른 후, 아래 코드를 붙이고 저장한 후 실행해봅시다.

function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a ** b;
}
function calculate(a, b, operator) {
let result;
if (operator === '+') {
result = add(a, b);
} else if (operator = '-') {
result = subtract(a, b);
} else if (operator === '*') {
result = multiply(a, b);
} else {
console.error("🚨 오류: 지원되지 않는 연산자입니다.");
return;
}
console.log(`${a} ${operator} ${b} = ${result}`);
return result;
}
calculate(10, 5, '+');
calculate(20, 7, '-');
calculate(2, 3, '*');

기대하는 동작이 일어났나요? 콘솔에 찍힌 메시지를 보아하니 -* 연산을 전달해도 +로 계산되는 것 같습니다.

실행: calculate(10, 5, '+');
결과: 10 + 5 = 15
실행: calculate(20, 7, '-');
결과: 20 + 7 = 27
실행: calculate(2, 3, '*');
결과: 2 + 3 = 5

디버깅을 통해 문제를 해결해봅시다.

추가 내용

비동기 스택 트레이스

크롬 개발자 도구에서는 비동기 작업의 실행 흐름을 추적할 수 있습니다. 이를 통해 해당 비동기 작업이 어떤 함수 또는 로직에서 시작되었는지 인과관계를 파악 할 수 있습니다.

다음 코드를 스니펫에 넣고 실행해 콜스택을 확인해보세요.

function testAsyncStackTrace() {
const closureValue = 'closureValue';
function setTimeoutCallbackInner() {
console.log(closureValue);
console.trace();
debugger;
}
function setTimeoutCallback() {
setTimeoutCallbackInner();
}
setTimeout(setTimeoutCallback, 1000);
}
testAsyncStackTrace();

Live Editing

개발자도구에서 수정한 코드들은 수정이 가능하고 실제 페이지에 적용됩니다. 이를 통해 코드를 수정하고 즉시 결과를 확인할 수 있습니다. 단 임시 작업이기에 페이지를 새로고침하면 수정한 내용은 사라집니다.

크롬개발자도구의 Workspace 기능을 통해 로컬 디렉토리를 연결하면, 개발자도구에서 수정한 코드가 실제 파일에 반영되어 저장됩니다.

저는 기본적으로 Workspace 기능을 사용하지는 않는데요, 코드 수정은 가능한 제가 작업 중인 IDE에서만 진행해 IDE의 기능(eslint, tsc, prettier 등과의 연동 극대화)을 최대한 활용하고자 하는 편입니다.

궁금하신 분들은 Workspace 기능에 대해 한번 시도해보세요.