IE11을 이용한 JavaScript 디버깅 04, F12 개발자 도구 DOM 탐색기 창 Part 2

등록일시: 2019-01-22 08:00,  수정일시: 2019-01-22 08:00
조회수: 6,014

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

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

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

들어가기 전에

계속해서 본문에서는 지난번 글에 이어 F12 개발자 도구 DOM 탐색기 창의 스타일 탭이 제공해주는 고급 기능을 마저 살펴보고, 계산됨, 레이아웃, 이벤트, 그리고 변경 내용 탭의 기능들을 살펴봅니다.

스타일 탭의 고급 기능

의사 상태 패널

의사 클래스는 HTML 요소의 특정 상태를 지정하는 CSS 클래스입니다. 이를테면 대부분의 개발자에게 친숙한 의사 클래스 중 하나로 :hover를 예로 들 수 있습니다. 제 홈페이지의 상단 메뉴에 마우스 커서를 올려보면 링크의 색상이 파란색으로 변경되는데 이 역시 :hover 의사 클래스를 이용해서 스타일 규칙을 설정한 것입니다. 그런데 만약 이렇게 마우스가 호버된 상태의 스타일 규칙을 살펴보고 싶다면 어떻게 해야 할까요? 지금까지 알아본 내용만으로는 원하는 정보를 볼 수 있는 방법이 없습니다. 즉 마우스를 메뉴 위에 올려봐도 스타일 탭에 원하는 정보는 나타나지 않습니다. 바로 이런 경우에 사용할 수 있는 기능이 의사 상태 패널입니다.

제 개인 홈페이지인 EgoCube 사이트의 메인에서 기술강좌 메뉴를 마우스 오른쪽 버튼으로 클릭한 다음, 컨텍스트 메뉴에서 요소 검사를 선택합니다. 그러면 다음과 같이 스타일 탭이 나타납니다.

F12 개발자 도구 - DOM 탐색기 창 - 스타일 탭 - 의사 상태 패널

스타일 탭의 우상단을 살펴보면 a:라는 버튼이 위치해 있습니다. 버튼의 외형이 클릭할 수 있는 것처럼 보이지는 않지만 토글 형태의 버튼입니다. 이 버튼을 클릭하면 다음과 같이 의사 상태 패널이 나타납니다. :hover 좌측의 체크박스를 클릭해서 선택합니다.

F12 개발자 도구 - DOM 탐색기 창 - 스타일 탭 - 의사 상태 패널

F12 개발자 도구 - DOM 탐색기 창 - 스타일 탭 - 페이지 반영 결과

그러면 기존에는 보이지 않던 의사 클래스가 지정된 스타일 규칙이 나타납니다. 또한 실제로는 페이지에서 마우스가 메뉴 위에 호버되어 있지 않더라도 마치 마우스가 호버되어 있는 것처럼 항상 해당 의사 클래스가 지정된 스타일 규칙이 적용되어 나타납니다. 이 상태로 지금까지 살펴본 것처럼 스타일 속성의 값들을 변경하거나 추가해보면서 원하는 결과가 나올 때까지 자유롭게 편집이 가능합니다.

당연한 얘기지만 의사 클래스가 아닌 JavaScript를 이용해서 비슷한 동작을 구현한 경우에는 의사 상태 패널의 기능을 사용할 수 없습니다.

스타일 규칙 원본

특정 HTML 요소에 적용된 스타일 규칙은 여러 원본에 정의된 스타일 규칙들이 상속을 통해서 합쳐진 전체 집합입니다. 우선 원본이 요소 자체의 style 특성에 정의된 인라인 스타일일 수 있습니다. 또는 페이지 내부의 style 태그에 정의된 규칙일 수도 있습니다. 아니면 link 태그를 이용해서 별도의 CSS 파일을 연결한 것일 수도 있습니다.

지난번 글과 본문에서 지금까지 살펴본 스타일 탭의 규칙들은 나름 간단한 경우에 속하지만, 실제 업무에서는 상당히 많은 원본들이 어우러져 다양한 규칙들이 복잡하게 얽혀 있는 경우가 대부분입니다. 특히 외부 JavaScript 라이브러리를 많이 사용할 경우 (Bootstrap이나 jQuery UI 등), 함께 사용되는 CSS 파일들까지 더해져서 복잡도가 더욱 증가합니다.

