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

등록일시: 2019-02-19 08:00,  수정일시: 2019-02-19 08:00
조회수: 9,085

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

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

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

들어가기 전에

지금까지 두 차례에 걸쳐서 F12 개발자 도구의 디버거 창을 이용하여 코드의 특정 위치에서 실행을 중단한 다음, 다양한 방식으로 코드를 탐색하는 방법들을 살펴봤습니다. 본질적으로 이 모든 과정은 응용 프로그램이 특정 시점에 어떤 상태에 놓여 있는지를 살펴보기 위한 준비 작업입니다. 다시 말해서 개체나 변수가 적절한 시점에 기대하는 값으로 설정되는지 면밀하게 검토하기 위한 사전 단계인 것입니다.

본문에서는 조사식 탭이나 콘솔 창 등을 이용해서 중단된 코드의 문맥을 감안하여 개체 및 변수의 상태를 살펴보고 필요한 경우 그 값을 변경해서 테스트해보는 방법을 알아봅니다.

예제 코드 살펴보기

지금까지 계속 예제로 사용했던, 단어에 적절한 조사를 덧붙여주는 예제 HTML 파일을 본문에서도 주요 예제로 사용합니다. 이 예제에 대한 자세한 설명은 여기를 참고하시기 바랍니다. (새 창에서 보기)

개체 및 변수 살펴보기 또는 값 변경하기

디버거 창

특정 시점의 개체나 변수의 상태를 확인할 수 있는 가장 간단한 방법은, 코드 실행이 중단된 디버거 창에서 살펴보고자 하는 대상 위에 마우스를 올려보는 것입니다. 그러면 다음과 같은 팝업 창이 나타납니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 살펴보기

지금처럼 대상이 배열이거나 개체인 경우, 콘솔 창에서 살펴봤던 것처럼 좌측의 삼각형 불릿을 클릭해서 하위 속성들의 정보를 확인할 수 있습니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 살펴보기 - 하위 정보 확인

또한 다음과 같이 마우스로 값을 더블 클릭해서 적절한 다른 값으로 변경할 수도 있습니다. 다만 이 팝업 창에서는 개체에 속성을, 또는 배열에 항목을 추가하거나 제거하는 등과 같은 작업은 불가능합니다. 대신 그런 유형의 작업은 잠시 후에 살펴볼 콘솔 창을 이용해서 처리할 수 있습니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 값 변경하기

이와 같이 배열의 항목 값을 임의로 변경한 다음, F5 키를 눌러서 예제를 계속 실행해보면 다음과 같이 출력 결과에 변경된 값이 그대로 반영되는 것을 확인할 수 있습니다.

F12 개발자 도구 - 콘솔 창 - 실행 결과

다만 이렇게 값을 변경할 때는 주의해서 작업해야 합니다. 가령 이번 예제에서 length는 거의 대부분의 경우 명시적으로 값을 설정하는 속성이 아닌 배열에 존재하는 항목의 개수에 의해 자동으로 값이 설정되는 속성입니다. 이 값을 억지로 5로 변경한 다음, 예제를 실행해보면 예상할 수 있는 것처럼 의도하지 않은 결과를 얻게 됩니다. 이런 식이라면 문제점을 해결하기 위한 디버깅 작업에 되려 혼란만 더 할 뿐입니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 값 변경하기 - 적절하지 않은 값 설정

F12 개발자 도구 - 콘솔 창 - 실행 결과

팝업 창을 통해서 변수나 배열만 확인할 수 있는 것은 아닙니다. 개체뿐만 아니라 함수나 메서드도 살펴볼 수 있습니다. 다음은 각각 jQuery 개체를 뜻하는 변수 $와 함수 hasFinalConsonant()를 팝업 창으로 살펴본 모습입니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 살펴보기 - $ 개체

F12 개발자 도구 - 디버거 창 - 개체 및 변수 살펴보기 - hasFinalConsonant() 함수

