Chrome DevTools로 비동기 자바스크립트 디버깅

소개

JavaScript를 고유하게 만드는 강력한 기능은 콜백 함수를 통해 비동기식으로 작동하는 기능입니다. 비동기 콜백을 할당하면 이벤트 기반 코드를 작성할 수 있지만 JavaScript가 선형 방식으로 실행되지 않으므로 버그 추적이 진행 상태가 됩니다.

다행히 이제 Chrome DevTools에서 비동기 JavaScript 콜백의 전체 호출 스택을 볼 수 있습니다.

비동기 호출 스택에 관한 간략한 티저 개요입니다.
비동기 호출 스택에 대한 간략한 티저 개요입니다. 이 데모의 흐름에 대해서는 곧 자세히 살펴보겠습니다.

DevTools에서 비동기 호출 스택 기능을 사용 설정하면 다양한 시점에서 웹 앱의 상태를 자세히 살펴볼 수 있습니다. 일부 이벤트 리스너(setInterval, setTimeout, XMLHttpRequest, 프로미스, requestAnimationFrame, MutationObservers 등)의 전체 스택 트레이스를 확인하세요.

스택 트레이스를 따라가면서 특정 런타임 실행 시점에 변수 값을 분석할 수도 있습니다. 마치 시계 표현을 위한 타임머신과 같은 역할을 합니다.

이 기능을 사용 설정하고 다음 몇 가지 시나리오를 살펴보겠습니다.

Chrome에서 비동기 디버깅 사용 설정하기

Chrome에서 이 새로운 기능을 사용 설정하여 사용해 보세요. Chrome Canary DevTools의 Sources 패널로 이동합니다.

오른쪽의 Call Stack 패널 옆에 'Async'를 위한 새로운 체크박스가 있습니다. 체크박스를 전환하여 비동기 디버깅을 켜거나 끕니다. 사용 설정하면 설정하고 싶지 않을 수도 있습니다.

비동기 기능을 사용 설정 또는 사용 중지로 전환합니다.

지연된 타이머 이벤트 및 XHR 응답 캡처

이전에 Gmail에서 다음과 같은 내용을 본 적이 있을 것입니다.

Gmail에서 이메일 전송을 다시 시도하는 중입니다.

요청을 보내는 데 문제가 있으면 (서버에 문제가 있거나 클라이언트 측에 네트워크 연결 문제가 있는 경우) Gmail에서 짧은 시간 초과 후 자동으로 메시지 재전송을 시도합니다.

비동기 호출 스택이 지연된 타이머 이벤트 및 XHR 응답을 분석하는 데 어떻게 도움이 되는지 알아보기 위해 모의 Gmail 예를 사용하여 이 흐름을 다시 만들었습니다. 전체 JavaScript 코드는 위 링크에서 확인할 수 있지만 흐름은 다음과 같습니다.

모의 Gmail 예의 흐름 차트
위의 다이어그램에서 파란색으로 강조표시된 메서드는 이 새로운 DevTool 기능이 비동기식으로 작동하기 때문에 이 기능이 가장 유용한 방법입니다.

이전 버전의 DevTools에서 호출 스택 패널만 살펴보는 경우 postOnFail() 내의 중단점에서는 postOnFail()가 어디에서 호출되었는지에 관한 정보를 거의 확인할 수 없습니다. 비동기 스택을 사용 설정할 때는 차이점을 살펴보세요.

이전
비동기 호출 스택 없이 모의 Gmail 예에 설정된 중단점
비동기가 사용 설정되지 않은 호출 스택 패널

여기서 postOnFail()가 AJAX 콜백에서 시작되었지만 추가 정보는 없는 것을 확인할 수 있습니다.

비동기 호출 스택을 사용하여 모의 Gmail 예시에서 설정된 중단점
비동기가 사용 설정된 호출 스택 패널