만약 스타일 탭을 이용해서 문제의 원인이 된 스타일 규칙을 발견하고 해결 방안을 찾았다면 그 다음으로 해야 할 일은 실제 원본에 이를 반영하는 것일 것입니다. 이때 어떤 수정 사항을 어떤 원본에 반영해야 할지를 알고 싶다면 역시 스타일 탭의 기능을 활용할 수 있습니다.

F12 개발자 도구 - DOM 탐색기 창 - 스타일 탭 - 스타일 규칙 원본

다시 기술강좌 메뉴의 스타일 탭을 살펴보면 각각의 스타일 규칙 우측에 해당 규칙이 정의된 원본으로 이동할 수 있는 링크가 제공됩니다. 이 링크에 마우스를 올려보면 원본의 URL과 규칙이 정의되어 있는 줄 및 열 정보도 툴팁으로 제공됩니다. 그러나 이 줄과 열 정보는 사실상 거의 쓸모가 없는데, 최근에는 클라이언트 측 자산이 대부분 번들링되고 축소되기 때문입니다. 그래서 줄은 거의 항상 1이고 열은 쓸모없이 큰 수인 경우가 대부분입니다. 가령 제 홈페이지의 공통 CSS 파일도 축소되어 있기 때문에 역시 같은 상황입니다.

F12 개발자 도구 - DOM 탐색기 창 - 스타일 탭 - 스타일 규칙 원본 정보

링크를 클릭해봅니다. 그러면 다음과 같이 디버거 창이 열리면서 해당 원본의 소스가 나타납니다. 거의 내용을 파악하기가 힘들기는 하지만 이미지를 자세히 살펴보면 정확한 위치에 커서가 이동해있는 것을 알 수 있습니다.

F12 개발자 도구 - 디버거 창 - 스타일 규칙 원본

이런 경우에 큰 도움이 되는 것이 예쁜 인쇄 기능입니다. 디버거 창 상단 중앙부에 위치해 있는 버튼들 중, 예쁜 인쇄 버튼을 클릭해서 기능을 활성화시킵니다. 참고로 이 버튼은 클릭해도 잘 동작하지 않을 때가 많은데 그런 경우에는 단축키인 Ctrl+Shift+P 키를 누르면 됩니다.

F12 개발자 도구 - 디버거 창 - 스타일 규칙 원본 - 예쁜 인쇄

그러면 위의 이미지에서 볼 수 있는 것처럼 한결 보기 쉽게 줄바꿈과 들여쓰기가 지정된 서식화된 원본이 제공됩니다. 이 예쁜 인쇄 기능은 JavaScript 소스를 대상으로도 동작하기 때문에, 압축되지 않은 JavaScript 소스를 제공하지 않는 타사 라이브러리를 사용하거나 외부 사이트를 분석할 때도 큰 도움이 됩니다.

색 선택 팝업

스타일 탭에서도 색 선택 팝업을 사용할 수 있습니다. 다만 언급한 것처럼 rgba 형태의 색상값만 제공해주기 때문에 일관된 컨벤션에 민감한 분들에게는 다소 거슬릴 수 있습니다. 색 선택 팝업을 띄우려면 색상값 좌측의 작은 색상 박스를 클릭합니다. 다시 색 선택 팝업에서 색을 선택하면 스타일 속성의 값에 해당 색상이 설정됩니다.

F12 개발자 도구 - DOM 탐색기 창 - 스타일 탭 - 색 선택 팝업

계산됨 탭

계산됨 탭의 기능은 단순하지만 강력합니다. 그 이름처럼 해당 요소에 최종적으로 적용된 스타일 속성의 값들만 계산해서 보여줍니다. 또한 계산됨 탭에서 특정 속성의 하위 노드를 열어보면 해당 값이 어떤 과정을 거쳐서 얻어진 결과인지도 손쉽게 확인할 수 있습니다. 다음은 이전 글에서 살펴봤던 로고를 구성하는 앵커 요소를 계산됨 탭으로 살펴본 모습입니다. 적용된 스타일 속성들을 일목요연하게 확인할 수 있으며, 스타일 탭을 이용해서 font-size 속성을 추적했던 방법에 비해 너무도 간단히 캐스케이딩 과정을 파악할 수 있습니다.

F12 개발자 도구 - DOM 탐색기 창 - 계산됨 탭