팝업 창 하단의 조사식 추가 링크 버튼을 클릭하면 현재 살펴보고 있는 대상을 조사식 탭에 추가하여 지속적으로 추적할 수도 있습니다. 조사식 탭에 관해서는 잠시 후에 자세히 알아봅니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 값 변경하기 - 조사식 추가 링크 버튼

F12 개발자 도구 - 디버거 창 - 조사식 탭

지금까지 살펴본 것처럼 간단한 마우스 조작만으로도 손쉽게 개체나 변수의 상태를 확인하거나 값을 변경할 수 있습니다. 그러나 이 방식은 잠시 간단하게 상태를 검토하기 위한 용도로는 무난하지만 여유를 갖고 세밀한 분석을 진행하기에는 적합하지 않습니다. 조금만 마우스를 잘못 건드려도 팝업 창이 의도와는 상관 없이 닫혀버리기 때문입니다. 또한 앞에서 언급했던 것처럼 단순값만 변경할 수 있을뿐 다양한 상황에 따른 개발자의 요구를 충족하지는 못합니다. 그래서 저는 개인적으로 콘솔 창을 사용하는 방식을 선호하는 편입니다.

다음 진행을 위해 F5 키를 길게 눌러서 디버거를 빠져나갑니다.

콘솔 창

다시 디버거를 이용해서 console.clear() 구문에서 코드 실행을 중단합니다. 그리고 디버거 창 우상단의 콘솔 표시 버튼을 클릭해서 디버거 창 하단에 콘솔 창을 함께 엽니다. 참고로 설명 상의 편의를 위해 명령줄을 여러 줄 모드로 변경하도록 하겠습니다.

F12 개발자 도구 - 콘솔 창 - 디버거 창과 함께 보기

이 상태에서 data 배열의 항목들을 보고 싶다면 그냥 명령줄에 다음과 같이 변수명을 입력하고 화살표 모양의 스크립트 실행 버튼을 클릭하기만 하면 됩니다.

F12 개발자 도구 - 콘솔 창 - data 배열 항목 보기

이전 절의 팝업 창이 보여주던 정보와 거의 유사한 정보가 출력되는 것을 확인할 수 있습니다. 그러나 아쉽지만 항목의 값을 변경하고자 할 경우, 팝업 창에서처럼 마우스로 값을 더블 클릭해서 간단히 변경할 수는 없습니다. 대신 다음과 같이 실제로 원하는 작업을 수행하는 코드를 실행해야 합니다.

F12 개발자 도구 - 콘솔 창 - data 배열 항목값 변경

이런 방식으로 코드를 실행한 결과는 다시 명령줄에 변수명을 입력하거나 이전 절에서처럼 팝업 창을 이용해서, 또는 잠시 후에 살펴볼 조사식 탭을 통해서 확인할 수 있습니다.

F12 개발자 도구 - 콘솔 창 - data 배열 항목값 변경 결과

이 상태에서 F5 키를 계속 눌러서 예제를 끝까지 실행해보면 이번에도 다음과 같이 출력 결과에 변경된 값이 그대로 반영되는 것을 확인할 수 있습니다.

F12 개발자 도구 - 콘솔 창 - 실행 결과

지금까지 살펴본 것처럼 단순히 특정 변수의 값을 변경하는 등의 작업에는 콘솔 창을 이용한 방식이 아주 조금이긴 하지만 팝업 창 방식보다 더 번거롭습니다. 그러나 콘솔 창은 사용하기에 따라 엄청나게 강력해서 어떤 면에서는 거의 제한이 없다고까지 말할 수 있습니다. 가령 이미 언급했던 것처럼 배열에 항목을 추가하거나 제거하는 것 같은 초보적인 작업은 물론이고, 코드로 작성할 수만 있다면 거의 모든 작업을 수행할 수 있습니다.

