IE11을 이용한 JavaScript 디버깅 06, F12 개발자 도구 디버거 창 Part 2

등록일시: 2019-02-12 08:00,  수정일시: 2019-02-12 08:00
조회수: 5,839

브라우저를 기반으로 한 웹 응용 프로그램 개발 생태계에서 클라이언트 측 개발의 난이도와 복잡성은 이미 서버 측에 못지 않습니다. 이런 어려움에 대한 대표적인 방안 중 하나로 대부분의 최신 브라우저들은 수 년 전부터 F12 개발자 도구를 제공하고 있습니다. F12 개발자 도구를 활용하면 직면한 문제점을 해결할 수 있는 실마리를 손쉽게 발견할 수 있는 경우가 많습니다. 그러나 초보자나 갑자기 웹 환경에서 작업하게 된 다른 언어의 전문가가 F12 개발자 도구에 부담 없이 접근할 수 있게 실질적인 도움을 제공하는 자료는 아직 많이 부족합니다.

이 시리즈에서는 가장 단순한 F12 개발자 도구를 갖고 있는 IE11을 사용해서 JavaScript를 디버깅하는 초보적인 수준의 방법을 소개합니다. F12 개발자 도구에서 어떤 기능의 도구가 제공되며 그 도구를 어떤 경우에 어떤 방식으로 활용할 수 있을지 간단히 경험해보고, 자신이 선호하는 다른 최신 브라우저에서 그에 대응하는 기능을 찾아서 사용할 수 있는 시작점이 되는 것이 본 시리즈의 목표입니다.

시리즈 목차: IE11을 이용한 JavaScript 디버깅

들어가기 전에

프로그램을 개발하다가 코드가 의도한 대로 동작하지 않거나 잘못된 결과를 반환할 때 개발자는 이런 생각을 떠올리기 마련입니다. "이 버튼을 클릭하면 화면에 메시지가 나타나야 하는데 왜 안나타나지?" 라던가 "뭔가 이상한데? 결과값이 왜 10이지? 분명히 15가 나와야 맞는데!" 같은 생각 말입니다. 가장 먼저 해야할 일은 아마도 오류 메시지를 살펴보고 상황을 재현해보는 일일 것입니다. 문제점이 동일하게 재현된다면 뭔가 버그가 존재하는 것이 확실하므로 디버거를 이용해서 원인을 찾아서 해결해야 합니다. 반대로 문제점이 재현되지 않거나 간헐적으로만 재현된다면 오히려 정시 퇴근이 위험해질 수 있는 진짜 심각한 상황일지도 모르는 일이기는 하지만 말입니다.

이 시점까지는 문제를 유발하는 잘못된 코드가 실제로 어디에 존재하는지 아무도 모릅니다. 단지 추측만 할 수 있을 뿐입니다. 사실 코드가 잘못된 것이 아닐 수도 있습니다. 데이터가 설계한 형태로 전달되지 않았을 수도 있고 실행 환경이 변경되었을 수도 있습니다. 그렇지만 그런 경우에도 여전히 코드 문제가 아니라는 점을 밝혀내야 합니다.

따라서 문제점이 존재하는 것으로 추측되는 코드 블럭의 진입점이나 그에 가장 근접한 위치에서부터 추적을 시작해서 (단계 1: 중단점 설정), 각 단계별로 개체나 변수가 의도한 값으로 변경되는지 추적해나가야 합니다 (단계 2: 코드 탐색 + 단계 3: 개체 및 변수 추적). 시작점을 바꿔가면서 조금씩 대상 범위를 좁혀나가는 과정을 반복하다보면 머지않아 대부분 문제점을 발견할 수 있습니다. 다시 말해서 메시지가 사라져버리거나 15로 설정되어야 할 변수값이 10으로 설정되는 잘못된 코드를 찾아낼 수 있는 것입니다. 그리고 사실상 이것이 디버깅의 근본적인 목적입니다.