다만 계산됨 탭에서는 체크박스를 이용해서 특정 스타일 속성을 활성/비활성시킬 수만 있을 뿐, 스타일 탭과는 달리 속성 값을 수정하거나 새로운 속성을 추가하는 등의 편집 기능은 지원하지 않습니다. 참고로 의사 상태 패널 기능은 스타일 탭과 동일하게 지원됩니다.

엄밀히 말해서 특정 요소에 적용된 스타일 속성에는 개발자가 지정한 속성만 존재하는 것은 아닙니다. 내부적으로는 브라우저에 의해 지정된 기본값이 함께 존재합니다. 계산됨 탭 우측 상단의 사용자 스타일만 표시 버튼을 클릭해서 해제하면 기본값을 비롯한 해당 요소에 적용된 모든 스타일 속성의 값을 확인할 수 있습니다.

F12 개발자 도구 - DOM 탐색기 창 - 계산됨 탭

레이아웃 탭

레이아웃 탭은 선택한 요소의 박스 모델을 도식으로 보여줍니다. 다음은 제 홈페이지 메인의 차트를 감싸는 idchartdiv 요소를 레이아웃 탭으로 살펴본 모습입니다. 이 도식을 통해서 해당 요소의 상대적 위치, 마진, 보더, 패딩, 너비 및 높이를 시각적인 방식으로 살펴보거나 편집할 수 있습니다. 예를 들어 아래 도식을 통해서 이 div 요소가 상단으로부터 432px 떨어져 있고 좌측으로부터 588px 떨어져 있으며 너비 및 높이는 236.95px308px로 지정되었고, 마진, 보더, 패딩이 각각 특정 값으로 설정되었음을 알 수 있습니다.

F12 개발자 도구 - DOM 탐색기 창 - 레이아웃 탭

각 값을 마우스로 클릭하면 값을 편집할 수도 있습니다. 그러나 모든 경우에 변경한 값이 적용되는 것은 아닙니다. 가령 이 div 요소의 좌측 위치는 부모 요소와 그 형제 요소에 의해 결정되기 때문에 값을 변경해도 원래 상태로 돌아옵니다. 또한 요소에 따라서 의사 상태 패널 기능도 제공되어 특정 상태의 박스 모델을 확인할 수도 있습니다.

재미있는 점은 이 레이아웃 탭의 박스 모델에서 볼 수 있는 각 항목의 색상과 이전 글에서 살펴본 요소 선택, DOM 요소 강조 표시 버튼을 사용할 때 페이지에서 반전되는 요소의 색상이 동일하다는 것입니다. 직접 비교해보시기 바랍니다.

박스 모델을 살펴볼 때 주의해야 할 점 한 가지는 box-sizing 스타일 속성이 어떻게 지정되어 있는지에 따라서 해당 요소의 실제 렌더링 크기가 달라질 수 있다는 사실입니다. 다음 예제 HTML 코드를 레이아웃 탭으로 살펴보시기 바랍니다. (새 창에서 보기)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>박스 모델 예제 01</title>
    <style>
      body {
        font-size: 12px;
        font-family: Tahoma;
      }
      div {
        width: 100px;
        height: 100px;
        margin: 5px;
        padding: 5px;
        border: solid 5px #222;
      }
    </style>
  </head>
  <body>
    <h3>default (no box-sizing)</h3>
    <div></div>

    <h3>box-sizing: content-box</h3>
    <div style="box-sizing:content-box;"></div>

    <h3>box-sizing: border-box</h3>
    <div style="box-sizing:border-box;"></div>
  </body>
</html>

이 HTML 페이지는 div 태그를 이용해서 세 개의 박스를 화면에 출력합니다. 마크업만 살펴봤을 때는 마진, 보더, 패딩, 너비 및 높이가 모두 일정한 박스입니다. 단 한 가지 차이점은 box-sizing 스타일 속성이 지정되지 않았거나, content-box로 지정됬거나 border-box로 지정된 것 뿐입니다. 다음은 실제 브라우저 출력 결과입니다.

F12 개발자 도구 - DOM 탐색기 창 - 레이아웃 탭 - 페이지 결과

간단히 봐도 상당히 재미있는 결과가 나왔습니다. 먼저 이 결과에서 추측할 수 있는 점은 IE11에서 box-sizing 스타일 속성의 기본값이 content-box라는 것입니다. 그리고 대부분의 최신 브라우저에서 이는 사실이기도 합니다. 따라서 본문에서는 두 번째 결과와 세 번째 결과만 서로 비교해보도록 하겠습니다. 다음 이미지는 두 박스를 레이아웃 탭으로 살펴본 결과를 비교하기 좋게 편집한 것입니다.