여기서 XHR이 submitHandler()에서 시작되었음을 알 수 있습니다. 다행입니다.

비동기 호출 스택을 사용 설정하면 전체 호출 스택을 보고 요청이 submitHandler() (제출 버튼 클릭 후 발생) 또는 retrySubmit() (setTimeout() 지연 후 발생)에서 시작되었는지 쉽게 확인할 수 있습니다.

submitHandler()
비동기 호출 스택을 사용하여 모의 Gmail 예시에서 설정된 중단점
retrySubmit()
비동기 호출 스택이 포함된 모의 Gmail 예에 설정된 또 다른 중단점

비동기식으로 표현식 감시

전체 호출 스택을 살펴보면 감시된 표현식도 당시의 상태를 반영하도록 업데이트됩니다.

aysnc 호출 스택과 함께 watch 표현식을 사용하는 예

이전 범위의 코드 평가

표현식을 관찰하는 것 외에도 DevTools JavaScript 콘솔 패널에서 바로 이전 범위의 코드와 상호작용할 수 있습니다.

여러분이 Dr. Who라고 상상해 보세요. Tardis에 들어가기 전의 시계를 '지금'과 비교하는 데 약간의 도움이 필요합니다. DevTools 콘솔에서 다양한 실행 지점에서 값을 쉽게 평가하고 저장하고 계산할 수 있습니다.

aysnc 호출 스택과 함께 JavaScript 콘솔을 사용하는 예
JavaScript 콘솔을 비동기 호출 스택과 함께 사용하여 코드를 디버그합니다. 위의 데모는 여기에서 찾을 수 있습니다.

DevTools 내에서 식을 조작하면 소스 코드로 다시 전환하고, 수정하고, 브라우저를 새로 고치는 시간을 절약할 수 있습니다.

체이닝된 프로미스 해결 방법 해결

비동기 호출 스택 기능을 사용 설정하지 않으면 이전의 모의 Gmail 흐름을 해결하기가 어렵다고 생각했을 때 체이닝된 프로미스와 같은 더 복잡한 비동기 흐름을 사용하면 얼마나 더 어려울지 상상할 수 있나요? 제이크 아치볼드가 작성한 자바스크립트 프로미스 튜토리얼의 마지막 예를 다시 확인해 보겠습니다.

다음은 Jake의 async-best-example.html 예에서 호출 스택을 이동하는 애니메이션입니다.

이전
비동기 호출 스택이 없는 프로미스 예제에 설정된 중단점
비동기가 사용 설정되지 않은 호출 스택 패널

프라미스를 디버그하려고 할 때 Call Stack 패널에 표시되는 정보가 매우 짧습니다.

비동기 호출 스택을 사용하는 프로미스 예제에 설정된 중단점
비동기가 사용 설정된 호출 스택 패널

잘하셨습니다. 그 약속이요. 다수의 콜백.

웹 애니메이션 통계 확인

HTML5Rocks 자료실에 대해 좀 더 자세히 살펴보겠습니다. 폴 루이스의 requestAnimationFrame을 사용한 Leaner, Meaner, Faster Animations를 기억하시나요?

requestAnimationFrame 데모를 열고 post.html의 update() 메서드 시작 부분 (874행 주변)에 중단점을 추가합니다. 비동기 호출 스택을 사용하면 시작 스크롤 이벤트 콜백까지 뒤로 이동하는 기능을 포함하여 requestAnimationFrame에 관한 훨씬 많은 정보를 얻을 수 있습니다.

이전
비동기 호출 스택이 없는 requestAnimationFrame 예제에 설정된 중단점
비동기가 사용 설정되지 않은 호출 스택 패널
비동기 호출 스택이 있는 requestAnimationFrame 예제에 설정된 중단점
이후
비동기가 사용 설정된 경우

MutationObserver를 사용할 때 DOM 업데이트 추적

