소개
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()
가 호출되는 위치에 관한 정보를 거의 얻을 수 없습니다. 그러나 비동기 스택을 사용 설정할 때의 차이점을 살펴보세요.
비동기 호출 스택을 사용 설정하면 전체 호출 스택을 확인하여 요청이 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 그룹에 알려주세요.