다음은 다시 console.clear() 구문에서 코드 실행을 중단한 다음, 일괄적으로 data 배열에 새로운 항목을 추가하고 재조회한 결과를 보여줍니다. 메시지를 살펴보기 편하도록 콘솔 창의 기존 메시지를 다시 초기화시킨 후 작업을 수행했습니다.

F12 개발자 도구 - 콘솔 창 - data 배열 항목 추가

대부분 여러분이 예상하신 것과 같은 결과일 것입니다. 페이지를 새로 고치지는 않았기 때문에 전역 변수인 data 배열 중 방금 전에 감귤로 변경했던 항목도 그대로 유지되고 있는 것을 볼 수 있습니다.

그러나 고작 이 정도만으로는 콘솔 창이 얼마나 강력한지, 또한 그렇기 때문에 웹 브라우저 기반의 응용 프로그램에서 JavaScript 코드를 작성할 때 얼마나 주의를 기울여야 하는지 초보자가 체감하기는 어렵습니다. 그래서 이번에는 콘솔 창을 이용해서 어떤 작업까지 시도할 수 있는지를 보여주는 극단적인 사례를 살펴보도록 하겠습니다.

본문의 예제 HTML 파일에서 가장 핵심적인 작업인, 단어에 적절한 조사를 덧붙여주는 작업은 attachPostposition() 함수에 의해서 수행됩니다. 만약 악의적인 사용자가 이 함수를 완벽하게 제어할 수 있다면 어떨까요? 여기에서 말하는 악의적인 사용자란 굳이 거창한 해커일 필요도 없습니다. 여러분이 운영하는 시스템의 정상적인 사용자, 단 시스템 정책에 따라 일부 권한이 제한된 평범한 사용자를 뜻합니다. 과연 이런 사용자가 attachPostposition() 함수를 완벽하게 제어하는 것이 가능한 일 일까요?

콘솔 창의 명령줄에 다음과 같이 입력하고 스크립트 실행 버튼을 클릭해봅니다.

function attachPostposition() { return "Invalid result !!!!!!"; }

F12 개발자 도구 - 콘솔 창 - attachPostposition() 함수 재정의

이 코드가 의도하는 바는 명백합니다. attachPostposition() 함수의 정의를 자신이 원하는 방식으로 재정의하는 것입니다. 과연 이 코드가 제대로 반영되었을지, F5 키를 계속 눌러서 예제를 끝까지 실행해보면 다음과 같은 결과를 확인할 수 있습니다.

F12 개발자 도구 - 콘솔 창 - attachPostposition() 함수 재정의 결과

직접 보실 수 있는 것처럼 새로운 함수 정의가 완벽하게 반영되었습니다. 말하자면 일종의 초보적인 XSS (Cross-Site Scripting) 공격이 어처구니 없을 정도로 간단하게 성공한 셈입니다.

이와 같은 작업을 관리자나 개발자가 직접 수행한다면 디버깅 작업의 일환일 뿐이겠지만, 그 외에는 모두 악의적인 의도를 가진 것으로 간주해도 무방할 것입니다. 또한 이번 예제에서는 단순히 대상 함수가 특정 문자열을 반환하도록만 변경했을 뿐입니다. 그러나 가령 자금 관련 시스템의 유효성 검사 로직을 대상으로 이런 비슷한 공격이 이루어진다면 어떤 결과가 발생할까요? 클라이언트 측 유효성 검사와 서버 측 유효성 검사를 동시에 병행하지 않는 시스템이라면 영향이 어디까지 파급될지 예상조차 불가능합니다. 더군다나 일반 사용자 중에서도 IT 기술에 능숙한 분들이 많은 최근에는 더욱 세심한 주의를 기울여야 합니다.

노트

이런 공격을 조금이라도 더 방어하기 위해서 클로저를 이용한 모듈 패턴 같은 기법도 많이 사용되지만 완벽한 방어는 불가능합니다. 웹 브라우저 환경에서는 언제든지 코드 전체를 보고, 또 덮어씌울 수 있기 때문입니다. 또는 간단한 웹 브라우저 설정만으로도 JavaScript 자체를 비활성시킬 수 있습니다.