문제점을 해결함에 있어서 단계 1과 단계 2, 그리고 단계 3은 모두 중요합니다. 절대적인 것은 아니지만 숙련된 개발자일수록 단계 1의 추적 시작점을 거의 직감적으로 파악해서 시행착오 횟수와 작업 시간을 크게 단축하는 경우도 많으며, 이는 오랜 경험과 학습을 바탕으로 얻어지는 능력입니다. 반면 단계 2와 단계 3은 작업 환경이 제공해주는 도구의 효율성과 숙련도에 따라서 결과가 크게 좌우됩니다. 어떤 분들에게는 자극적인 비교겠지만, 모든 문제를 로그만 보고 해결할 때의 효율성과 디버거를 활용해서 단계별로 재현해가면서 해결할 때의 효율성은 어마어마한 차이를 보여줍니다. 다른 사람의 말만 듣고서 머리 속으로 교통사고 현장을 재현해가면서 운전자의 과실을 가늠하는 것과, 직접 CCTV를 보면서 앞으로 감기나 뒤로 감기로 몇 번이고 반복해가면서, 또는 일시 중지한 상태로 상황을 자세히 살펴보는 것의 차이를 떠올려보시면 이해가 쉬을 것입니다.

지난 글에서는 상황에 따라 적절한 방식으로 단계 1을 수행하기 위해서 필요한, 중단점을 비롯한 다섯 가지 대표적인 기능들을 살펴봤습니다. 계속해서 본문에서는 디버거에서 중단한 코드를 다양한 방식으로 탐색하기 위한, 즉 단계 2를 수행하기 위한 방법들을 알아봅니다. 그리고 이어지는 다음 글에서는 조사식 탭과 콘솔 창 등을 이용하여 개체 및 변수의 상태를 살펴보거나 값을 변경해보면서 단계 3을 수행하는 방법을 살펴보도록 하겠습니다.

노트

그럼에도 불구하고 로그는 여전히 중요합니다. 모든 업무를 필요할 때마다 원하는 데이터로 재현할 수 있는 것은 아닙니다. 한 가지 예로 운영 환경에서 타사의 시스템과 데이터 인터페이스를 수행하는 서버 측 모듈을 떠올려보시기 바랍니다. 이런 모듈은 사실상 상황 재현이 거의 불가능하기 때문에 로그조차 남겨져있지 않다면 문제 발생 시 추측 말고는 대처할 수 있는 방안이 전무합니다. 다만 기본적으로 휘발성 로그만 제공되는 브라우저 기반의 개발 환경에서는 상대적으로 로그의 활용도가 많이 떨어집니다.

본문에서 말하고자 하는 바는 작업 중인 개발 환경에서 유용한 도구가 제공되는데 사용하지 않을 이유가 없다는 것입니다.

예제 코드 살펴보기

본문에서는 지난 글에서 살펴봤던, 단어에 적절한 조사를 덧붙여주는 예제 HTML 파일을 계속해서 주요 예제로 사용합니다. 이 예제에 대한 자세한 설명은 여기를 참고하시기 바랍니다. 그 밖에 추가적으로 사용되는 예제에 대해서는 별도로 설명하도록 하겠습니다. (새 창에서 보기)

코드 탐색하기

파일 열기 창

파일 열기 창을 사용하면 현재 페이지에서 사용되는 거의 모든 .js 파일을 명시적으로 선택해서 디버거 창에서 탭으로 열 수 있습니다. 여기서 '거의 모든'이라고 표현한 이유는 새 작업자에서 중단 버튼을 살펴보면서 언급했던 것처럼 페이지에서 사용되는 .js 파일을 디버거가 인식하지 못하는 경우도 일부 존재하기 때문입니다. 파일 열기 창을 열려면 디버거 창 좌상단의 툴 바 바로 아래에 위치한 폴더 아이콘을 클릭합니다.

노트

이 창은 조금만 스크롤하거나 마우스를 움직이면 멋대로 닫혀버리는 것으로 악명이 높습니다. Microsoft도 이런 불만을 알고 있었는지 Edge에서는 이 창이 항상 열려있는 상태로 변경되었습니다.

F12 개발자 도구 - 디버거 창 - 파일 열기 창