F12 개발자 도구 - DOM 탐색기 창 - 레이아웃 탭 - 비교

이렇게 나란히 놓고 비교해보면 렌더링된 박스의 크기가 차이나는 이유가 명확하게 보입니다. 결론부터 말해서 content-box로 지정된 요소는 너비와 패딩, 보더가 각각 별개로 간주되어 모두 더해집니다. 편의를 위해 마진을 감안하지 않을 경우, 이 예제에서 두 번째 박스의 실제 너비는 지정한 너비(100px) + 좌우 패딩(5px * 2) + 좌우 보더(5px * 2)를 모두 더한 합인 120px입니다.

반면 border-box로 지정된 세 번째 박스는 지정한 너비(100px)가 그대로 렌더링된 실제 요소의 너비가 됩니다. 그 대신 좌우 패딩(5px * 2) + 좌우 보더(5px * 2)에 해당하는 너비 만큼 요소의 내부 너비를 점유하기 때문에 실질적인 요소의 내부 너비는 해당 너비를 뺀 80px이 됩니다.

브라우저마다 구현 시기가 조금씩 차이나기는 하지만 이 box-sizing 스타일 속성은 CSS3에서 처음 등장했습니다. 그리고 IE6나 IE7 같은 오래된 브라우저에서는 기본적으로 border-box를 지정한 것처럼 동작했었습니다. 그러다가 어느 순간 box-sizing 스타일 속성이 등장했는데 그 기본값이 content-box였기 때문에 새 버전의 브라우저에서 많은 사이트의 레이아웃이 갑자기 깨지는 상황이 벌어지기도 했었습니다.

이벤트 탭

이벤트 탭은 선택한 요소에 연결된 이벤트 핸들러의 목록을 보여줍니다. 다음 예제 HTML 코드를 이벤트 탭으로 살펴보시기 바랍니다. (새 창에서 보기)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>이벤트 예제 01</title>
    <style>
      body {
        font-size: 12px;
        font-family: Tahoma;
      }
      input[type="button"] {
        display: block;
        width: 150px;
        height: 30px;
        margin: 5px;
      }
    </style>
  </head>
  <body>
    <input id="btn01" type="button" value="onclick Attribute" onclick="click1();click2();" onmouseover="over1();" />
    <input id="btn02" type="button" value="addEventListener" />
    <input id="btn03" type="button" value="jQuery.click()" />

    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.js"></script>
    <script>
      // Attribute에 직접 설정
      function click1() {
        console.log("Called from attribute-style onclick event Handler! (1)");
      }

      function click2() {
        console.log("Called from attribute-style onclick event Handler! (2)");
      }

      function over1() {
        console.log("Called from attribute-style onmouseover event Handler!");
      }

      // addEventListener 메서드로 설정
      var objBtn02 = document.getElementById("btn02");
      objBtn02.addEventListener("click", function(event) {
        console.log("Called from addEventListener-style onclick event Handler! (1)");
      });

      objBtn02.addEventListener("click", function(event) {
        console.log("Called from addEventListener-style onclick event Handler! (2)");
      });

      objBtn02.addEventListener("mouseover", function(event) {
        console.log("Called from addEventListener-style onmouseover event Handler!");
      });

      // jQuery.click() 및 jQuery.mouseover() 메서드로 설정
      $("#btn03").click(function() {
        console.log("Called from jQuery-style onclick event Handler! (1)");
      });

      $("#btn03").click(function() {
        console.log("Called from jQuery-style onclick event Handler! (2)");
      });

      $("#btn03").mouseover(function() {
        console.log("Called from jQuery-style onmouseover event Handler!");
      });
    </script>
  </body>
</html>

이 HTML 페이지는 평범한 세 개의 버튼을 출력합니다. 버튼들 간의 차이점은 이벤트 핸들러를 특성에 직접 설정했는지, JavaScript를 이용해서 addEventListener 메서드로 이벤트 핸들러를 설정했는지, 아니면 jQueryclick 메서드나 mouseover 메서드를 이용해서 이벤트 핸들러를 설정했는지 밖에 없으며 실제 동작 결과는 겉으로 보기에는 모두 동일합니다. 각 버튼을 이벤트 탭으로 살펴본 모습은 다음과 같습니다. 이번에도 비교하기 편하게 이미지를 편집했습니다.