MutationObserver를 사용하면 DOM의 변경사항을 관찰할 수 있습니다. 이 간단한 예에서는 버튼을 클릭하면 새 DOM 노드가 <div class="rows"></div>에 추가됩니다.

데모.html의 nodeAdded() (31행) 내에 중단점을 추가합니다. 비동기 호출 스택을 사용 설정하면 이제 addNode()를 통해 초기 클릭 이벤트까지 호출 스택을 다시 걸 수 있습니다.

이전
비동기 호출 스택이 없는 mutObserver 예시에 설정된 중단점
비동기가 사용 설정되지 않은 호출 스택 패널
비동기 호출 스택을 사용하는 mutObserver 예시에 설정된 중단점
이후
비동기가 사용 설정된 경우

비동기 호출 스택에서 자바스크립트 디버깅 팁

함수 이름 지정

모든 콜백을 익명 함수로 할당하려는 경우 대신 호출 스택을 더 쉽게 볼 수 있도록 이름을 지정하는 것이 좋습니다.

예를 들어 다음과 같은 익명 함수를 살펴보겠습니다.

window.addEventListener('load', function() {
  // do something
});

windowLoaded()와 같은 이름을 지정합니다.

window.addEventListener('load', function <strong>windowLoaded</strong>(){
  // do something
});

로드 이벤트가 실행되면 DevTools 스택 트레이스에 암호화된 '(익명 함수)' 대신 함수 이름과 함께 표시됩니다. 이렇게 하면 스택 트레이스에서 발생하는 상황을 한눈에 확인하기가 훨씬 쉬워집니다.

이전
익명 함수입니다.
이름이 지정된 함수
이후

더 살펴보기

요약하면 다음은 DevTools에서 전체 호출 스택을 표시하는 비동기 콜백입니다.

  • 타이머: setTimeout() 또는 setInterval()가 초기화된 곳으로 돌아갑니다.
  • XHR: xhr.send()가 호출된 위치로 돌아갑니다.
  • 애니메이션 프레임: requestAnimationFrame가 호출된 곳으로 돌아갑니다.
  • 프로미스: 프로미스가 해결된 위치로 돌아갑니다.
  • Object.observe: 관찰자 콜백이 원래 바인딩된 위치로 돌아갑니다.
  • MutationObservers: 변형 관찰자 이벤트가 실행된 위치로 돌아갑니다.
  • window.postMessage(): 프로세스 내 메시지 호출을 살펴봅니다.
  • DataTransferItem.getAsString()
  • FileSystem API
  • IndexedDB
  • WebSQL
  • addEventListener()를 통한 대상 DOM 이벤트: 이벤트가 실행된 위치로 돌아갑니다. 성능상의 이유로 모든 DOM 이벤트에 비동기 호출 스택 기능을 사용할 수 있는 것은 아닙니다. 현재 사용 가능한 이벤트의 예로는 'scroll', 'hashchange', 'selectionchange'가 있습니다.
  • addEventListener()를 통한 멀티미디어 이벤트: 이벤트가 실행된 위치로 돌아갑니다. 사용 가능한 멀티미디어 이벤트에는 오디오 및 동영상 이벤트 (예: '재생', '일시중지', 'ratechange'), WebRTC MediaStreamTrackList 이벤트 (예: 'addtrack', 'removetrack'), MediaSource 이벤트 (예: 'sourceopen')가 있습니다.

JavaScript 콜백의 풀 스택 트레이스를 볼 수 있으려면 이러한 헤어가 머리에 남아 있어야 합니다. DevTools의 이 기능은 여러 비동기 이벤트가 서로 관련하여 발생하거나 비동기 콜백 내에서 포착되지 않은 예외가 발생할 때 특히 유용합니다.

Chrome에서 이 기능을 사용해 보세요. 이 새로운 기능에 관한 의견이 있다면 Chrome DevTools 버그 추적기 또는 Chrome DevTools 그룹에 의견을 남겨주세요.