만약 페이지가 여러 프레임으로 구성되어 있다면 각 프레임의 파일들이 트리 뷰의 하위 노드로 분류되어 표시됩니다. 다음은 포털 다음의 메인 페이지를 파일 열기 창으로 살펴본 모습입니다. 아래에서 planmall, shopbox, toploginform.do 는 모두 별도의 프레임입니다. 참고로 toploginform.do 프레임의 jQuery 라이브러리 버전만 다른 것을 확인할 수 있습니다.

F12 개발자 도구 - 디버거 창 - 파일 열기 창 - 프레임 분류

파일 열기 창 상단의 입력란에 검색할 파일명을 입력하면 파일명에 검색어가 포함되어 있는 파일들만 필터링되어 나타납니다.

F12 개발자 도구 - 디버거 창 - 파일 열기 창 - 검색

파일 열기 창 이미지를 자세히 살펴보면 일부 파일 우측에 빨간색 빗금이 그어진 아이콘이 표시되어 있는 것을 볼 수 있습니다. 이 아이콘에 대해서는 나중에 내 코드만 디버그 기능을 설명할 때 다시 자세히 살펴보도록 하겠습니다.

프로시저 단위 실행 (F10)

디버거에서 중단된 코드를 탐색하는 가장 기본적인 방식은 프로시저 단위 실행을 수행하는 것입니다. 간단히 말해서 이 방식은 코드에 작성된 세미콜론(;)을 한 단위로 간주하여 코드를 실행한다고 생각하시면 이해하기가 쉽습니다. 구체적인 내용은 실제로 코드를 탐색해보면서 설명하도록 하겠습니다.

다음은 예제 HTML 페이지에서 fnTest() 함수의 console.clear() 구문에 중단점을 설정하고 버튼을 눌러서 디버거에서 중단한 상태를 보여줍니다.

F12 개발자 도구 - 디버거 창 - 프로시저 단위 실행

코드를 프로시저 단위로 실행하려면 툴 바에서 프로시저 단위 실행 버튼을 클릭하거나 단축키인 F10 키를 누르면 됩니다.

F12 개발자 도구 - 디버거 창 - 툴 바 - 프로시저 단위 실행 버튼

따라서 이 상태에서 F10 키를 누르면 현재 위치한 console.clear() 구문이 실행된 다음, 그 다음 구문인 console.log("Data length: %d", data.length) 구문으로 이동합니다. 아직 이동한 구문이 실행된 것은 아니라는 점에 유의하시기 바랍니다.

F12 개발자 도구 - 디버거 창 - 프로시저 단위 실행

다시 한 번 F10 키를 눌러보면 이번에는 현재 위치한 console.log("Data length: %d", data.length) 구문이 실행된 다음, for 문 전체가 아닌 var i = 0 구문으로 이동합니다.

F12 개발자 도구 - 디버거 창 - 프로시저 단위 실행

그리고 계속해서 F10 키를 누를 때마다 그 다음에는 i < data.length 구문으로 이동하고, 다시 그 다음에는 var result = attachPostposition(data[i]) 구문으로 이동하는 식입니다. 세미콜론(;)을 한 단위로 코드를 실행한다는 말이 어떤 의미인지 이제 이해하셨으리라고 생각합니다.

이 과정에서 초보자분들이 주목해야 할 점은 그저 코드를 이동하기만 하는 것이 아니라 실제로 한 단계씩 코드가 실행된다는 사실입니다. 따라서 수 십, 수 백 줄로 구성된 코드 전체를 한 번에 실행하는 대신 자신이 원하는 위치까지만 실행할 수 있습니다. 그런 다음 앞에서 언급했던 것처럼 마치 CCTV를 일시 중지시킨 것 같은 상태에서 변수값을 살펴보거나 필요하다면 실시간으로 편집하는 것이 가능합니다. 그 구체적인 방법은 이어지는 글에서 자세히 살펴봅니다.