F12 개발자 도구 - DOM 탐색기 창 - 이벤트 탭

가장 기본적인 방식인 특성 방식으로 이벤트 헨들러를 설정할 경우, 한 이벤트 특성에 여러 개의 함수를 설정하더라도 이벤트 유형별로 하나의 이벤트 핸들러만 표시됩니다. 이 예제에서는 idbtn01인 버튼의 onclick 특성에 두 가지 함수, 즉 click1() 함수와 click2() 함수를 설정하고 있지만 이벤트 탭에는 onclick이라는 이벤트 핸들러만 표시됩니다. 아마도 이벤트 특성에 지정된 JavaScript 코드 전체를 하나의 스크립트 블럭으로 간주하는 것으로 보입니다. 이 경우 이벤트 핸들러 우측의 원본 링크를 클릭하면 디버거 탭이 열리면서 해당 이벤트를 정의하고 있는 HTML 태그의 특성으로 바로 이동합니다.

반면 addEventListener 메서드를 사용해서 이벤트 헨들러를 설정하는 경우에는 각각의 익명 함수가 개별적으로 설정된 것으로 표시됩니다. 이 경우에는 이벤트 핸들러 우측의 원본 링크를 클릭하면 디버거 탭이 열리면서 해당 이벤트를 정의하고 있는 JavaScript의 익명 함수 선언으로 바로 이동합니다.

그런데 jQuery를 이용해서 이벤트 헨들러를 설정한 경우에는 그 결과가 매우 흥미롭니다. 결론부터 말하면 위의 이미지에서 볼 수 있는 것처럼 모든 이벤트가 elemData.hadle이라는 뭔가 수상한 함수로 연결됩니다. 실제 코드에서는 addEventListener 방식과 비슷하게 익명 함수가 사용되고 있지만 정작 이벤트 탭의 목록에는 익명 함수가 전혀 보이지 않습니다. 이벤트에 설정된 익명 함수의 개수나 이벤트 유형과는 무관하게 항상 elemData.hadle이라는 함수로 연결됩니다. 이벤트 핸들러 우측의 원본 링크를 클릭해보면 초보자가 이해하기에는 약간 무리가 있지만 실제로 jQuery 내부에 정의된 해당 함수로 이동하는 것을 확인할 수 있습니다. (이를 보여 드리기 위해 일부러 위의 예제에서 축소되지 않은 jQuery의 CDN을 사용했습니다.)

본문에서는 jQuery의 이벤트 동작 메커니즘에 대해 자세히 설명하지는 않습니다. 다만 나중에 메모리 창을 살펴보면서 다시 거론하겠지만 이런 특징으로 인해서 jQuery로 클라이언트 측의 이벤트 헨들러를 동적으로 관리할 때 아주 사소한 이유로 생각치도 못한 메모리 누수가 발생할 수 있습니다. 일단 본문에서는 이에 대한 간단한 지침만 제시합니다.

jQuery로 동적 이벤트 핸들러를 할당한 HTML 요소는 반드시 jQuery의 기능을 이용해서 제거하십시오.

변경 내용 탭

변경 내용 탭의 기능은 간단합니다. 스타일 탭 등을 이용해서 페이지의 UI 요소들을 이리저리 테스트하다 보면 어느새 많은 수정 사항들이 발생하게 됩니다. 그런 경우, 어떤 값을 어떻게 변경했는지 모든 사항을 기억하기는 어렵기 때문에 혼란스럽기 마련입니다. 변경 내용 탭은 변경되기 전의 원본 값과 변경된 후의 수정 값을 비교하기 좋게 정리해서 보여줍니다.

F12 개발자 도구 - DOM 탐색기 창 - 변경 내용 탭

정리

지금까지 2회에 걸쳐서 F12 개발자 도구의 DOM 탐색기 창이 제공하는 다양한 기능들을 살펴봤습니다. 이미 언급한 바와 같이 다른 최신 브라우저들이 제공하는 F12 개발자 도구의 기능에 비해 IE11의 F12 개발자 도구는 매우 기본적인 기능들만 제공합니다. 반드시 본인이 주력으로 사용하는 최신 브라우저를 이용해서 지금까지 살펴본 기능에 대응하는 기능들을 직접 테스트해 보시기 바랍니다.

계속해서 다음 글부터는 본격적인 디버깅 도구인 디버거 창의 기능을 살펴봅니다.