ASP.NET WebForms, ASP.NET MVC, 또는 ASP.NET Core 개발자분들은 프레임워크에서 기본으로 제공하는 유효성 검사 컨트롤이나 모델 유효성 검사 기술을 사용하여 동일한 규칙이 적용된 클라이언트 측 유효성 검사와 서버 측 유효성 검사가 동시에 병행되도록 손쉽게 시스템을 구현할 수 있습니다. 각자 자신이 사용 중인 프레임워크에서 이에 대응하는 기능을 찾아보시기 바랍니다.

유효성 검사의 경우, 근본적으로 클라이언트 측 유효성 검사는 사용자에게 편의를 제공하고 불필요한 서버 요청을 줄이기 위한 수단으로만 간주하는 것이 좋습니다.

이런 결과를 감안해 볼 때, F12 개발자 도구가 오히려 문제의 원인은 아닌지 의구심을 갖는 초보자분도 계실 것입니다. 물론 F12 개발자 도구로 인해서 누구나 이런 작업을 간단하게 수행할 수 있게 되었다는 사실은 부정할 수 없습니다. 웹 브라우저가 능동적으로 개발자와 비개발자, 또는 정상적인 개발자와 악의적인 사용자를 구분해서 기능을 제공해주는 것은 아니니까 말입니다. 그러나 F12 개발자 도구 외에도 이와 비슷한 작업을 수행할 수 있는 방법은 여전히 많습니다.

구분하기 쉽도록 페이지를 새로 고친 다음, IE11의 주소 표시줄에 다음 코드를 한 줄로 입력하고 엔터 키를 눌러보시기 바랍니다. 반드시 javascript:... 부터 전체 줄을 입력해야 합니다.

javascript: function attachPostposition() { return "It's from Address Bar !!!!!!"; }

주소 표시줄 - attachPostposition() 함수 재정의

그러면 일단은 아무런 일도 일어나지 않는 것처럼 보입니다. 이제 버튼을 클릭해서 예제 HTML 파일의 코드를 다시 실행해보시기 바랍니다. 만약 중단점에서 코드 실행이 중단되면 F5 키를 계속 눌러서 예제를 끝까지 실행합니다. 다음은 그 결과입니다.

F12 개발자 도구 - 콘솔 창 - attachPostposition() 함수 재정의 결과

단지 주소 표시줄에 한 줄의 코드를 입력했을 뿐인데, 이번에도 동일하게 실제 코드를 완벽하게 재정의했습니다. 보다 정확한 확인을 위해서 명령줄에 이 함수명을 입력하고 실행해봅니다. 함수명을 입력할 때 괄호(())는 입력하지 말아야 합니다.

attachPostposition;

F12 개발자 도구 - 콘솔 창 - attachPostposition() 함수 정의

그러면 재정의 된 attachPostposition() 함수의 구현을 콘솔 창을 통해서 다시 한 번 확인할 수 있습니다. 이전 절에서 팝업 창으로 살펴봤던 함수의 정보와 거의 비슷한 정보가 제공되는 것을 확인할 수 있습니다.

조사식 탭을 살펴보기 전에 콘솔 숨기기 버튼을 클릭해서 다시 콘솔 창을 숨깁니다.

조사식 탭

조사식 탭은 앞에서 살펴본 디버거 창의 팝업 창과 비슷한 형태의 정보를 제공해줍니다. 다만 팝업 창이 특정 개체 또는 변수 하나에만 집중한다면, 조사식 탭은 현재 실행 문맥 또는 범위에서 접근 가능한 모든 개체 및 변수들에 대한 정보를 (this, arguments, 전역 변수, 지역 변수 등) 일목요연하게 정리해서 보여준다는 차이점이 존재합니다.

