소개
JavaScript를 고유하게 만드는 강력한 기능은 콜백 함수를 통해 비동기식으로 작동하는 기능입니다. 비동기 콜백을 할당하면 이벤트 기반 코드를 작성할 수 있지만 JavaScript가 선형 방식으로 실행되지 않으므로 버그 추적 작업이 매우 번거로워집니다.
다행히 이제 Chrome DevTools에서 비동기 JavaScript 콜백의 전체 호출 스택을 볼 수 있습니다.

DevTools에서 비동기 호출 스택 기능을 사용 설정하면 다양한 시점에서 웹 앱의 상태를 자세히 살펴볼 수 있습니다. 일부 이벤트 리스너, setInterval
, setTimeout
, XMLHttpRequest
, 프로미스, requestAnimationFrame
, MutationObservers
등의 전체 스택 트레이스를 탐색합니다.
스택 트레이스를 탐색할 때 런타임 실행의 특정 지점에서 변수의 값을 분석할 수도 있습니다. 시계 화면을 위한 타임머신과도 같습니다.
이 기능을 사용 설정하고 이러한 시나리오 중 몇 가지를 살펴보겠습니다.
Chrome에서 비동기 디버깅 사용 설정
Chrome에서 이 새로운 기능을 사용 설정하여 사용해 보세요. Chrome Canary DevTools의 Sources 패널로 이동합니다.
오른쪽의 Call Stack 패널 옆에 'Async'의 새 체크박스가 있습니다. 체크박스를 선택 또는 선택 해제하여 비동기 디버깅을 사용 또는 사용 중지합니다. 사용 설정하면 사용 중지하지 않는 것이 좋습니다.

지연된 타이머 이벤트 및 XHR 응답 캡처
Gmail에서 다음과 같은 메시지를 본 적이 있을 것입니다.

요청을 전송하는 데 문제가 있는 경우 (서버에 문제가 있거나 클라이언트 측에 네트워크 연결 문제가 있는 경우) Gmail은 잠시 후 자동으로 메일을 다시 전송하려고 시도합니다.
비동기 호출 스택이 지연된 타이머 이벤트와 XHR 응답을 분석하는 데 어떻게 도움이 되는지 확인하기 위해 예시 Gmail을 사용하여 이 흐름을 다시 만들었습니다. 전체 JavaScript 코드는 위 링크에서 확인할 수 있지만 흐름은 다음과 같습니다.

이전 버전의 DevTools에서 호출 스택 패널만 보면 postOnFail()
내의 브레이크포인트로 postOnFail()
가 호출되는 위치에 관한 정보를 거의 얻을 수 없습니다. 그러나 비동기 스택을 사용 설정할 때의 차이점을 살펴보세요.

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

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


비동기식 감시 표현식
전체 호출 스택을 탐색하면 관찰 표현식도 업데이트되어 그때의 상태를 반영합니다.

이전 범위의 코드 평가
표현식을 단순히 감시하는 것 외에도 DevTools JavaScript 콘솔 패널에서 바로 이전 범위의 코드와 상호작용할 수 있습니다.
Dr. Who라고 가정해 보겠습니다. 타디스 안에 들어가기 전의 시계와 '지금'의 시계를 비교하는 데 도움이 필요합니다. DevTools 콘솔에서 여러 실행 지점의 값을 쉽게 평가, 저장, 계산할 수 있습니다.

DevTools에서 표현식을 조작하면 소스 코드로 다시 전환하고 수정한 후 브라우저를 새로고침하는 데 드는 시간을 절약할 수 있습니다.
체이닝된 약속 확인 해제
이전의 Gmail 모의 흐름은 비동기 호출 스택 기능을 사용 설정하지 않으면 파악하기 어렵다고 생각했다면 체이닝된 약속과 같이 더 복잡한 비동기 흐름에서는 얼마나 더 어려울지 상상해 보세요. 제이크 아치볼드의 JavaScript Promises 튜토리얼의 마지막 예시를 다시 살펴보겠습니다.
다음은 Jake의 async-best-example.html 예에서 호출 스택을 탐색하는 애니메이션입니다.

약속을 디버그하려고 할 때 호출 스택 패널에 정보가 거의 표시되지 않는 것을 볼 수 있습니다.