또한 이런 방식으로 코드를 단계별로 탐색해보면서 JavaScript 엔진이 코드를 처리하는 실제 과정을 일부나마 엿볼 수도 있습니다. 가령 이제 막 프로그램 개발에 입문한 초보자가 가장 이해하기 힘들어하는 문법 중 하나인 while 문과 do ... while 문의 실행 순서를 직접 눈으로 확인해가면서 차이점을 비교해 볼 수 있습니다. 물론 비동기 함수나 재귀 함수 같은 경우는 말할 필요도 없습니다.

사실 멀리 갈 필요도 없이 본문의 예제에 사용된 for 문의 경우도 마찬가지입니다. 방금 살펴봤듯이 F10 키를 눌러서 i < data.length 구문을 실행한 뒤에 이동하는 구문은 i++ 구문이 아니라 var result = attachPostposition(data[i]) 구문입니다. i++ 구문은 for 문의 중괄호 내부에 작성된 코드가 모두 실행된 이후에 호출됩니다. 또한 최초 1회 루프가 실행된 이후부터는 반대로 i++ 구문이 실행된 이후에 i < data.length 구문이 평가됩니다. 숙련된 개발자에게는 그다지 새로울 바가 없는 일이지만 초보자에게는 이런 정보 하나하나가 모두 소중합니다.

툴 바에서 계속 버튼을 클릭하거나 F5 키를 눌러서 디버거를 빠져나간 다음 테스트에 사용된 중단점을 삭제합니다.

한 단계씩 코드 실행 (F11)

한 단계씩 코드 실행 방식은 방금 살펴본 프로시저 단위 실행 방식과 동작 패턴이 거의 비슷합니다. 단 한 가지 중요한 차이점은 현재 위치의 구문이 함수나 메서드일 경우, 세미콜론(;)을 기준으로 다음 코드로 이동하는 것이 아니라 해당 함수나 메서드 내부로 이동한다는 점뿐입니다. 그 외의 경우에는 프로시저 단위 실행 방식과 동일한 방식으로 동작합니다.

노트

개인적으로 이런 동작 패턴을 감안해볼 때, 저는 이 기능의 한글 번역이 매우 불만스럽습니다. 영어 원문은 Setp into로, 이 용어 쪽이 제공해주는 기능을 훨씬 잘 축약하여 전달해준다고 생각합니다.

중단된 코드를 한 단계씩 코드 실행 방식으로 실행하려면 툴 바에서 한 단계씩 코드 실행 버튼을 클릭하거나 단축키인 F11 키를 누르면 됩니다.

F12 개발자 도구 - 디버거 창 - 툴 바 - 한 단계씩 코드 실행 버튼

다음은 예제 HTML 페이지에서 for 문 내부의 var result = attachPostposition(data[i]) 구문에 중단점을 설정하고 버튼을 눌러서 디버거에서 중단한 상태를 보여줍니다. 이전 절에서 살펴봤던 것처럼 만약 이 상태에서 F10 키를 눌러서 프로시저 단위로 코드를 실행한다면 다음 줄인 console.log("'%s' -> '%s'", data[i], result) 문으로 이동할 것입니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

그러나 F10 키 대신 F11 키를 눌러서 한 단계씩 코드를 실행해보면, 다음과 같이 attachPostposition() 함수가 정의된 my_library.js 파일이 디버거 창에서 새 탭으로 열리고 이 함수 정의의 가장 첫 번째 줄로 이동합니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

그리고 이 상태에서 다시 한 번 F11 키를 누르면 또 다시 $.trim() 메서드가 정의된 jquery-3.3.1.js 파일이 디버거 창에서 새 탭으로 열리고 이 메서드 정의의 가장 첫 번째 줄로 이동합니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

그런데 이 $.trim() 메서드의 정의를 가만히 살펴보면 여기에서도 JavaScript의 String 개체가 제공해주는 replace()라는 메서드가 사용되고 있음을 알 수 있습니다. 그렇다면 다시 한 번 F11 키를 누르면 이번에도 이 메서드 내부의 정의로 이동하는 것일까요? 아쉽지만 그렇지는 않습니다. 이런 네이티브 함수 또는 메서드에서 한 단계씩 코드 실행을 수행하면 프로시저 단위 실행 방식과 동일하게 그냥 다음 구문으로 넘어갈 뿐입니다. 따라서 지금까지 살펴본 내용을 간단하게 정리해보면 다음 구문이 Function 개체인 경우에만 내부로 이동한다고 보시면 됩니다. 그 외의 경우에는 프로시저 단위 실행 방식과 동일하게 동작합니다.

