본문 바로가기

FRONT-END/JAVASCRIPT

자바스크립트 디자인 패턴 #5. Init-time branching 패턴

반응형

Init-time branching 패턴

Init-time branching 패턴은 자바스크립트에서 매우 유용한 패턴 중 하나이다.

이름 그대로 초기화 단계에서 분기하여 같은 함수를 환경에 따라 다르게 정의하는 것을 의미한다.

보통 웹페이지가 처음 열릴 때 실행된다고 해서 Init-branching 패턴이라고도 하며, 페이지가 로드될때 실행되기 때문에 Load-branching 패턴이라고도 한다.


이 패턴의 유용한 이유는 자바스크립트가 다양한 브라우저 환경에서 제공되어야 하기 때문이다.

최근에는 웹 표준이 확립되어 대부분 브라우저에서 자바스크립트에 대한 거의 모든 함수를 지원하지만, 웹표준이 빠르게 확립되기  전이나 확립되는 과정에서는 그렇지 않았다.

따라서 주요 기능에 대해서 이러한 호환성을 보장해줄 때 사용하면 좋은 패턴이다.


일부 웹페이지에서는 자바스크립트가 실행될 때 어떠한 브라우저인지 판단하여 다른 처리를 하곤한다.

(function(){

  var IEVersion = getIEVersion();

  if(IEVersion === -1){

    alert("Not IE");

  }else if(IEVersion < 9) {

    alert("IE8 or lower, need to upgrade");

  }else{

    alert("IE9 or higher");

  }


  function getIEVersion(){

    if(navigator.appName == "Microsoft Internet Explorer" && /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(navigator.userAgent) != null){

      return parseFloat(RegExp.$1);

    }else{

      return -1;

    }

  }

}());

getIEVersion() 함수 안에서는 우선 브라우저의 이름이 "Microsoft Internet Explorer"인지 비교한 다음, 정규 표현식으로 MSIE 문자열 뒤에 오는 버전 숫자를 "."을 포함해서 가져오도록 하고 있다

웹페이지들은 Internet Explorer 8 또는 그 이하 버전을 가장 많이 구분하기 위해 navigator.userAgent를 사용한다.


중간의 MSIE 8.0 숫자를 가져오기 위한 정규 표현식을 사용하고 있으며, 이러한 문자열이 발견되지 않으면 -1 을 반환한다.

위의 함수를 호출하고 나서 -1이면 Internet Explorer가 아닌 것으로 생각하면되고, 그 외의 숫자는 MSIE 위에 오는 버전을 반환하므로 각각의 버전에 맞게 처리하면된다.

그러나 과거에는 이렇게 처리하는 웹페이지들이 많았으나, 최근에는 이러한 처리방식은 적당하지 않다.


이벤트 핸들러 호환성 지원

브라우저 버전에 따라서 다른 기능을 설정하는 것이 Init-time branching 패턴응용의 기본이다.

웹페이지들이 Internet Explorer 8을 구분하기 위한 이유는 이벤트 핸들러 설정 때문이다.

이벤트 핸들러는 이미 많은 브라우저가 웹 표준을 지키키 위해 노력하고 있어서 대부분 브라우저는 문제없이 돌아간다.

그러나 또 다른 브라우저, 아니면 구버전 브라우저를 생각해서 이벤트 핸들러의 호환성을 지원해주는 것이 좋다.


(function(){

  function getInternetExplorerVersion(){

    var rv = -1;

    if(navigator.appName == "Microsoft Internet Explorer"){

      var ua = navigator.userAgent;

      var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");

      if(/"MSIE ([0-9]{1,}[\.0-9]{0,})"/.exec(ua) != null){

        rv = paseFloat(RegExp.$1);

      }

    }

    return rv;

  }


  var ieVersion = getInternetExplorerVersion(),

       addEventHandler,

       removeEventHandler;

if(ieVersion !== -1 && ieVersion < 9){

  addEventHandler = function(dom, type, fn){

    dom.attachEvent('on'+type, function(){

      fn.call(dom, window.event);

    });

  }

  removeEventHandler = function(dom, type, fn){

    dom.detachEvent('on'+type, fn);

  }

}else{

  addEventHandler = function(don, type, fn){

    dom.addEventListener(type, fn);

  };

  removeEventHandler = function(dom, type, fn){

    dom.removeEventHandler(type, fn);

  }

}



}());


W3C 웹표준에서는 addEventListener() 또는 DOM에서의 onclick 속성 등과 같은 방법으로 이벤트 핸들러를 할당하도록 정의하고 있다.

그런데 Interner Explorer 8 이하버전에서는 DOM의 함수로 addEventListener()가 없고  attachEvent() 함수를 사용하기 때문에 Internet Explorer의 버전을 가져와서 9버전보다 낮은 버전이면 attachEvent()함수를 사용하고