기본적으로 JavaScript의 실행 문맥은 초보자가 정확하게 이해하기는 매우 어렵고, 다른 환경의 전문가가 보기에는 전통적인 문맥의 개념과 뭔가 미묘하게 다른, 조금은 난해한 개념입니다. 본문의 범위를 벗어나는 주제이기 때문에 여기에서는 실행 문맥에 관해서 자세하게 설명하지는 않습니다. 일단은 간단하게 디버거에서 코드 실행을 중단한 상태에서 해당 시점에 접근 가능한 개체 및 변수들의 모음을 조사식 탭을 통해서 살펴본다라고만 이해하시면 무리가 없을 것입니다.

다음 이미지는 console.clear() 구문에서 코드 실행을 중단하고 조사식 탭을 살펴본 모습입니다. 아무런 조사식도 추가되지 않은 기본 상태에서 조사식 탭의 유일한 루트 노드는 지역 변수를 의미하는 [Locals] 노드이며, 전역 변수들의 루트 노드인 [Globals] 노드조차도 이 [Locals] 노드 하위에 위치합니다. 그러나 이런 배치가 [Locals] 노드와 [Globals] 노드 간의 관계를 반영하는 것은 아니며, 단지 이런 구조로 표현될 뿐입니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭

결론부터 말해서 이렇게 조사식 탭의 [Locals] 노드 하위에서 확인할 수 있는 항목들이 현재 중단된 코드의 실행 문맥에서 접근할 수 있는 모든 개체 및 변수들이라고 생각하시면 됩니다. 즉 이 목록에 존재하지 않는 개체 및 변수들은 해당 시점에 사용할 수 없습니다. 이 이미지에서 확인할 수 있는 [Locals] 노드 하위의 각 항목들은 다음과 같습니다.

i, result

이 변수들은 중단된 현재 코드, 즉 fnTest() 함수 내부에 정의된 지역 변수들입니다. 그러나 이제 막 함수에 진입했을 뿐, 변수에 값을 할당하는 코드는 수행되지 않았기 때문에 그 값이 undefined 입니다.

그런데 재미있는 사실은 엄격하게 말해서 값 할당은 커녕 아직까지 변수를 선언하는 코드 자제도 수행되지 않았다는 점입니다. 그럼에도 불구하고 조사식 탭은 이미 이 변수들을 정확하게 인식하고 있습니다. 그리고 이 말은 지금 이 상태에서도 코드를 이용해서 이 변수들을 사용할 수 있다는 뜻과 동일합니다. 다른 언어에서는 찾아보기 힘든 이런 특징을 호이스팅(Hoisting)이라고 합니다. 본문에서는 호이스팅 자체에 관해서는 자세히 살펴보지 않습니다. 인터넷 상에 호이스팅을 설명하는 자료들은 이미 많습니다. 다만 이렇게 디버거 창을 활용하면 그냥 글로된 설명이 아닌 실제로 호이스팅 수행되는 모습을 눈으로 직접 확인할 수 있다는 점이 중요합니다.

눈여겨 볼만한 부분이 한 가지 더 있습니다. 다른 언어의 전문가분들은 이미 짐작하셨을지 모르겠습니다만, 대부분 다른 언어에는 블록 범위라는 개념이 존재합니다. 가령 다른 많은 언어에서는 for 문 내부의 코드가 별도의 블럭 범위를 갖습니다. 따라서 이 변수들은 for 문 외부에서는 접근할 수 없습니다. 만약 동일한 개념이 JavaScript에도 존재한다면 애시당초 호이스팅은 성립하지 않을 것입니다. 그런데 방금 우리는 호이스팅을 통해서 for 문 외부에서도 이 변수들에 접근이 가능함을 직접 목격했습니다. 결론부터 얘기하면 JavaScript에는 블럭 범위라는 개념이 존재하지 않습니다. 대신 함수, 전역, 그리고 eval의 세 가지 범위 유형만 존재합니다. 따라서 어떤 변수를 for 문 내부에서 정의해도, 또는 외부에서 정의해도 그 결과는 동일합니다. Java 같은 언어에서 두 가지 경우를 엄격하게 구분하도록 지시하는 일반적인 코딩 가이드는 JavaScript에서는 대부분 무의미합니다.