특이한 사실 한 가지는 IE11에서 console 개체의 메서드를 대상으로 한 단계씩 코드 실행을 수행해보면 다음과 같은 엉똥한(?) 코드로 이동한다는 점입니다. 참고로 이 이미지는 예쁜 인쇄 기능을 적용한 상태로 캡처한 것입니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

본래 console은 네이티브 개체이기 때문에 그냥 다음 구문으로 이동하는 것이 올바른 동작입니다. 또한 실제로도 다른 최신 브라우저에서는 그렇게 동작합니다. 여기에 보이는 코드는 IE11이 자체적으로 console에 적용하는 일종의 래핑으로 보입니다.

F5 키를 길게 눌러서 디버거를 빠져나간 다음 테스트에 사용된 중단점을 삭제합니다.

프로시저 나가기 (Shift+F11)

프로시저 나가기 기능은 이전 절에서 살펴본 한 단계씩 코드 실행 방식과 정확하게 반대되는 기능을 제공해줍니다. 특정 함수나 메서드 내에서 프로시저 나가기를 수행하면 해당 함수나 메서드의 나머지 모든 코드를 즉시 실행한 다음, 다시 호출자로 빠져나갑니다. 이 동작은 프로시저 나가기를 사용할 때마다 호출 스택이 텅 빌 때까지 반복되며, 호출 스택이 완전히 비워지면 디버깅 세션이 종료되어 디버거를 빠져나갑니다.

프로시저를 빠져 나가려면 툴 바에서 프로시저 나가기 버튼을 클릭하거나 단축키인 Shift+F11 키를 누르면 됩니다.

F12 개발자 도구 - 디버거 창 - 툴 바 - 프로시저 나가기 버튼

다음은 my_library.js 파일의 attachPostposition() 함수 정의에 중단점을 설정하고 버튼을 눌러서 디버거에서 중단한 상태와 해당 시점의 호출 스택 탭을 보여줍니다. 호출 스택 탭에 관해서는 나중에 다시 자세하게 살펴봅니다. 일단 지금은 버튼 클릭으로 호출된 onclick 이벤트 핸들러와 이 이벤트 핸들러에서 호출된 fnTest() 함수, 그리고 다시 fnTest() 함수에서 호출된 attachPostposition() 함수가 나란히 호출된 순서의 역순으로 보여지고 있다라고만 이해하시면 됩니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

F12 개발자 도구 - 디버거 창 - 호출 스택 탭

이 상태에서 attachPostposition() 함수를 빠져 나가려면 Shift+F11 키를 누릅니다. 그러면 다음과 같이 이 함수의 나머지 코드가 모두 실행된 다음 (비록 이번 예제에서는 한 줄 뿐이지만), 호출자인 fnTest() 함수로 이동하게 됩니다. 그리고 이때 당연히 호출 스택도 함께 영향을 받습니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

F12 개발자 도구 - 디버거 창 - 호출 스택 탭

다시 계속 버튼을 클릭하거나 F5 키를 눌러서 코드를 계속 실행하면 for 루프가 한 번 더 반복되어 다시 attachPostposition() 함수 정의에 설정된 중단점에서 코드 실행이 중단됩니다. 이번 예제에서 attachPostposition() 함수는 for 문 내부에서 모두 네 차례 호출되므로 지금까지의 과정을 네 번 반복한 다음, 마지막으로 Shift+F11 키를 한 번 더 누르면 다음과 같이 최종 호출자인 onclick 이벤트 핸들러로 이동하게 됩니다.

F12 개발자 도구 - 디버거 창 - 한 단계씩 코드 실행

F12 개발자 도구 - 디버거 창 - 호출 스택 탭

이제 마지막으로 Shift+F11 키를 한 번 더 누르면 더 이상 상위 호출자가 존재하지 않으므로 디버거를 빠져나가게 됩니다. 다시 테스트에 사용된 중단점을 삭제합니다.