아니면 W3C   표준대로 addEventListener()함수를 사용하도록 하고 있다.


attachEvent() 함수의 사용법은 addEventListener()함수와 다르다.

이를 중재하는 기능을 제공해주면 좋고, addEventHandler()와 removeEventHandler()라는 사용자 정의 함수를 추가하여 이벤트 핸들러를 할당하고 있다.

특히 이벤트 핸들러는 웹페이지의 기능 제공을 위해서 핵심이기도 하고, 노력에 비해 얻을 수 있는 기대효과가 크므로 아직은 고려해줘도 괜찮을 것이다.


이벤트핸들러를 할당하는 기능을 브라우저의 버전에 따라서 다르게 제공하도록 구현해놨지만, 사실 브라우저 버저만 가지고 구분하는 일이 꼭 좋은 것만 아니다.

브라우저 버전 마다 웹표준에서 지원해주는 기능이 제 각각이여서 명확하게 Internet Explorer 8이하는 attachEvent()함수를 사용해야 한다는 것이 구분되지 않는다.

대표적인 것이 바로 ECMAScript 6의 추가 기능들은 아직도 세부 기능 지원이 확대되고 있으며, 이후의 새로운 ECMAScript 표준에 대한 추가 기능들은 앞으로도 호환성을 고민해야한다.


이벤트핸들러 예도 "호환성" 측면에서 브라우저의 종류와 버전에 따라서 구분하는 것이 아니라 현재 브라우저의 기능이 있느냐 없느냐를 기준으로 제공하는 것이 더 좋은 방법이다.

Internet Explorer 버전을 가져오는 것은 사용하지 않고 addEventListener()함수 여부에 따라서 처리하는 것이 더 바람직하다.

(function(){

  var addEventHandler,

      removeEventHandler;


  if(document.addEventListener) {

    addEventHandler = function(dom, type, fn){

      dom.addEventListener(type, fn);

    }

    removeEventHandler = function(dom, type, fn){

      dom.removeEventListener(type, fn);

    }

  }else if(document.attachEvent){

    addEventHandler = function(dom, type, fn){

      dom.attachEvent('on'+type, function(){

        fn.call(dom, window.event);

      });

    };

    removeEventHandler = function(dom, type, fn){

      dom.detachEvent('on'+type, fn);

    };

  }else{

    addEventHandler = function(dom, type, fn){

      dom["on"+type] = fn;

    };

    removeEventHandler = function(dom, type, fn){

      dom["on"+type] = null;

    };

  }

}());

같은 addEventHandler() 함수지만, 초기화 단계에서 브라우저의 기능에 따라서 호출되도록 함수가 변경되는 것을 Init-time branching 패턴이라고 한다.

자바스크립트는 변수에 함수를 할당할 수 있고, 그것을 변경하는 것 또한 자유로워서 이렇게 시작 단계에 사용자 환경이나 현재 상태에 따라서 다른 역할을 수행하게 할 수 있다.


addEventHandler()함수는 표준함수가 아닌 사용자 정의함수이다.

따라서 소스관리 측면에서는 다소 안좋을 수도 있으므로 한번 더 보완하는 것도 좋다.

이번에는 아예 최초 초기화를 할때 addEventListener()함수가 있는지 확인하고 없으면 attachEvent()함수를 수용할 수 있도록 한다.

(function(){

  if(!Element.prototype.addEventListener){

    if(Element.prototype.attachEvent){

      Element.prototype.addEventListener = function(type, fn){

        this.attachEvent("on"+type, fn);

      }

    }else{

      Element.prototype.addEventListener = function(type, fn){

        this["on"+type] = fn;

      }

    }

  }

  if(!Element.prototype.removeEventListener){

    if(Element.prototype.detachEvent){

      Element.prototype.removeEventListener = function(type, fn){

        this.detachEvent("on"+type, fn);

      }

    }else{

      Element.prototype.removeEventListener = function(type, fn){

        this["on"+type] = null;

      }

    }

  }


}());

DOM 요소들의 기본 함수로 addEventListener()가 있으면 그대로 사용하고, addEventListener()가 없으면서 attachEvent()가 있으면 attachEvent()함수를 호출하도록 변경한다.

removeEventListener()함수도 마찬가지로 표준에 정의된대로 사용하도록 함수를 만들어서 등록한다.


Init-time branching 패턴의 장점은 브라우저 환경이나 상태에 따라서 같은 기능을 다양한 방법으로 제공할 수 있어서 매우 유용하다.

하지만 어찌되었든 최초 최기화 시점에 그러한 브라우저 환경과 상태를 확인하기 위한 컴퓨티 자원이 소모되므로 최초 초기화 때 소모하는 것이 나을지, 아니면 매번  컴퓨팅 자원을 소모하는 것이 나을지 판단하여 선택하면 좋다.


출처-속깊은 자바스크립트 양성익 지음

반응형