javascript

자바스크립트 성능 향상 팁

jeeyong 2007. 11. 23. 08:47

자바 스크립트 사용 시 성능 향상을 위해 주의할 사항입니다.

 

1. 전역변수, 로컬변수 구분을 확실히 할 것

자바스크립트는 변수 선언 시 var 를 붙이지 않아도 에러를 발생시키지 않습니다. 따라서 개발자마다 var 를 임의대로 붙이거나 붙이지 않고 사용하는 경우를 자주 볼 수 있습니다. 그러나 이러한 사용은 간혹 예상치 않은 문제를 야기하거나 성능에 영향을 줄 수 있습니다.

예를 들어 함수에서 변수 선언자 var 없이 status 라는 변수가 선언된 경우를 가정해봅시다. 개발자는 status 변수를 함수 내부에서 단순히 값을 저장하거나 참조할 단순 목적으로만 사용하고자 하였을 겁니다. 그러나 브라우저는 status 변수가 선언된 코드를 만났을 때, 먼저 window 객체 내에서 해당 변수에 대한 검색을 시작하게 됩니다. (자바 스크립트가 수행되는 scope 는 기본적으로 window 라는 객체 범위이며, 따라서 기본적으로 선언되는 메서드, 변수 등도 window 라는 객체 내부에 멤버 메서드, 변수로 선언이 된다고 볼 수 있습니다.) 이때 status window 의 기본 속성값으로 존재하고 있기 때문에 window.status 의 값이 참조되거나 window.status 의 값이 변경될 수 있으며, 이는 의도하지 않은 상황을 만들어 낼 수 있습니다. 따라서 var 없이 사용한 status 변수는 예상치 않은 문제를 야기할 수 있게 되는 것입니다. 물론 status 라는 변수명이 아니라 다른 변수명(window 에 기본으로 포함되어 있지 않은)을 사용하는 경우는 문제는 발생하지 않을지 몰라도, 검색 범위를 window 까지 확장하게 됨으로 인하여 성능 저하를 발생시킬 수 있습니다. 자바스크립트 코드가 많을 경우 검색 범위가 더 커지게 됩니다.

따라서 함수내에 변수 선언 시 var 를 항상 붙여 주는 것이 필요하다고 할 수 있습니다.

function WorkOnLocalVariable()
{
  local_variable = ObtainValueFromDOM();       // var 지정을 필요가 있음
  return (local_variable + 1);
}



2. 가능한 경우 캐쉬를 사용할 것

자바스크립트는 기본적으로 late binding 을 하게 됩니다. 다시 말하면, 해당 코드를 만나는 순간 코드상에서 참조되는 속성, 함수, 변수들을 검색(look-up) 하게 됩니다. 따라서 동일한 참조 코드가 반복적으로 있을지라도, 매 코드마다 동일한 검색 작업을 반복하게 됩니다.

예를 들어, 다음의 코드에서는 innerHTML 이 존재하는 코드마다 값을 set, get 하기 위해 baseElement 로부터 innerHTML에 대한 검색이 반복적으로 수행되어 집니다. 다음 예에서는 해당 작업이 4번 반복해서 발생한다고 볼 수 있습니다. 물론 이외에도 + 연산을 위해 임시 변수를 필요로 하겠지만요.. 이러한 동일한 작업을 반복적으로 요구하는 코드는 수행성능을 떨어뜨리는 원인이 될 수 있습니다.

function BuildUI()
{
      var baseElement = document.getElementById(‘target’);
      baseElement.innerHTML = ‘’; // Clear out the previous
      baseElement.innerHTML += BuildTitle();
      baseElement.innerHTML += BuildBody();
      baseElement.innerHTML += BuildFooter();
}

위의 코드는 다음과 같이 수정될 수 있습니다.

function BuildUI()
{
      var elementText = BuildTitle() + BuildBody() + BuildFooter();
      document.getElementById(‘target’).innerHTML = elementText;   // set 하기 위해 한번만 lookup 발생한다.
}

이외에도 DOM 객체의 참조 시 매번 document 부터 시작하는 코드를 볼 수 있는데, 이러한 코드도 또한 반복적인 검색을 발생시켜 성능을 저하시키는 원인으로 작용하게 됩니다.

function CalculateSum()
{
      var lSide = document.body.all.lSide.value;
      var rSide = document.body.all.rSide.value;
      document.body.all.result.value = lSide + rSide;
}

위의 함수는 다음과 같이 고쳐질 수 있습니다.

function CalculateSum()
{
      var elemCollection = document.body.all; // Cache this
      var lSide = elemCollection.lSide.value;
      var rSide = elemCollection.rSide.value;
      elemCollection.result.value = lSide + rSide;
}

 

3. 다수 문자열을 하나의 문자열로 합치는 경우 array 를 사용할 것

문자열에 대해 + 연산자를 사용하는 경우 코딩이 쉽고 다소 읽기 쉬운 코드 구조가 되기 때문에 자주 사용하게 되지만, 성능이 느린 단점을 가지고 있습니다. 특히 소수 문자열에 대해 + 연산을 사용하는 것은 별 차이가 없지만 많은 문자열에 대해 + 연산을 수행하는 경우 심각한 성능 저하를 초래할 수 있습니다. 이에 대한 해법으로는 array 를 사용하여 코드를 구성하는 것입니다.