와우! 이러한 약속 콜백이 많습니다.
웹 애니메이션에 대한 유용한 정보 얻기
HTML5Rocks 보관 파일을 자세히 살펴보겠습니다. 폴 루이스의 requestAnimationFrame을 사용한 더 가볍고 강력하며 빠른 애니메이션을 기억하시나요?
requestAnimationFrame 데모를 열고 post.html의 update() 메서드 시작 부분 (874번 줄 주변)에 중단점을 추가합니다. 비동기 호출 스택을 사용하면 스크롤 이벤트 콜백을 시작하는 지점까지 거슬러 올라갈 수 있는 기능을 비롯하여 requestAnimationFrame에 대한 더 많은 통계를 얻을 수 있습니다.


MutationObserver를 사용할 때 DOM 업데이트 추적
MutationObserver
를 사용하면 DOM의 변경사항을 관찰할 수 있습니다. 이 간단한 예에서는 버튼을 클릭하면 새 DOM 노드가 <div class="rows"></div>
에 추가됩니다.
demo.html의 nodeAdded()
(31번 줄) 내에 중단점을 추가합니다. 비동기 호출 스택을 사용 설정하면 이제 addNode()
를 통해 호출 스택을 초기 클릭 이벤트로 다시 이동할 수 있습니다.


비동기 호출 스택에서 JavaScript를 디버깅하기 위한 도움말
함수 이름 지정
모든 콜백을 익명 함수로 할당하는 경향이 있다면 호출 스택을 더 쉽게 볼 수 있도록 이름을 지정하는 것이 좋습니다.
예를 들어 다음과 같은 익명 함수를 생각해 보겠습니다.
window.addEventListener('load', function() {
// do something
});
이름은 windowLoaded()
와 같이 지정합니다.
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
로드 이벤트가 실행되면 DevTools 스택 트레이스에 난해한 '(익명 함수)' 대신 함수 이름이 표시됩니다. 이렇게 하면 스택 트레이스에서 어떤 일이 일어나고 있는지 한눈에 쉽게 확인할 수 있습니다.


더 살펴보기
요약하면 다음은 DevTools에서 전체 호출 스택을 표시하는 모든 비동기 콜백입니다.
- 타이머:
setTimeout()
또는setInterval()
가 초기화된 위치로 돌아갑니다. - XHR:
xhr.send()
가 호출된 위치로 돌아갑니다. - 애니메이션 프레임:
requestAnimationFrame
가 호출된 위치로 돌아갑니다. - Promise: 프로미스가 해결된 위치로 돌아갑니다.
- Object.observe: 관찰자 콜백이 원래 바인딩된 위치로 돌아갑니다.
- MutationObservers: 변형 관찰자 이벤트가 실행된 위치로 돌아갑니다.
- window.postMessage(): 프로세스 내 메시지 호출을 살펴봅니다.
- DataTransferItem.getAsString()
- FileSystem API
- IndexedDB
- WebSQL
addEventListener()
를 통한 대상 DOM 이벤트: 이벤트가 실행된 위치로 돌아갑니다. 성능상의 이유로 일부 DOM 이벤트는 비동기 호출 스택 기능을 사용할 수 없습니다. 현재 사용 가능한 이벤트의 예로는 'scroll', 'hashchange', 'selectionchange'가 있습니다.addEventListener()
를 통한 멀티미디어 이벤트: 이벤트가 발생한 위치로 돌아갑니다. 사용 가능한 멀티미디어 이벤트에는 오디오 및 동영상 이벤트 (예: 'play', 'pause', 'ratechange'), WebRTC MediaStreamTrackList 이벤트 (예: 'addtrack', 'removetrack'), MediaSource 이벤트 (예: 'sourceopen')가 있습니다.
JavaScript 콜백의 전체 스택 트레이스를 볼 수 있으면 머리카락이 빠지지 않습니다. DevTools의 이 기능은 여러 비동기 이벤트가 서로 관련하여 발생하거나 비동기 콜백 내에서 포착되지 않은 예외가 발생하는 경우에 특히 유용합니다.
Chrome에서 사용해 보세요. 이 새로운 기능에 관한 의견이 있으면 Chrome DevTools 버그 추적기 또는 Chrome DevTools 그룹에 알려주세요.