Rev. 2.73

Ajax가 성행하면서 자바스크립트로 DOM을 직접 다뤄야 하는 일이 잦아졌다. 만약 자바스크립트를 이용해 노드를 대량으로 생산해야하는 임무가 주어졌다면 어떻게 처리하는 것이 효과적일까? 실제로 이와 비슷한 프로세스를 수행하는 이미지 리플랙터의 소스코드를 분석하고 이를 개선해 보자. 참고로, 이미지 리플랙터는 이미지의 반사된 잔상효과를 만들어주는 이펙트 라이브러리이다.

var Reflector = {
    reflect: function(element){
        element = $(element);
        var options = $H({
            amount: 1/3,
            opacity: 1/3
        }).merge(arguments[1] || {});
        var p = element.parentNode, n = element.nextSibling;
        var d = 1.0/(element.height*options.amount);
        (element.height*options.amount).times( function(line){
            var h = Builder.node('div',{style:'height:1px;overflow:hidden;'},
            [Builder.node('img',{src:element.src,
                style:'margin-top:-'+(element.height-line-1)+'px'
            })]);
            p.insertBefore(h,n);
            $(h).setOpacity((1-d*line)*options.opacity);
        });
    }
};

// Run
Reflector.reflect('img_id',{ amount:1/5, opacity:1/4 });

위 코드는 이미지 리플랙터의 원본 코드로서 실행되면 브라우저가 먹통이 되는 증상을 보인이다. Scriptaculous 프레임웍의 builder.js 라이브러리를 사용하는 Builder 메서드를 사용해서 높이 1px짜리 DIV와 IMG태그를 대량으로 생산하고 있다. Builder메서드는 엘리먼트를 생성하기 위한 document.createElement()와 innerHTML, Prototype의 setStyle 등을 간단하게 사용할 수 있게한다. 즉 createElement와 setStyle 메서드가 루프를 돌면서 브라우저에게 마비증상을 선사(?)하는 것이다.

var Reflector = {
    reflect: function(element){
        element = $(element);
        var options = $H({
            amount: 1/3,
            opacity: 1/3
        }).merge(arguments[1] || {});
        var p = element.parentNode, h = '';
        var d = 1.0/(element.height*options.amount);
        var m = document.createElement('DIV');
        for(var i=0; i < element.height*options.amount; i++){
            h += '<div style="height:1px; overflow:hidden;'+this.setOpacity((1-d*i)*options.opacity)+'">';
            h += '<img src="'+element.src+'" style="margin-top:-'+(element.height-i-1)+'px" alt="" /></div>';
        }
        m.innerHTML = h;
        p.insertBefore(m, element.nextSibling);
    },
    setOpacity: function(value) {
        var style = 'opacity:'+value;  
        if(Prototype.Browser.IE) style = 'filter:alpha(opacity='+value*100+')';
        return style;
    }
};

이것은 innerHTML에 직접 코드를 삽입하는 방식으로 변경한 것이다. 이미지 하단에 들어갈 'DIV'엘리먼트를 하나만 생성하고 루프되는 코드를 직접 작성하여 생성한 DIV에 innerHTML로 넣어준다. 결과는 완전히 동일하지만 소요되는 시간차이는 상당하다. createElement메서드로 640x360 이미지의 효과를 만들어 내는데 걸리는 시간은 파이어폭스가 2.25~4.563초, 익스플로러(IE7)가 12.19~15.18(HTML이 들고있는 주변 태그에 영향을 크게 받는다. 상단의 문법 하이라이트 코드를 수정했더니 결과가 확 바뀌었다.)초 정도가 소요된다. innerHTML 방식은 파이어폭스가 0.07~0.11초, 익스플로러가 0.04~0.11초 정도로 환경에 따라 다르겠지만 약 30 ~ 50배 가까이 소요시간을 단축하는 효과를 볼 수 있다. 생성하는 노드의 수가 많으면 많을 수록 더욱 큰 차이를 보인다. 아래의 이미지를 직접 테스트해 보자.

Faux_Fur_wide_screen.jpg

최근에는 노드를 대량으로 생성하는 방식으로 엘리먼트의 코너를 둥글게하거나, 대각선을 그리는 등 굳이 이미지를 사용하지 않아도 자바스크립트를 사용하여 DOM을 동적으로 표현하는 꼼수들이 속속 선보이고 있다. 가뜩이나 새로선보인 IE7 RC1의 노드생성속도는 완전 쉣이다. 랙현상으로 인해 사용이 꺼려진다면 innerHTML로 DOM을 직접 그려보는 것도 좋은 대안이 될 수 있겠다.

Comments