this, [Globals] 노드

클래스를 지원하는 많은 언어에서 this는 클래스의 현재 인스턴스를 가리키는 키워드이며, 대부분 개발자가 알아야 할 개념은 이게 전부입니다. JavaScript의 this 역시 기본적으로는 현재 실행 문맥을 가리키는 비슷한 개념입니다. 그러나 JavaScript에서 this는 수 많은 초보자들을 혼란에 빠트리는 주범이기도 합니다. 문제는 이 this 값이 호출 방법에 따라서 변한다는 점입니다. 역시 본문에서는 this 자체에 관해서 자세하게 살펴보지는 않습니다. 이는 주제를 벗어날 뿐만 아니라 설명을 위해 필요한 분량이 상당하기 때문입니다. 다행히 JavaScript의 this를 자세하게 소개하는 자료들은 인터넷 상에 이미 많이 존재합니다. 본문에서는 조사식 탭을 이용해서 실시간으로 변하는 this의 값을 확인하는 방법만 중점적으로 살펴봅니다.

지금처럼 일반적인 방식으로 함수가 실행될 때 this는 전역 개체, 즉 웹 브라우저 환경에서는 window 개체를 가리킵니다. (설명 상의 편의를 위해 본문에서는 strict 모드 여부는 감안하지 않습니다.) 다음은 코드 실행이 중단된 fnTest() 함수의 문맥에서 this 항목을 자세히 살펴본 모습입니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - this

그런데 [Globals] 노드 역시 항상 전역 개체인 window 개체를 가리킵니다. 따라서 지금과 같은 경우, this[Globals] 노드는 본질적으로 같은 window 개체를 가리킵니다. 얼핏 보기에는 내용이 다른 것 같지만 표현 방식만 다를 뿐입니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - [Globals] 노드

또한 이 이미지를 자세히 살펴보면 예제 HTML 파일과 .js 파일에서 정의한 data 배열 및 fnTest() 함수, attachPostposition() 함수, 그리고 hasFinalConsonant() 함수가 모두 window 개체의 멤버로 정의되어 있는 것을 확인할 수 있습니다. 이런 점을 감안해본다면 기본적인 함수 실행에서 thiswindow 개체인 것은 어찌보면 납득이 가는 일입니다. 현재 인스턴스가 window 개체인 셈이니 말입니다. 따라서 이번 예제에서 다음의 식은 모두 true 입니다.

data === this.data;
data === window.data;
data === window["data"];
window.data === window["data"];

fnTest === this.fnTest;
fnTest === window.fnTest;
fnTest === window["fnTest"];
window.fnTest === window["fnTest"];

다시 계속해서 이전 글에서 살펴본 코드 탐색 방법을 이용하여 my_library.js 파일에 정의된 attachPostposition() 함수 내부까지 탐색해봅니다. 다음 이미지는 그런 다음 조사식 탭을 살펴본 모습입니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭

이번에도 일반적인 함수 방식으로 실행되었기 때문에 this[Globals] 노드는 모두 window 개체를 가리키고 있습니다. 변수 iresult 대신 매개 변수로 선언된 word 항목이 지역 변수로 나타났을 뿐입니다.

다시 한 번 F11 키를 눌러서 jquery-3.3.1.js 파일에 정의된 $.trim() 메서드 내부까지 탐색합니다. 이 메서드는 지금까지와는 달리 그 이름처럼 함수가 아닌 메서드 방식으로 실행됩니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭

그러면 이번에는 [Globals] 노드는 여전히 window 개체를 가리키지만, this는 메서드 자신이 정의되어 있는 개체, 즉 여기에서는 jQuery 개체를 가리키게 됩니다. 따라서 다음의 식은 모두 true 입니다.

this === $;
this === jQuery;

F12 개발자 도구 - 디버거 창 - 조사식 탭 - this

