Nextree

jQuery : 플러그인 개발

Nextree May 27, 2014 0 Comments

개발 작업을 간단하게 처리하거나 자동화하는 jQuery는 자바스크립트의 핵심 라이브러리가 된지 오래입니다. 많은 개발자들이 jQuery의 기본 기능을 사용하여 작업을 수행합니다. 하지만 더 나아가 플러그-인을 사용하여 jQuery를 확장하여 사용하기도 합니다. 이미 배포된 플러그-인을 적용해서 사용할 수 있지만 직접 플러그-인을 개발하여 프로젝트에서 활용한다면 개발팀의 기술 수준 뿐만 아니라 개발 생산성을 더 높일 수 있습니다. 오늘은 왜 플러그-인이 필요한 지 알아본 다음, 간단한 플러그-인을 예로, 플러그-인 설계 규칙과 개발 방법을 소개합니다.

왜 jQuery 플러그-인을 만드는가

다음과 같은 두가지 이유가 있습니다.

  • 일관된 코드 스타일을 유지한다.
  • jQuery의 기반 코드를 잘 활용한다.

jQuery가 자바스크립트 라이브러리로서 널리 사용되고 있기에 스크립트 개발방식에 상당한 영향을 주고 있습니다. 예제1을 살펴보겠습니다. 셀렉터는 엘리먼트 확장 집합의 형태로 표현하고 있습니다. 그리고 jQuery 메소드와 메소드 체인을 셀렉터(확장 집합)에 적용합니다. 이런 코드 스타일은 jQuery를 적용한 스크립트에서 대표적으로 사용되는 스타일입니다.

예제1

$(this).parents().find("input").val();

개발 할 때 이러한 스타일을 반드시 고수해야 하는 것은 아닙니다. 이와 다른 스타일로도 작성이 가능합니다. 하지만 일반적으로 경험많은 개발자들은 프로젝트를 진행하거나, 협업을 하는 경우 코드의 일관성을 유지하는것이 좋다고 조언합니다. 자신의 개발하는 코드를 일반 자바스크립트 함수가 아닌 jQuery 플러그-인으로 제작하면 사이트 전체에 일관된 코드 스타일을 유지할 수 있습니다. 코드 스타일이 일관된다면 코드에 대한 이해가 쉽고 유지보수에도 용이합니다.

jQuery 플러그-인을 제작하는 두번째 이유는 jQuery가 제공하는 강력한 기능(셀렉터, 메소드)을 활용할 수 있다는 점입니다. 이미 jQuery에는 우리에게 익숙하고, 강력한 도구가 많이 있습니다. 이를 두고 처음부터 다시 코드를 작성할 이유가 없습니다.

결론적으로 jQuery 도구 재사용이 가능한 스크립트 환경에서는 jQuery 플러그-인으로 작성하는 것이 현명한 방법이라 할 수 있습니다.

플러그-인 제작

간단한 jQuery 플러그-인을 제작해 보겠습니다. 자바스크립트 함수를 jQuery Object 인스턴스 프로퍼티로 추가하여 제작했습니다. 그 내용은 예제2와 같습니다.

예제2

$.say = function(what) { alert('I Say ' + what); }

마치 자바스크립트 함수를 선언하고 객체 프로퍼티로 할당하는 것과 같이 쉽고 단순합니다.

[플러그-인에서 '$'별칭 사용]

jQuery를 사용하는 개발자라면 대부분 '$'별칭을 사용합니다. 코드 작성시 편리하게 사용되고 '$'로 작성된 코드가 보다 읽기 편하기 때문입니다. 하지만 플러그-인에서 '$'별칭을 사용할 수 있다고 단정지을 수 없습니다. 완성된 플러그-인이 배포되어 다른 환경에서 사용이 된다고 가정 해보겠습니다. 만약 그 환경이 $.noConflict() 메소드를 사용하여 '$'별칭을 모두 양도한다면 위와 같이 작성된 플러그-인은 작동하지 않습니다.

예제3

jQuery.say = function(what) { alert('I Say ' + what); }  

예제3과 같이 작성한다면 '$'별칭의 충돌을 피할 수 있습니다. 하지만 이미 '$'별칭에 익숙하고 그 장점을 포기할 수 없으면 예제4와 같이 정의합니다.

예제4