그 밖의 탐색 기능들

지금까지 살펴본 프로시저 단위 실행, 한 단계씩 코드 실행, 그리고 프로시저 나가기 기능을 적절히 함께 사용하면 대부분의 경우 원하는 코드 줄로 손쉽게 이동하고 실행할 수 있습니다. 그 밖에도 코드를 탐색하기 위한 유용한 기능이 몇 가지 더 제공되는데, 주로 디버거 창을 마우스 오른쪽 버튼으로 클릭하면 나타나는 컨텍스트 메뉴를 통해서 제공됩니다.

F12 개발자 도구 - 디버거 창 - 컨텍스트 메뉴

대부분의 코드 편집기에서 제공되는 것처럼 줄 이동 기능을 사용하면 프롬프트에 원하는 코드 줄 번호를 입력해서 해당 줄로 바로 이동할 수 있습니다. 다만 이 기능은 순수하게 해당 코드 줄을 찾아서 표시만 해줄뿐 코드가 실행되거나 하는 것은 아닙니다.

다음 문 표시 기능은 말 그대로 다음에 실행될 구문으로 이동하는 기능입니다. 뭔가 대단한 기능으로 오해하기 쉬운데, 여기에서 말하는 다음 문이란 딱히 별다른 구문을 지칭하는 것이 아니고 현재 중단되어 있는 코드 구문, 다시 말해서 좌측 여백에 화살표가 표시되어 있는 줄에 위치한 구문을 뜻합니다. 코드 실행을 중단하고 디버거 창을 이용해서 한동안 코드를 검토하다보면 어느새 열려 있는 여러 탭들 사이를 이동해가면서 수 천 줄 짜리 다른 파일의 코드를 보고 있는 경우도 허다합니다. 그런 경우에 현재 중단되어 있는 코드 줄로 신속하게 되돌아오기 위한 기능입니다.

정의로 이동 및 참조 찾기

정의로 이동 기능과 참조 찾기 기능은 유용하기는 하지만 제한적으로만 사용할 수 있는 다소 애메한 기능들입니다. 정의로 이동 기능을 사용하면 지정한 변수나 함수 등이 정의되어 있는 위치로 바로 이동할 수 있습니다. 예를 들어 다음과 같이 디버거 창에서 my_library.js 파일을 열고 attachPostposition() 함수 정의에서 hasFinalConsonant() 함수를 호출하는 구문을 마우스 오른쪽 버튼으로 클릭한 다음, 컨텍스트 메뉴에서 정의로 이동을 선택해봅니다.

F12 개발자 도구 - 디버거 창 - 정의로 이동

그러면 다음과 같이 hasFinalConsonant() 함수가 정의되어 있는 줄로 바로 이동합니다. 수 백, 수천 줄의 코드로 구성된 파일에서 특정 변수나 함수 등의 정의로 이동하고자 할 때 유용한 기능입니다.

F12 개발자 도구 - 디버거 창 - 정의로 이동

이와는 반대로 참조 찾기 기능을 사용하면 지정한 변수나 함수 등이 사용되고 있는 모든 위치의 목록을 조회할 수 있습니다. 이번에는 디버거 창에서 jquery-3.3.1.js 파일을 열고 다음과 같이 코드 시작 부분에 위치한 global 매개 변수를 마우스 오른쪽 버튼으로 클릭한 다음, 컨텍스트 메뉴에서 참조 찾기을 선택해봅니다.

F12 개발자 도구 - 디버거 창 - 참조 찾기

그러면 다음과 같이 global 매개 변수가 사용되는 코드 상의 모든 위치를 찾아서 목록으로 제공해줍니다. 물론 목록의 링크를 클릭하면 해당 코드 줄로 바로 이동합니다.

F12 개발자 도구 - 디버거 창 - 참조 찾기