비록 본문에서는 크게 함수 실행과 메서드 실행의 두 가지 경우만 살펴봤지만 this의 값을 결정하는 요인은 너무나 다양해서 (예를 들어, 단순 함수 실행인지 메서드 실행인지, strict 모드를 사용 중인지, call() 메서드 또는 apply() 메서드를 통한 호출인지, bind() 메서드가 사용됐는지, 화살표 함수인지, 생성자 실행인지 등) 초보자가 코드만 보면서 그 값을 추측하기는 매우 어렵습니다. 그러나 지금까지 살펴본 것처럼 조사식 탭을 활용하면 현재 this가 어떤 값으로 설정되어 있는지 직접 눈으로 살펴보면서 추적할 수 있습니다.

[Scope] 노드

다시 한 번 $.trim() 메서드 실행 상태의 조사식 탭을 살펴보면 함수 실행 상태에서는 보지 못했던 [Scope]라는 노드가 존재하는 것을 볼 수 있습니다. [Scope] 노드는 현재 개체의 지역 변수들의 루트 노드입니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - [Scope] 노드

arguments

배열 형태의 arguments는 함수나 메서드에 전달된 매개 변수들의 정보를 제공해주는 독특한 개체입니다. 이 개체를 활용하면 params 키워드를 이용한 C#의 가변길이 매개 변수varargs(...)를 이용한 Java의 가변 인자 같은 기능을 구현할 수 있습니다. 오히려 JavaScript에서는 별도의 작업 없이도 arguments 개체만 사용하면 모든 함수 및 메서드가 기본적으로 가변길이 매개 변수를 사용하고 있는 셈이므로 이 부분에만 국한지어 생각해보면 사실상 JavaScript 쪽이 더 강력하다고도 말할 수 있을 것입니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - arguments

이를테면 현재 $.trim() 메서드에는 text라는 매개 변수를 통해서 '사과'라는 단일 값이 인자로 전달된 상태입니다. 조사식 탭은 이 정보를 두 가지 방식으로 표현합니다. 먼저 방금 언급했던 것처럼 text 변수의 값이 '사과'임을 보여줍니다. 그리고 두 번째로 arguments 개체의 첫 번째 요소, 즉 arguments[0] 값이 '사과'임을 보여줍니다. 실제로도 이 두 항목은 같은 값을 바라보고 있습니다. 그 증거로 조사식 탭에서 둘 중 한 값을 마우스로 더블 클릭하여 변경하면 나머지 한 쪽도 값이 따라서 변경됩니다.

보기 편하도록 $.trim() 메서드의 시그니처를 정리해보면 다음과 동일하므로 arguments라는 개체가 존재한다는 사실만 알고 나면 아무런 문제도 없는 당연한 동작인 것처럼 보입니다. 그다지 신기해보이지도 않습니다.

function trim(text)

그런데 만약 다음과 같이 $.trim() 메서드를 호출한다면 어떻게 될까요? 분명히 시그니처에 매개 변수는 text 하나 밖에 정의되어 있지 않습니다. 따라서 다른 언어에서라면 당연히 오류가 발생할 것입니다. 그러나 JavaScript에서는 사정이 조금 다릅니다. 오히려 첫 번째 매개 변수가 문자열이기만 하면 일단은 정상적으로 동작할 것입니다.

var result = $.trim("사과", { value: "수박" }, ["배"], 10);
console.log(result);

예제 HTML 파일 및 .js 파일들에 설정되어 있는 기존의 모든 중단점을 제거한 다음, jquery-3.3.1.js 파일에 정의된 $.trim() 메서드에만 중단점을 하나 설정합니다. 그리고 콘솔 창에서 위의 코드를 실행해봅니다. 실행이 중단된 코드의 조사식 탭을 살펴보면 다음과 같습니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - arguments