예를 들어 + 연산을 사용하는 다음의 코드에 대해

 

var str = "Hello" + " World";

 

array 를 사용하여 다음의 코드처럼 작성하여 주시면 됩니다.

 

var buffer = new Array();

buffer[buffer.length] = "Hello";

buffer[buffer.length] = " World";

 

buffer.join("");


 

1만개의 문자열에 대해 두가지 방식으로 연산을 수행하였을 경우 '+' 연산은 460 (ms), array 사용은 15(ms) 정도 걸리게 됩니다. 엄청난 차이겠지요.

 


4. eval 사용을 가급적 자제할 것

I/E 4.0(?) 이상부터는 DOM 을 사용하여 HTML 내에 있는 모든 element 에 대해 접근이 가능합니다.  eval() 을 사용하여 객체를 얻어내어 참조하는 방식의 코드는 상당히 낮은 성능을 초래할 수 있으며, 2에서 언급한 캐쉬 방식의 사용에도 적합하지 않을 수 있습니다.  따라서 HTML 내부의 element 에 대한 접근은 DOM 구조에 의해 참조하는 형태로 코드를 작성하여 주시기 바랍니다. DOM 에 대한 참조는 document 로부터 시작하며, 포함된 element 의 이름을 사용하여 객체를 참조하시면 됩니다.

예를 들어, form_name 이라는 이름을 가진 form element 안에 input_name 이라는 이름을 가진 input 요소의 값을 참조하고자 할 때에는

 

document.form_name.input_name.value = ...

 

로 코드를 작성하시면 됩니다.

기존의 방식에서는

 

var inputName = eval( document. + form_name. + input_name);

inputName.value =


 

처럼 코드를 작성하였겠지요.

DOM 형식의 코딩에 대해서는 추후 관련 자료를 올리도록 하겠습니다.

 

5. innerHTMl createElement

innerHTML createElement 보다 성능이 훨씬 더 좋습니다(대략 5배 정도). 하지만 DOM 방식에서는 createElement 를 사용하여 DOM 을 다루는 것이 필요할 수도 있기 때문에 상황에 따라 두 경우를 혼용해서 사용하시면 될 듯 합니다.

다만, createElement 를 사용하실 때 createElement 에 의해 생성된 객체를 document 에 먼저 appendChild 한 후 작업하시는 것과 작업한 후 appendChild 하는 것에는 rendering 시간이 현격하게 차이날 수 있습니다. 전자가 두 배 정도 더 빠르게 처리합니다. 이유는 모르겠습니다.

 

예를 들어,

코드 1

var table = document.createElement("table"); // 생성(create)하고

table.border = 1;

container.appendChild(table);          // 곧바로 붙인다(append)

 

var tbody = document.createElement("tbody"); // 생성(create)하고

table.appendChild(tbody);              // 곧바로 붙인다(append).

 

for (var i = 1; i <= rows; i++){

        var tr = document.createElement("tr");

        tbody.appendChild(tr);

        for (var j = 1; j <= cols; j++){

                var td = document.createElement("td");

               tr.appendChild(td)

               td.appendChild(document.createTextNode(i*j));

        }

}


 

코드 2

var tbody = document.createElement("tbody");

 

for (var i = 1; i <= rows; i++){

    var tr = document.createElement("tr");    // 생성하고

    for (var j = 1; j <= cols; j++){

         var td = document.createElement("td");

         td.appendChild(document.createTextNode(i*j));

 

         tr.appendChild(td)                   // 작업하고

    }

    tbody.appendChild(tr);                    // 붙인다.

}

var table = document.createElement("table");

table.border = 1;

 

table.appendChild(tbody);

container.appendChild(table);


 

에서 코드 1 이 코드 2 보다 두 배 정도 빠를 수 있습니다.

DOM 을 다루는 경우 위의 경우를 기억하셨다가 사용하여 주시길 바라겠습니다.

 

예전에는 스크립트의 사용 범위가 단순한 요청 검사, 처리 위주로 머물렀기에 수행 시간에 대한 고려가 거의 필요 없었습니다. 하지만, web2.0이 나타나면서 rich experienced 사용자 인터페이스에 대한 요구, Ajax 사용, 고급 UI 등에 대한 요구로 인하여 자바스크립트 코드의 필요성이 증가하게 되었고, 이로 인하여 애플리케이션의 성능에 영향을 미치는 상황이 다소나마(?) 발생하고 있습니다. '다소나마' 보다는 '좀더 많이' 라고 보심이 맞을듯 합니다.

향후 자바스크립트의 비중이 확대되어짐이 예상되며, 이에 대한 대비로 자바스크립트 개발에 대해 어느정도 학습을 통하여 준비를 해주셨으면 합니다.

위에서 언급한 사항은 성능 향상을 위한 일부 팁에 해당합니다. 이외에도 자바스크립트 성능과 관련된 사항을 보시게 되면 공유를 부탁 드리겠습니다.