여기까지만 설명을 들어보면 정의로 이동 기능과 참조 찾기 기능이 굉장히 유용하게만 느껴집니다. 그러나 처음에 언급했던 것처럼 이 기능들은 제한적인 상황에서만 사용이 가능합니다. 문제는 두 기능 모두 상황에 따라 지원되지 않거나 또는 정상적으로 원하는 변수나 함수 등이 검색되지 않는 경우가 많다는 것입니다. 가령 예제 HTML 파일에서 attachPostposition() 함수를 호출하는 구문을 마우스 오른쪽 버튼으로 클릭해보면 컨텍스트 메뉴에서 아예 기능 자체가 활성화되지 않습니다.

정의로 이동 및 참조 찾기 오동작

또한 디버거 창에서 my_library.js 파일을 열고 $.trim() 메서드를 호출하는 구문을 대상으로 정의로 이동 기능이나 참조 찾기 기능을 수행해보면 여러 가지 잘못된 형태를 보이며 역시 정상적으로 동작하지 않는 것을 확인할 수 있습니다. IE11의 경우 이미 기술지원이 중단된 상황임을 감안해볼 때 앞으로 개선될 여지도 없으므로 더욱 아쉬운 기능입니다.

커서까지 실행

다음은 커서까지 실행 기능과 다음 문 설정 기능을 살펴보기 위한 간단한 예제 HTML 파일입니다. 별다른 기능은 없으며 단지 루프를 500번 돌면서 콘솔 창에 메시지를 출력할 뿐입니다. (새 창에서 보기)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>디버거 - 긴 루프문 예제</title>
    <style>
      body {
        font-size: 12px;
        font-family: Tahoma;
      }
      input[type="button"] {
        display: block;
        width: 150px;
        height: 30px;
        margin: 5px;
      }
    </style>
  </head>
  <body>
    <input type="button" value="Test" onclick="fnTest();" />

    <script>
      function fnTest() {
        console.clear();
        console.log("Loop Start!");

        for (var index = 0; index < 500; index++) {
          console.log("Current Index is: %d", index);
        }

        console.log("Loop End!");
      }
    </script>
  </body>
</html>

간단히 말하자면 커서까지 실행은 CCTV의 앞으로 감기 같은 기능입니다. 예를 들어 다음과 같이 이 예제 HTML 페이지에서 fnTest() 함수의 console.clear() 구문에 중단점을 설정하고 버튼을 눌러서 디버거에서 코드를 중단했다고 가정해보겠습니다.

F12 개발자 도구 - 디버거 창 - 커서까지 실행

그런데 만약 for 문 내부의 로직은 이미 검증이 끝났기 때문에 더 확인할 필요가 없다면 어떻게 하는게 좋을까요? 다시 말해서 이 상태에서 바로 fnTest() 함수의 가장 마지막 줄인 console.log("Loop End!") 구문까지 이동하고 싶다면 말입니다.

가장 먼저 생각해볼 수 있는 간단한 방법은 마지막 코드 줄에 도달할 때까지 반복해서 F10 키를 누르는 것입니다. 그러나 그것도 한두 번이지 지금처럼 수백 회에 달하는 루프가 존재하는 등의 경우에는 큰 곤욕이 아닐 수 없습니다. 게다가 실수로 F10 키를 한 번이라도 더 누른다면 원하는 코드 줄을 지나쳐버릴 것입니다. 두 번째 방법은 마지막 코드 줄에 임시로 중단점을 설정하고 F5 키를 눌러서 코드를 다시 실행하는 것입니다. 그러면 for 문이 모두 수행된 다음, 마지막 코드 줄에서 실행이 중단됩니다. 그리고 나서 임시 중단점을 다시 삭제하면 됩니다.

바로 이 두 번째 방법과 비슷한 기능을 제공해주는 것이 커서까지 실행 기능입니다. 게다가 임시 중단점을 설정했다가 다시 삭제할 필요도 없습니다. 실행을 중단하고자 하는 코드 줄, 가령 이번 예제에서는 console.log("Loop End!") 구문을 마우스 오른쪽 버튼으로 클릭하고 컨텍스트 메뉴에서 커서까지 실행을 선택합니다.

F12 개발자 도구 - 디버거 창 - 커서까지 실행

그러면 해당 줄까지 코드가 실행된 다음, 다시 실행이 중단됩니다.