매개 변수로 전달된 값들이 지정된 순서에 따라 각각 arguments 개체의 항목으로 담겨 있는 것을 확인할 수 있습니다. 따라서 명시적으로 이름이 지정된 매개 변수가 없더라도 전달된 인자들의 개수와 형식에 따라 적절한 동적 처리를 구현할 수 있습니다. 이처럼 arguments 개체를 활용하면 가변길이 매개 변수 기능을 손쉽게 구현할 수 있습니다. 그러나 다른 한편으로 이는 함수나 메서드의 명확한 사용법을 알 수 없게 만드는 혼란의 원인이기도 합니다. 그런 경우에 조사식 탭을 활용해서 함수나 메서드의 매개 변수에 기대한 인자 값이 정상적으로 전달되고 있는지를 확인할 수 있습니다.

조사식

지금까지 살펴본 [Locals] 노드 하위의 각 항목들은 디버거 창이 자동으로 선별하여 제공해주는 항목들입니다. 이 항목들은 코드가 진행됨에 따라 나타나기도 하고 사라지기도 합니다. 그러나 디버깅 작업을 진행하다 보면 특별히 관심을 갖고 추적하고 싶은 개체나 변수, 또는 식 등이 생기기 마련입니다. 그리고 때로는 이를 찾기 위해서 트리 뷰의 하위 노드 깊숙한 곳까지 찾아 들어가야 합니다. 이런 개체나 변수, 또는 식 등을 조사식으로 추가해놓으면 코드 진행에 영향을 받지 않고 손쉽게 추적할 수 있습니다.

조사식은 몇 가지 방법으로 손쉽게 추가할 수 있습니다. 먼저 디버거 창을 살펴보면서 언급했던 것처럼 개체나 변수 위에 마우스를 올리면 나타나는 팝업 창 하단의 조사식 추가 링크 버튼을 클릭해서 현재 살펴보고 있는 대상을 조사식으로 추가할 수 있습니다.

F12 개발자 도구 - 디버거 창 - 개체 및 변수 값 변경하기 - 조사식 추가 링크 버튼

또는 소스에서 개체나 변수 등을 마우스 오른쪽 버튼으로 클릭한 다음, 컨텍스트 메뉴에서 조사식 추가 버튼을 클릭해도 됩니다.

F12 개발자 도구 - 디버거 창 - 조사식 추가 버튼

콘솔 창에 입력한 변수나 식을 마우스 오른쪽 버튼으로 클릭한 다음, 컨텍스트 메뉴에서 조사식에 추가를 선택할 수도 있습니다.

F12 개발자 도구 - 콘솔 창 - 조사식에 추가

조사식 탭에서 추적하고 싶은 개체나 변수를 마우스 오른쪽 버튼으로 클릭한 다음, 컨텍스트 메뉴에서 조사식 추가를 선택할 수도 있고, 또는 조사식 탭 우상단의 조사식 추가 버튼이나 목록 최하단에 위치한 조사식 추가 링크 버튼을 클릭하면 직접 조사식을 입력할 수도 있습니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - 조사식 추가

다음 이미지는 이런 방식으로 추가한 다양한 조사식들을 보여줍니다. data 같은 단순 변수뿐만 아니라 data === window.data 같은 평가식은 물론, attachPostposition(data[i]) 같은 함수 실행 결과까지 실시간으로 추적이 가능합니다.

F12 개발자 도구 - 디버거 창 - 조사식 탭 - 조사식 사례

만약 이미 추가한 조사식을 수정하고 싶다면 마우스로 더블 클릭하여 편집할 수도 있습니다.

정리

본문에서는 조사식 탭이나 콘솔 창 등을 이용해서 중단된 코드의 개체나 변수 등을 살펴보는 방법을 알아봤습니다. 또한 필요에 따라 해당 값이나 함수 정의 자체를 변경하는 방법과 그 위험성에 관해서도 간단하게 살펴봤습니다.

계속해서 다음 글에서는 호출 스택 탭과 중단점 탭을 사용하는 방법을 살펴보도록 하겠습니다.