(function($) {
    $.say = function(what) { alert('I Say ' + what); }
})(jQuery);

이처럼 '$'를 플러그-인 내부의 지역 변수로 정의하여 사용하면 충돌을 피하면서도 '$'별칭을 사용할수 있습니다. 이렇게 함수를 정의해 바로 사용하는 문법을 즉석 호출 함수 표현식(IIFE, Immediately Invoked Function Expression)이라 합니다. 앞으로 보여드릴 플러그-인 예제는 이러한 형식을 사용하고 있습니다.

[전역함수]

jQuery 전역함수는 실제로 jQuery 객체의 메소드지만, 현실적으로 보면 jQuery 네임스페이스의 함수입니다. 따라서 jQuery 네임 스페이스에 함수를 작성하면 일반 전역함수와의 충돌 가능성은 없습니다. 단 jQuery 메소드와 이름이 충돌하는지의 여부만 신경써주면 됩니다. jQuery 라이브러리에서 제공하는 전역함수 중에는 $.each(), $.map(), $.grep()과 같은 다양한 유틸리티 메소드(utility method)가 있습니다.

간단한 유틸리티 메소드를 직접 제작해 보겠습니다.

예제5

(function ($) {
    $.sum = function(array) {
        var total = 0;
        $.each(array, function(index, value) {
        value = $.trim(value);
        value = parseFloat(value) || 0;

        total += value;
    });
    return tatal;
};
})(jQuery);

(function ($) {
    $.average = function(array) {
        if($.isArray(array)) {
            return $.sum(array) / array.length;
        }
        return '';
    };
})(jQuery);

배열을 받아 총계를 리턴하는 함수와 평균을 리턴하는 함수를 jQuery 객체의 프로퍼티에 추가하여 간단히 플러그-인을 제작했습니다. 이 플러그-인들은 예제6과 같이 사용할 수 있습니다.

예제6

$.sum();
$.average();

이렇게 제작된 전역함수는 jQuery 네임스페이스 내 같은 이름을 갖는 함수와 충돌할 수 있습니다. 이를 예방하기 위해 전역함수를 하나의 객체로 캡슐화(encapsulation) 할 수 있습니다.

예제7