F12 개발자 도구 - 디버거 창 - 커서까지 실행

다음은 이 방법으로 코드를 실행하고 다시 F5 키를 눌러서 나머지 코드를 모두 실행한 다음 살펴본 콘솔 창에 출력된 메시지 결과입니다.

F12 개발자 도구 - 디버거 창 - 커서까지 실행 - 콘솔 창 결과

다음 문 설정

다음 문 설정은 방금 살펴본 커서까지 실행 기능과 비슷한 것 같지만 사실은 완전히 다른 용도로 사용되는 기능입니다. 이 기능은 이름 그대로 본래의 코드 흐름을 무시하고 다음에 실행될 코드 구문을 강제로 지정합니다. 방금 전과 동일하게 console.clear() 구문에 중단점을 설정하고 버튼을 눌러서 디버거에서 코드를 중단합니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정

그리고 console.log("Loop End!") 구문을 마우스 오른쪽 버튼으로 클릭한 다음, 이번에는 컨텍스트 메뉴에서 다음 문 설정을 선택합니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정

그러면 다음과 같이 console.log("Loop End!") 구문이 다음에 실행될 코드 구문으로 설정됩니다. 좌측 여백에 화살표가 위치해 있는 것에 주목하시기 바랍니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정

여기서 중요한 점은 이전 절의 커서까지 실행 기능과는 다르게 단지 다음에 실행될 구문만 변경됐을 뿐 코드는 아직 실행되지 않은 상태이며 지금도 console.clear() 구문에서 실행이 중단되어 있는 상태 그대로라는 것입니다. 이제 다시 F5 키를 눌러서 나머지 코드를 모두 실행한 다음, 콘솔 창에 출력된 메시지 결과를 확인해보면 다음과 같을 것입니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정 - 콘솔 창 결과

이미 짐작하셨겠지만 커서까지 실행의 결과와는 크게 다릅니다. 단지 Loop End!라는 메시지 하나만 달랑 출력되어 있을 뿐입니다.

그 이유는 매우 단순한데, console.clear() 구문에서 코드 실행이 중단된 상태에서 console.log("Loop End!") 구문을 다음 문으로 설정했기 때문에 코드를 계속 실행하면 그 사이에 존재하는 모든 코드를 건너뛰고 (심지어 console.clear() 구문조차도 실행하지 않고), 바로 console.log("Loop End!") 구문을 실행하기 때문입니다. 마치 중간에 존재하는 모든 코드를 임시로 주석 처리한 것처럼 동작하는 것입니다. 다음 문 설정은 자주 사용되지는 않지만 이처럼 적절히 사용할 경우 매우 유용한 기능입니다.

이와는 반대로 코드 흐름을 거슬러 올라가는 용도로 다음 문 설정 기능을 사용할 수도 있습니다. console.clear() 구문에 설정된 중단점을 삭제한 다음, 이번에는 console.log("Loop End!") 구문에 중단점을 설정하고 버튼을 눌러서 디버거에서 코드를 중단합니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정

그런 다음, 이번에는 오히려 코드의 더 위쪽에 위치한 for 문의 var index = 0 구문을 다음 문으로 설정합니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정

그리고 다시 F5 키를 눌러서 코드를 실행해보면 다시 for 문부터 코드가 실행되어 console.log("Loop End!") 구문에서 실행이 중단됩니다. 다음은 이 상태에서 콘솔 창의 출력 결과를 살펴본 모습입니다.

F12 개발자 도구 - 디버거 창 - 다음 문 설정 - 콘솔 창 결과

이미 대부분 예상하셨겠지만 for 문이 두 차례 실행되었으므로 메시지 출력도 역시 두 차례 수행되었음을 확인할 수 있습니다.

정리

본문에서는 디버거에서 중단한 코드를 다양한 방식으로 탐색하기 위한 방법들을 살펴봤습니다. 다음 글에서는 조사식 탭이나 콘솔 창 등을 이용해서 개체 및 변수의 상태를 살펴보거나 값을 변경하는 방법에 대해서 알아보도록 하겠습니다.