(function ($) {
    $.mathUtils = {
        sum: function(array) {
            var total = 0;
            $.each(array, function(index, value) {
                value = $.trim(value);
                value = parseFloat(value) || 0;

                total += value;
            });
            return tatal;
        },
        average: function(array) {
            if($.isArray(array)) {
                return $.sum(array) / array.length;
            }
            return '';
        };
})(jQuery);

예제7과 같은 패턴은 jQuery.mathUtils 네임스페이스를 생성하고 정의된 함수를 jQuery 객체의 프로퍼티인 mathUtils 객체의 메소드가 됩니다. 이렇게 정의된 플러그인은 예제8과 같이 사용할 수 있습니다.

예제8

$.mathUtils.sum(sum);
$.mathUtils.average(average);

[객체 메소드]

앞에서 jQuery 전역함수를 만들어 보았습니다. 하지만 대부분의 jQuery 내장 기능은 객체 메소드로 제공됩니다. 이번에는 DOM일부를 조작하는 함수인 객체 메소드를 제작해 보겠습니다.

예제9

(function($) {
    $.fn.swapClass = function(class1, class2) {
        if (this.hasClass(class1)) {
            this.removeClass(class1).addClass(class2);
        }
        else if (this.hasClass(class2)) {
            this.removeClass(class2).addClass(class1);
        }
    };
})(jQuery);

jQuery객체를 확장했던 전역함수와는 달리 객체 메소드는 jQuery.fn 객체를 확장하고 있습니다.(jQuery.fn은 jQuery.prototype의 별칭입니다.) 위와 같이 제작한 플러그인은 지정된 DOM 엘리먼트의 class를 변경하는 기능을 제공하고있습니다. 사용하는 방법은 예제10과 같습니다.

예제10

$('div').swapClass('one', 'two');

기존의 <div>엘리먼트의 class가 'one' 이라면 'two' 로 'two' 라면 'one'로 변경이 된다고 예측할 수 있습니다.

[묵시적 반복]

앞에서 소개된 객체 메소드는 DOM 엘리먼트 집합중 최초의

엘리먼트만 적용이 됩니다. 하지만 일반적으로 사용되는 jQuery 함수는 jQuery 선택자 표현식이 0개 또는 1개 이상의 요소와 매칭이 됩니다. 즉, jQuery 함수는 다수의 엘리먼트에 함수의 기능이 적용되어야 합니다. 따라서 플러그-인을 설계할 때 jQuery 선택자가 복수인 경우를 모두 감안해야 합니다. .each() 메소드를 이용하여 묵시적인 반복(implicit iteration)을 수행하도록 플러그-인을 수정하겠습니다.

예제11

(function($) {
    $.fn.swapClass = function(class1, class2) {
        this.each(function() {
            var $element = $(this);
            if ($element.hasClass(class1)) {
                $element.removeClass(class1).addClass(class2);
            }
            else if ($element.hasClass(class2)) {
                $element.removeClass(class2).addClass(class1);
            }
        });
    };
})(jQuery);

예제11에서 this 키워드의 의미에 주의해야 합니다. swapClass 메소드 안에서의 this는 jQuery 객체를 의미하고 .each() 메소드의 콜백함수에 사용된 this 키워드는 DOM 요소를 의미합니다. 이렇게 구현된 플러그-인은 DOM내의 모든 <div> 엘리먼트에 swapClass() 메소드를 적용합니다.

[메소드 체인]

앞에서'묵시적 반복' 규칙을 준수한 예제11에서도 부족함이 보입니다. 이 플러그-인을 사용할 경우 선택자에 swapClass()를 적용한 후 다른 메소드를 적용하려면 다시 선택자에 해당 메소드를 호출하는 새로운 문장을 작성해야만 합니다. 그러나 예제1과 같이 일반적으로 jQuery 메소드는 메소드 체인을 사용하고 있습니다. 다른 메소드들과 함께 자요롭게 체인을 사용하게 되면 코드를 간결하게 만드는데에도 도움이 됩니다. 메소드 체인이 가능하도록 다음과 같은 규칙을 추가하겠습니다.

  • 특정한 값을 리턴할 의도가 없다면 선택 요소를 리턴한다

위의 규칙을 적용하여 메소드 체인을 사용할 수 있도록 수정하겠습니다. 그 결과는 예제12와 같습니다.

예제12

(function($) {
    $.fn.swapClass = function(class1, class2) {
        return this.each(function() {
            var $element = $(this);
            if ($element.hasClass(class1)) {
                $element.removeClass(class1).addClass(class2);
            }
            else if ($element.hasClass(class2)) {
                $element.removeClass(class2).addClass(class1);
            }
        });
    };
})(jQuery);

메소드 체인은 위와 같이 DOM요소를 리턴하는 것만으로도 간단히 구현됩니다. 묵시적 반복과 메소드 체인과 같은 규칙을 준수함으로서 기존 jQuery 메소들과의 일관성을 유지할 수 있고 플러그-인 개발자 외 다른 개발자들이 쉽게 이해하고 사용할 수 있습니다.

[파일명 짓기]

제작된 플러그-인을 파일로 저장시 파일이름 또한 다른 파일명과의 충돌을 고려해야 합니다. 아래 규칙은 jQuery팀에서 추천하는 명명규칙입니다.

접두어로 jQuery.를 사용한다. 이어서 플러그-인 이름을 적는다. 선택적으로 플러그-인의 메이저와 마이너 버전 값을 적는다. .js로 파일 이름을 끝맺는다. 제작된 플러그-인의 이름이 'nextree'라면 jquery.nextree-1.0.js 와 같은 파일명을 적용하면 되겠습니다.

마치며..

jQuery 플러그-인을 제작하는 것은 자바스크립트 일반 코드를 작성하는 것과 크게 다르지 않습니다. jQuery 환경의 자바스크립트 코딩에 익숙한 개발자라면 어려움없이 플러그-인을 제작할 것입니다. 자신뿐 아니라 동료 개발자들에게도 유용한 플러그-인을 개발한다면 개인의 발전을 넘어 프로젝트에 기여할 것입니다. 감사합니다.

참고문헌

  • Learning jQuery 1.7 Jonathan Chaffer
  • jQuery in Action 2/E Bear Bibeault and Yehuda Katz

참고사이트

Nextree

Read more posts by this author.