jQuery로 작업하기, Part 3: 매개체로서의 JQuery: 고유한 플러그인 작성하기

Michael Abernethy, Product Development Manager, Optimal Auctions

요약: jQuery를 사용하면 고유한 플러그인을 작성하여 jQuery의 기능을 확장할 수 있을 뿐만 아니라 jQuery 커뮤니티와 함께 공유할 수도 있습니다. 이 기사에서는 고유한 플러그인을 작성한 후 jQuery 플러그인 커뮤니티 웹 페이지에 게시하는 과정을 단계별로 안내합니다.

이 연재 자세히 보기

원문 게재일:  2009 년 5 월 26 일 번역 게재일:   2009 년 7 월 21 일
난이도:  중급 영어로:  보기 PDF:  A4 and Letter (89KB | 17 pages)Get Adobe® Reader®
페이지뷰: 251 회
의견: 0 (의견 추가)

1 star2 stars3 stars4 stars5 stars 평균 평가 등급 (총 0표)

소개

이 시리즈의 이전 기사인 jQuery로 작업하기, Part 2: 매개체로서의 JQuery: UI 프로젝트에서는 사용자의 jQuery 코드에서 플러그인을 사용하여 웹 애플리케이션의 효과를 높이는 과정을 살펴보았다. 하지만 이러한 플러그인은 사용자가 직접 작성한 것이 아니다. 필자와 같은 개발자가 jQuery 커뮤니티의 발전을 위해 자신의 소중한 시간을 투자하여 작성하고 테스트한 것이다. 그리고 이러한 결과는 모두 jQuery에 대한 개발자의 애정을 양분 삼아 아무런 대가 없이 이루어진 것이다. 이 기사에서는 이 훌륭한 커뮤니티에 일조할 수 있는 방법을 사용자에게 알려 주기 위해 사용자 자신의 플러그인을 작성한 후 jQuery에서 호스팅하는 플러그인 페이지에 게시하는 과정을 설명한다. 이렇게 하면 사용자가 작성한 플러그인을 모두가 사용할 수 있으며 더 나아가 전체 jQuery 개발 커뮤니티의 발전도 앞당길 수 있다. 이 기사 또한 jQuery 커뮤니티를 위한 필자의 헌신 중 하나이다.

이 기사에서 설명할 플러그인을 작성하는 중 필자는 플러그인을 작성하는 과정과 작성 프레임워크가 매우 쉽고 단순하다는 것을 알게 되었다. 실제로 어려운 부분은 다른 사람들이 아직까지 실현하지 않은 무엇인가를 생각해 내서 그 작업을 수행하는 "까다롭고 힘든" JavaScript 코드를 작성하는 것이다. 플러그인 구조가 단순하여 입문자는 쉽다는 느낌을 받고 전문 코더는 유연하다는 느낌을 받기 때문에 플러그인의 수가 빠르게 증가하고 있다.

이 기사를 위한 연구 조사를 수행하던 중에 필자는 모든 작성자가 각자의 고유한 스타일로 플러그인을 작성하고 있을 뿐 아니라 jQuery에서 여러 다양한 스타일로 플러그인을 작성할 수 있다는 것도 알게 되었다. 이 기사에서는 jQuery에서 권장하는 스타일이기도 하면서 가장 쉬운 스타일을 위주로 하여 설명하겠지만 필요에 따라 몇 가지 차이점이나 옵션에 대해서도 설명한다.

플러그인 설명하기

플러그인을 작성하는 과정에서 첫 번째 단계는 좋은 아이디어를 찾는 것이다. 대부분의 좋은 아이디어가 그렇듯이 다른 사람에 의해 이미 구현되어 있기 때문에 정작 우리 자신이 구현할 수 있는 기회를 잡지 못하는 경우가 많다. 이 기사에서 필자가 개발하고 있는 플러그인의 경우에는 새롭지는 않지만 이 기사를 집필하던 당시에 jQuery 플러그인 커뮤니티에서 찾을 수 없었던 개념이었다. 필자는 개인적으로 매우 유용한 플러그인이라고 생각한다.

필자가 개발한 플러그인은 NumberFormatter 플러그인이다. 숫자 형식 지정 기능은 Java™ 또는 PHP와 같은 서버측 코드 및 국제화 관련 작업을 처리하는 작업자라면 누구나 익숙한 기능이다. 모든 사람이 동일한 방법으로 숫자의 형식을 지정하지는 않는다는 것은 모두가 알고 있는 상식이다. 예를 들어, 어떤 사람은 거리에 마일이라는 단위를 사용한다. 미국에서는 "1,250,500.75"(필자의 세금 고지서에 적혀 있던 숫자)라는 형식으로 숫자를 쓰지만 독일에서는 "1.250.500,75", 프랑스에서는 "1 250 500,75", 스위스에서는 "1'250'500.75", 일본에서는 "125,0500.75"와 같이 나라마다 고유한 형식으로 숫자를 쓰고 있다. 숫자 자체는 동일하지만 웹 애플리케이션에서 사용자에게 표시될 때는 각기 다른 형식으로 작성되는 것이다.

그렇다면 국제화된 애플리케이션을 작성할 때 다양한 국가의 사용자에게 숫자를 표시하려면 어떻게 해야 하는지에 대한 질문이 발생하기 마련이다. 물론 그 해결 방법은 매우 일반적인 방법인 서버측 형식 지정 기능을 사용하는 것이다. Java에는 숫자의 형식을 쉽게 지정할 수 있는 강력한 형식 지정 라이브러리가 있다. 페이지를 숫자와 함께 서버에 설정하면 서버에서 이러한 숫자의 형식을 지정할 수 있다. 하지만 서버를 사용할 수 없는 경우도 많기 때문에 서버에게 의존하지 않고 클라이언트에서 직접 숫자의 형식을 지정할 수 있는 방법이 필요하다.

이 기사에서 설명하고 있는 형식 지정 기능의 일반적인 사용 사례를 살펴보자. 웹 애플리케이션에 사용자의 임금을 묻는 입력 필드가 있다. 미국 사용자의 경우 "$65000", "65,000", "65000", "65,000.00" 등의 다양한 형식으로 값을 입력할 수 있다. 이들 숫자는 모두 같은 값을 의미한다. 하지만 사용자에게 일관된 환경을 제공하기 위해 화면에 표시되는 숫자의 형식을 제어하려는 경우 숫자가 입력된 후 서버를 호출할 수도 있다. 하지만 이렇게 하면 다양한 형식을 사용하는 숫자 필드가 많이 있을 경우 매우 번거로운 작업이 될 것이다. 게다가 이 문제를 클라이언트에서 처리함으로써 사용자에게 피드백을 바로 줄 수 있다면 이 방법을 따르는 것이 효과적일 것이다.

이제 이 기사에서는 JavaScript/jQuery 기능의 부족한 부분을 보완하는 플러그인을 작성해 볼 것이다. 이 플러그인은 클라이언트에서 숫자의 형식을 지정하는 기능을 제공하므로 이 플러그인을 사용하는 개발자는 웹 애플리케이션에서 서버와 통신하지 않고도 국제화를 지원할 수 있다. 부가적으로 이 플러그인은 반대의 기능도 수행한다. 즉, 숫자를 구문 분석하여 형식이 지정된 텍스트 문자열에서 숫자를 가져올 수 있다. 이 기능은 클라이언트에서 수학 연산을 수행할 때 사용할 수 있다. 또한 숫자 형식 지정을 수행하는 클라이언트 코드와 표준 서버측 메소드 사이의 공통성을 유지하기 위해 Java DecimalFormatter 클래스의 기능을 모방한다.

단계 1 결과: 플러그인에 대한 요구를 설정한 후 이 요구를 충족하는 데 사용할 스펙을 정의했다.

플러그인 규칙

jQuery 팀에서는 공통으로 기대되는 환경을 플러그인 사용자에게 제공하기 위해 플러그인 작성자가 따르기를 바라는 수많은 일반 규칙을 정해 놓았다. 필자의 입장에서 동의할 수 없는 규칙도 있기는 하지만 jQuery 팀이 필자 개인보다는 훨씬 현명할 것이라고 생각하기 때문에 여기에서는 jQuery 팀에서 제안하는 규칙을 소개한 후 이 기사의 플러그인을 작성하는 모든 단계에서도 이러한 규칙을 따를 것이다.

  • 파일의 이름을 "jquery.<your plug-in name>.js" 형식으로 지정한다.
    파일 이름을 보고 jQuery 플러그인이라는 점과 어떤 플러그인인지를 바로 알 수 있기 때문에 효과적이다.

    이 기사의 플러그인에는 "jquery.numberformatter.js"라는 이름을 지정한다.

  • 새 메소드는 모두 jQuery.fn 오브젝트에 연결하고, 새 함수는 모두 jQuery 오브젝트에 연결한다.
    이 단계에서는 이해하기 어려운 규칙이지만 실제 코딩 작업에서는 가장 중요한 규칙이므로 다음 섹션에서 자세히 설명한다.

    이 기사에서는 메소드/함수를 두 해당 오브젝트에만 연결한다.

  • "this"는 jQuery 오브젝트에 대한 참조이다.
    이 방법을 사용하면 모든 플러그인 작성자가 "this"를 참조할 때 jQuery에서 어느 오브젝트가 리턴될지를 알 수 있으므로 플러그인을 효율적으로 작성할 수 있다.

    이 기사의 플러그인에서는 "this"를 jQuery 오브젝트에 대한 참조로만 사용한다.

  • 플러그인에 정의된 모든 메소드/함수는 ";"(세미콜론)으로 끝나야 하며 그렇지 않으면 코드 미니마이저가 중단된다.
    이 규칙은 JavaScript 파일을 최소화할 수 있는 최적의 방법이므로 미니마이저가 중단되면 플러그인이 빠르게 중단될 수 있다.

    모든 메소드/함수가 ";"으로 끝나도록 작성한다.

  • 따로 언급하지 않는 한 모든 메소드는 jQuery 오브젝트를 리턴해야 한다.
    jQuery 메소드의 데이지 체인은 매우 유명하다. 만일 이 체인을 끊는 플러그인을 작성하면 글자 그대로 "체인이 끊어진다".

    이 플러그인의 format() 메소드는 parse() 메소드가 jQuery 오브젝트를 리턴하지 않더라도 jQuery 오브젝트를 리턴한다. 필자는 이 함수로 인해 체인이 끊어지는 많은 부분에서 이 내용을 언급해 두었다. 따라서 체인을 끊지 않으면서 Number 오브젝트를 리턴하기는 어렵거나 불가능할 것이다.

  • 일치하는 요소를 반복하려면 항상 this.each()를 사용해야 한다. 왜냐하면 이 방법이 안정적이면서도 효율적으로 오브젝트를 반복할 수 있는 방법이기 때문이다.
    성능과 안정성을 위해 일치하는 요소를 반복하려는 모든 메소드에서는 이 방법을 사용하는 것이 좋다.

    이 플러그인의 메소드에서는 이 방법만을 사용해서 일치하는 요소를 반복한다.

  • 플러그인 코드에서 "$" 대신 항상 "jQuery"를 사용한다.
    이 규칙을 따르면 "$"로 인한 충돌이 발생하는 사용자(다른 JavaScript 라이브러리를 사용하는 사용자)가 "var JQ = jQuery.noConflict();" 함수를 사용하여 jQuery에 대한 별칭을 변경할 수 있기 때문에 좋은 효과를 기대할 수 있다. 하지만 필자가 여러 플러그인을 살펴본 결과, 아쉽게도 이 규칙이 잘 지켜지지 않고 있다는 사실을 알게 되었다. 개발자가 jQuery 별칭을 변경해야 한다는 것은 끊어진 플러그인이 중단될 것이라는 것을 의미한다.

    이 플러그인에서는 jQuery만을 사용하고 jQuery의 "$" 별칭은 사용하지 않는다.

여기까지가 플러그인 코드에서 따라야 하는 규칙/권장 사항이다. 실질적으로 이러한 규칙은 사실 상의 강제 규정에 가깝다. 왜냐하면 이러한 규칙을 따르지 않는 플러그인은 많이 사용되지도 않을 뿐만 아니라 동료들로부터의 평가도 좋지 못할 것이기 때문이다. 플러그인이 거의 사용되지 않는다면 소중한 시간을 낭비하게 되는 결과만 낳을 것이다. 따라서 규칙을 따르는 것이 중요하다. 이렇게 규칙을 따르면 동료들이 플러그인을 쉽게 이해할 수 있고 코드의 일관성도 유지할 수 있을 뿐만 아니라 플러그인의 성공 가능성을 높여 주기도 한다.

단계 2 결과: jQuery 플러그인 작성과 관련된 모든 규칙을 따른다.

플러그인 작성하기

이제 코드를 작성할 시간이다. 플러그인을 작성하려면 가장 먼저 플러그인의 구조를 결정해야 한다. 이 경우 플러그인을 메소드로 만들 것인지 아니면 함수로 만들 것인지 선택해야 한다. 아마도 그 차이가 무엇인지 궁금할 것이다.

앞에서 언급한 것처럼 메소드는 jQuery.fn 오브젝트에 연결해야 하고 함수는 jQuery 오브젝트에 연결해야 한다. 이제 모든 것이 명확해졌을 것이다. 그리고 jQuery에 어느 정도 경험이 있는 개발자라면 다음과 같이 생각해 볼 수도 있을 것이다. 메소드를 사용하면 플러그인에 전달되는 특정 요소 전체를 반복할 수 있다. 결과적으로 플러그인에서 모든 유형의 HTML 요소를 받아들일 수 있기 때문에 각 요소에 대한 작업 방법을 플러그인에서 정의해야 한다. 이제 플러그인 메소드가 "p"와 "#mySpecificPageElement"를 포함한 모든 유형의 jQuery 선택기를 받아들일 수 있다. 이 기능은 유형의 제한 없이 페이지 요소를 전달할 수 있는 유연한 플러그인을 작성하려는 경우에 유용하다. 이처럼 메소드 형식의 플러그인에서는 플러그인 개발자가 모든 기능을 적절히 처리해야 한다. 메소드와는 달리 함수는 특정 요소를 인수로 받지 않는다. 함수는 전체 페이지에 적용되는 기능이다. 이 경우 플러그인 개발자는 플러그인과 상호 작용할 페이지 요소를 정의해야 하고 나머지 요소는 무시한다. 이제 코드를 통해 차이점을 살펴보자.


Listing 1. jQuery 플러그인 메소드/함수
		   
// This is a method because you can pass any type of selector to the method and it 
// will take some action on the results of the selector.  In this case, it will
// doSomething() on the page element with an ID of myExample
$("#myExample").doSomething();
   
// This is also a method, even though you are passing the entire page body to
// the method, because you are still passing a selector
$("body").doSomethingElse();

// This is a function, because you are NOT passing any selector to the function
// The plug-in developer must determine what page elements they want to take action on.
// This is usually accomplished by the plug-in developer requiring the page elements
// to contain a certain class name.

<div class="anotherThing">

// This hypothetical plug-in developer would document that his plug-in only works
// on elements with the class "anotherThing"
$.anotherThing();

지금까지의 설명을 바탕으로 생각해 보면 사용자가 형식을 지정할 페이지 요소를 지정해야 하므로 이 플러그인에서는 메소드를 사용해야 한다. Listing 2에서는 지금까지 논의된 내용을 바탕으로 구현한 플러그인 코드를 보여 준다.


Listing 2. 메소드 정의
		   
jQuery.fn.format = function();

// You would call your plug-in like this (at this point)
$("#myText").format();

국제화를 처리하고 있는 중이고 형식을 지정할 텍스트의 언어와 형식을 자동으로 파악할 수 없기 때문에 함수가 모든 것을 처리하는 만능 플러그인이 될 수는 없다. 따라서 몇 가지 옵션을 받아들일 수 있도록 플러그인을 약간 수정해야 한다. 이 기사에서는 형식 지정 메소드에 두 가지 옵션이 필요하다. 하나는 숫자가 사용해야 하는 형식(예: #,### 또는 #,###.00)이고 다른 하나는 로케일(사용할 국제 숫자 형식을 결정하는 간단한 2자리 국가 코드)이다.

또한 플러그인의 성공 가능성을 높이고 싶으므로 플러그인을 최대한 사용하기 쉽게 작성하려고 한다. 이는 사용자가 원하지 않을 경우 옵션을 전달할 필요가 없도록 하기 위해 몇 가지 기본 옵션을 미리 정의해야 한다는 뜻이다. 필자가 지금 미국에 살고 있을 뿐만 아니라 세계에서 가장 일반적으로 사용되는 숫자 형식이기도 하기 때문에 여기에서는 "us" 로케일과 "#,###.00" 형식을 기본값으로 사용하며 환율의 경우에도 특별히 다른 형식을 사용할 이유가 없으므로 이 기본값을 사용한다.


Listing 3. 플러그인에서 옵션 허용하기
		   
jQuery.fn.format = function(options) {

    // the jQuery.extend function takes an unlimited number of arguments, and each
    // successive argument can overwrite the values of the previous ones.
    // This setup is beneficial for defining default values, because you define
    // them first, and then use the options passed into the method as the
    // second argument.  This allows the user to override any default values with their
    // own in an easy-to-use setup.
    var options = jQuery.extend( {

      format: "#,###.00",
      locale: "us"

}, options);

플러그인 프레임워크를 작성하는 마지막 단계는 메소드에 전달된 특정 요소를 올바르게 처리하는 것이다. 위 예제를 다시 한번 보면 특정 요소는 단일 페이지 요소 또는 다수 페이지 요소일 수 있다. 이들 모두를 올바르게 처리해야 한다. 또한 jQuery 플러그인 규칙에서 언급했듯이 "this" 오브젝트는 jQuery 오브젝트에 대한 참조이다. 그러므로 메소드로 전달된 jQuery 특정 요소에 대한 참조를 사용해서 이러한 요소를 반복해야 한다. 또한 규칙을 다시 한번 보면 각 플러그인 메소드는 jQuery 오브젝트를 리턴해야 한다. 물론 jQuery 오브젝트가 "this"이기 때문에 메소드에서 이 오브젝트를 큰 무리 없이 쉽게 리턴할 수 있다. 이제 각 특정 요소를 반복하고 jQuery 오브젝트를 리턴하는 두 가지 작업을 코드로 구현하는 방법을 살펴보자.


Listing 4. jQuery 오브젝트 작업하기
		    
jQuery.fn.format = function(options) {

    var options = jQuery.extend( {

      format: "#,###.00",
      locale: "us"

    }, options);

// this code snippet will loop through the selected elements and return the jQuery object 
// when complete
return this.each(function(){
  // inside each iteration, you can reference the current element by using the standard
  // jQuery(this) notation
  // the rest of the plug-in code goes here
  });

실제 플러그인 코드 자체는 이 기사의 범위를 벗어나는 내용이므로 여기에서는 자세히 다루지 않는다. 하지만 이 기사에 첨부된 플러그인 코드에서 모든 내용을 볼 수 있다(다운로드 참조). 또한 메소드 대신 함수를 작성하기로 결정한 경우에 플러그인 아키텍처를 설정하는 방법을 보여 주는 예제도 볼 수 있다.


Listing 5. 함수를 사용하는 예제 플러그인
		   
jQuery.exampleFunction = function(options) {

   var options = jQuery.extend( {

     // your defaults

    }, options);
    
    jQuery(".exampleSelector").each(function(){
    
    });

});

플러그인 미세 조정하기

웹에 올려져 있는 초보 수준의 플러그인 기사에서는 대부분 기본 플러그인 형식을 설정한 후 실행해 보는 단계까지만 다루고 있다. 하지만 기본 프레임워크는 말 그대로 기본에 불과하다. 이 외에도 고유한 플러그인을 작성할 때 고려해야 하는 중요한 내용이 더 있다. 이러한 내용은 초보 수준의 플러그인을 넘어서는 우수한 플러그인을 작성하는 데 필요한 고급 기능을 제공한다. 앞으로 설명할 두 단계에서는 초보 수준의 플러그인을 중급 수준의 플러그인으로 변모시키는 과정을 보여 준다.

미세 조정 #1 - 내부 메소드를 private 메소드로 설정하기

오브젝트 지향 프로그래밍 언어에서는 반복 코드를 실행할 수 있는 외부 함수를 쉽게 작성할 수 있다. 필자가 작성한 NumberFormatter 플러그인에서 그 예를 찾아보면 함수에 전달된 로케일과 소수점 및 그룹 구분 기호로 사용할 문자를 결정하는 코드가 있다. 이 코드는 format() 메소드 및 parse() 메소드에 필요하며 초보 프로그래머도 자신의 메소드에서 이 코드를 사용하고 있다고 말할 수 있다. 하지만 여기에서는 jQuery 플러그인을 작업하고 있기 때문에 한 가지 문제가 대두된다. 즉, JavaScript로 정의된 고유한 함수를 작성한 경우에는 해당 스크립트를 사용하는 모든 사람이 임의의 목적으로 메소드를 호출할 수 있게 된다. 하지만 이러한 상황은 함수를 사용하는 의도와 맞지 않다. 함수는 내부 작업에만 사용하기 위한 것이므로 외부자가 함수를 호출하지 못하도록 해야 한다. 이제 함수를 private 함수로 작성하는 방법을 살펴보자.

이 private 메소드 문제를 해결할 수 있는 방법으로 클로저가 있다. 이 방법은 개발자가 jQuery 오브젝트(public 오브젝트)에 연결한 호출을 제외한 다른 모든 외부 호출이 액세스할 수 없도록 전체 플러그인 코드를 닫는다. 이 설계를 사용하면 외부 스크립트에 의해 호출될 것을 걱정할 필요 없이 플러그인 내에 원하는 코드를 자유롭게 넣을 수 있다. jQuery 오브젝트에 연결된 플러그인 메소드는 기본적으로 public 메소드가 되며 나머지 모든 함수/클래스는 private가 된다. Listing 6에서는 이를 수행하는 데 필요한 코드를 보여 준다.


Listing 6. private 함수 작성하기
		   
// this code creates the Closure structure
(function(jQuery) {

   // this function is "private"
   function formatCodes(locale) {
      // plug-in specific code here
   };  // don't forget the semi-colon

   // this method is "public" because it's attached to the jQuery object
    jQuery.fn.format = function(options) {

     var options = jQuery.extend( {

          format: "#,###.00",
          locale: "us"

     },options);

     return this.each(function(){
         var text = new String(jQuery(this).text());
         if (jQuery(this).is(":input"))
             text = new String(jQuery(this).val());

         // you can call the private function like any other function
         var formatData = formatCodes(options.locale.toLowerCase());

         // plug-in-specific code  here
      });
     }; // don't forget the semi-colon to close the method

// this code ends the Closure structure
})(jQuery);

미세 조정 #2 - 재정의 가능한 플러그인 기본값 설정하기

이 플러그인을 미세 조정하는 마지막 단계는 플러그인의 기본값을 재정의할 수 있는 기능을 제공하는 것이다. 독일에 있는 개발자가 이 플러그인을 다운로드한 후 그의 모든 웹 애플리케이션 사용자가 독일 로케일로 보기를 원한다고 가정해 보자. 이 경우에는 개발자에게 모든 메소드 호출에 독일 로케일을 작성하도록 요청하기 보다는 코드의 한 행을 수정하여 기본 로케일을 변경할 수 있는 기능을 제공하는 것이 현명한 방법일 것이다. 특히 웹 애플리케이션에서 사용자에게 다양한 국제 형식으로 숫자를 표현하는 경우가 매우 드물기 때문에 이 기사의 플러그인에서는 이 기능이 매우 유용하다. 지금 보고 있는 웹 페이지에서 모든 숫자가 동일한 로케일을 사용하여 형식 지정되고 있다는 사실을 확인할 수 있을 것이다. 이것이 바로 기회이다.

지금까지 살펴본 내용에 이 마지막 꾸미기 단계를 반영하기 위해 이 단계에서는 코드를 약간 수정해야 한다.


Listing 7. 재정의 가능한 기본값
		   

jQuery.fn.format = function(options) {
// Change how you load your options in to take advantage of your overridable defaults
// You change how your extend() function works, because the defaults
// are globally defined, rather than within the method.  If you didn't use the
// {} as the first argument, you'd copy the options passed in over the defaults, which is
// undesirable.  This {} creates a new temporary object to store the options
// You can simply call the defaults as an object within your plug-in
var options = jQuery.extend({},jQuery.fn.format.defaults, options);

   return this.each(function(){
   
   // rest of the plug-in code here

// define the defaults here as an object in the plug-in
 jQuery.fn.format.defaults = {
      format: "#,###.00",
      locale: "us"
      }; // don't forget the semi-colon

여기까지가 플러그인 작성의 최종 단계이다. 드디어 고급 기능을 갖춘 플러그인을 모두 완료하고 마지막으로 테스트 단계만 남겨두었다. Listing 8에서는 이 기사에서 작성한 플러그인 코드가 어떻게 작동하는지 한눈에 알아볼 수 있는 완성된 플러그인을 보여 준다. 또한 아직까지 설명하지 않았지만 플러그인에는 포함되어 있는 parse() 함수도 포함되어 있다. (플러그인 작업 중 까다롭고 어려운 부분인 형식 지정을 처리하는 부분은 이 기사의 범위에 포함되지 않기 때문에 제외했다. 하지만 예제와 플러그인 자체에는 포함되어 있다.)


Listing 8. NumberFormatter 플러그인
		   
(function(jQuery) {

    function FormatData(valid, dec, group, neg) {
       this.valid = valid;
       this.dec = dec;
       this.group = group;
       this.neg = neg;
    };

    function formatCodes(locale) {
      // format logic goes here
      return new FormatData(valid, dec, group, neg);
    };

 jQuery.fn.parse = function(options) {

    var options = jQuery.extend({},jQuery.fn.parse.defaults, options);

    var formatData = formatCodes(options.locale.toLowerCase());

    var valid = formatData.valid;
    var dec = formatData.dec;
    var group = formatData.group;
    var neg = formatData.neg;

    var array = [];
    this.each(function(){

       var text = new String(jQuery(this).text());
       if (jQuery(this).is(":input"))
           text = new String(jQuery(this).val());


       // now we need to convert it into a number
       var number = new Number(text.replace(group,'').replace(dec,".").replace(neg,"-"));
       array.push(number);
     });

     return array;
 };

 jQuery.fn.format = function(options) {

    var options = jQuery.extend({},jQuery.fn.format.defaults, options);  

    var formatData = formatCodes(options.locale.toLowerCase());

    var valid = formatData.valid;
    var dec = formatData.dec;
    var group = formatData.group;
    var neg = formatData.neg;

    return this.each(function(){
       var text = new String(jQuery(this).text());
       if (jQuery(this).is(":input"))
           text = new String(jQuery(this).val());

       // formatting logic goes here

       if (jQuery(this).is(":input"))
           jQuery(this).val(returnString);
       else
           jQuery(this).text(returnString);
    });
 };
 
 jQuery.fn.parse.defaults = {
    locale: "us"
 };

 jQuery.fn.format.defaults = {
    format: "#,###.00",
    locale: "us"
 };
 
 })(jQuery);

플러그인 테스트하기

플러그인을 작성하는 최종 단계는 플러그인을 전체적으로 테스트하는 것이다. 플러그인 버그를 보는 것보다 플러그인 사용자를 화나게 만드는 것은 없다. 사용자들은 버그를 고치기 보다는 그 즉시 플러그인을 종료하고 다시는 사용하지 않을 것이다. 이러한 사용자가 몇 명만 발생해도 그리고 나쁜 내용의 리뷰가 몇 건만 올라와도 플러그인에 대한 평판이 급속도로 떨어질 수 있다. 하지만 우리 자신도 정상적으로 테스트된 플러그인을 사용하기를 원한다는 점을 생각해 보면 이들의 행위도 받아들일 수 있어야 할 뿐만 아니라 플러그인 코드를 작성할 때 더욱 심혈을 기울여야 할 것이다.

이 기사에서는 플러그인을 테스트하기 위해 단위 테스트 라이러브리가 필요 없는 간단한 테스트 구조를 작성했다. 이 구조는 다양한 숫자와 숫자 뒤에 올바른 형식이 있는 수십 개의 요소를 작성한다. 이 JavaScript 테스트는 숫자에 대한 형식을 호출한 후 형식이 지정된 숫자를 원하는 결과와 비교하여 두 값이 서로 일치하지 않으면 빨간색으로 표시한다. 이 간단한 테스트를 통해 필자는 가능한 모든 형식을 테스트하는 수십 가지의 다양한 테스트 케이스를 설정할 수 있다. jQuery를 활용하여 테스트를 수행하는 테스트 페이지를 예제 다운로드에 첨부해 두었으므로 이를 통해 자신의 플러그인을 직접 테스트해 볼 수 있다.

완료된 플러그인 살펴보기

작동 중인 새 NumberFormatter를 살펴보자. NumberFormatter 플러그인을 웹 애플리케이션에 적용하는 방법을 보여 주기 위해 간단한 웹 애플리케이션을 작성해 보았다.


그림 1. 작동 중인 NumberFormatter
숫자 필드가 있는 일련의 레이블을 보여 주는 스크린샷. 각 숫자 필드에는 다양한 형식이 올바르게 지정된 숫자가 표시되어 있다.

이 웹 애플리케이션은 매우 단순하고 직관적이다. 사용자가 임금, 주거 및 자녀 정보를 입력한 후 입력란을 떠나면 NumberFormatter 플러그인에 의해 사용자 입력이 적합한 형식으로 변경된다. 웹 애플리케이션에서 이 플러그인을 사용하면 사용자가 입력한 숫자의 형식을 일관되게 지정할 수 있다. 또한 이 웹 애플리케이션은 독일 사용자에 맞춰서 형식이 지정되므로 미국 사용자의 경우 소수점 및 그룹 문자가 익숙하지 않을 것이다. 이 문제를 해결하기 위해 뒤에서 기본값을 변경하는 방법을 설명한다.


Listing 9. 작동 중인 NumberFormatter
		   
$(document).ready(function() {

   // use the AlphaNumeric plug-in to limit the input
   $(".decimal").decimal();
   $(".numeric").numeric();

   // you want to change the defaults to use the German locale
   // this will change the default for every method call on the
   // entire page, so you won't have to pass in the "locale"
   // argument to any function
   $.fn.format.defaults.locale = "de";
   $.fn.parse.defaults.locale = "de";

   // when the salary field loses focus, format it properly
   $("#salary").blur(function(){
      $(this).format({format:"#,###.00"});
   });

   // when the house field loses focus, format it properly
   $("#houseWorth").blur(function(){
      $(this).format({format:"#,###"});
   });
   
   // when the kids field loses focus, format it properly
   $("#kids").blur(function(){
      $(this).format({format:"#"});
   });
   
   // calculate the tax
   $("#calculate").click(function(){
      // parse all the numbers from the fields
      var salary = $("#salary").parse();
      var house = $("#houseWorth").parse();
      var kids = $("#kids").parse();
      // make some imaginary tax formula
      var tax = Math.max(0,(0.22*salary) + (0.03*house) - (4000*kids));
      // place the result in the tax field, and then format the resulting number
      // you need one intermediate step though, and that's the "formatNumber" function
      // because all numbers in JavaScript use a US locale when made into a String
      // you need to convert this Number into a German locale String before
      // calling format on it.
      // So, the steps are:
      //   1) the tax is a Number that looks like 9200.54 (US locale)
      //   2) formatNumber converts this to a String of 9200,54 (German locale)
      //   3) put this String in the #tax field
      //   4) Call format() on this field
      $("#tax").text($.formatNumber(tax)).format({format:"#,###"});
   });

});

NumberFormatter 플러그인에 대한 설명을 마무리하기 전에 몇 가지 언급해야 할 내용이 남아 있다. 무엇보다도 이 플러그인의 릴리스는 최초의 1.0.0이므로 앞으로 확장되면서 Java DecimalFormatter에 있는 추가 형식 지정 기능까지도 포함하게 될 것이다. 이 릴리스에는 환율, 과학적 기수법 및 백분율에 대한 지원이 포함되어 있다. 앞으로는 간단히 "-"만을 사용하여 음수를 표현하는 방법이 아닌 양수 및 음수에 대한 별도의 형식 지정 규칙도 포함될 것이다. 예를 들어 회계 분야에서는 (5,000)이라는 형식으로 음수를 표현한다. 마지막으로 좋은 형식 지정기는 형식을 따르는 모든 문자를 허용하면서 예약된 문자의 일부가 아닌 문자만을 무시한다. 필자는 이러한 기능을 모두 추가하여 근시일 내에 강력한 플러그인을 작성할 계획이다.

사용자 로케일 가져오기

jQuery 플러그인과는 관련이 없지만 이 플러그인을 사용하는 중에 발생할 수 있는 한 가지 질문이 있다. 이 질문은 바로 사용자의 로케일을 어떻게 가져올 수 있는가라는 것이다. 현재까지는 JavaScript를 사용하여 이 정보를 가져올 수 있는 방법이 없기 때문에 좋은 질문이다. JavaScript Bridge를 작성해서 이 작업을 수행해야 한다. JavaScript Bridge란 무엇인가? 간단히 말하자면 서버측 코드에서 JavaScript 코드로 값을 가져오는 간단한 설계 패턴을 설정할 수 있다는 뜻이다. Listing 10에서는 Java를 사용하는 방법 즉, JSP 페이지에 이 작업을 수행하는 방법을 보여 준다.


Listing 10. 사용자 로케일 가져오기
		   
<%

    // the request object is built into JSPs
    // unfortunately, it's not any easier
    // tested on FF, IE, Safari, Chrome
    String locale = "us"; // or your default locale
    String accLang = request.getHeader("Accept-Language");
    if (accLang.length() > 5)
    {
       accLang = accLang.substring(0,5);
       locale = accLang.substring(accLang.indexOf("-")+1);
    }

%>

$(document).ready(function() {

   // take advantage of the ability to override defaults by using the JavaScript
   // Bridge here.  Then your page can use the format() and parse() functions
   // elsewhere in the page without modifying them for a user's locale.
   $.fn.format.defaults.locale = "<%=locale%>";
   $.fn.parse.defaults.locale = "<%=locale%>";
   
   });

플러그인 공유하기

이제 플러그인 작성과 테스트를 모두 완료했다. 마지막 단계로 플러그인을 다른 사람들과 공유하기 위해 jQuery 웹 사이트의 플러그인 저장소에 올리는 작업만 남았다.

  • jQuery 웹 사이트의 플러그인 페이지의 왼쪽 탐색 메뉴에서 Login/RegisterCreate New Account를 차례로 클릭한다. 이미 계정을 가지고 있으면 로그인하고 그렇지 않으면 새 계정을 만든다.
  • 인증이 완료되면 왼쪽 탐색 메뉴에 옵션이 표시된다. 그 중에 "Add plug-in"이 있다.
  • 플러그인 작성 페이지로 이동한다. 이 플러그인을 jQuery 1.2에서만 테스트했으므로 호환 버전으로만 포함할 것이다. 플러그인에 적합한 좋은 제목과 적절한 설명을 입력한다. 이제 플러그인을 다른 사용자에게 알릴 시간이 다가왔다. 이 사이트에는 수백 개의 플러그인이 있으므로 자신의 플러그인을 수많은 사용자에게 홍보할 수 있는 효과적인 방법이 필요하다. 최선을 다한 결과이므로 부끄러워하지 말자.
  • 이제 플러그인 홈 페이지를 입력해야 한다. 플러그인만을 작성했다면 아마 홈 페이지가 없을 것이다. 다행히도 플러그인을 호스팅할 수 있는 고유한 서버 설정이 없는 개발자를 위해 Google에서 오픈 소스 프로젝트에 대한 호스팅을 제공하고 있다. 필자는 이 플러그인을 Google Code에 넣기로 결정했다. 고유한 Google Code 프로젝트를 설정하려면 code.google.com에서 등록 프로세스를 수행하면 된다.
  • Submit을 누르면 플러그인이 작성된다.
    축하한다. 플러그인이 드디어 jQuery 플러그인 커뮤니티의 일부가 되었을 뿐만 아니라 여러분도 오픈 소스 프로젝트의 공식적인 기여자가 되었다. 자신이 직접 작성한 플러그인이므로 자신에게 보상하는 마음으로 별 5개를 부여하자.

결론

이 기사에서는 jQuery JavaScript 프레임워크용 플러그인을 작성하는 방법을 중점적으로 살펴보았다. 좋은 아이디어를 찾아서 플러그인에 사용할 몇 가지 스펙을 모으는 기초 단계부터 설명한 다음 작성자 간에 플러그인의 일관성을 유지하는 데 도움이 되는 jQuery 플러그인 규칙에 대해 설명했다. 또한 필자는 이러한 규칙이 준수되지 않는 경우를 여러 플러그인에서 자주 보았기 때문에 기사의 여러 부분에서 규칙에 대해 언급했었다. 특히 플러그인에서 "$" 대신 "jQuery"만을 사용해야 한다는 규칙이 중요하다. (물론 필자는 이 규칙을 따랐다.)

플러그인에 대한 배경 지식과 플러그인 작성 규칙에 대한 설명을 마친 후에는 기본 플러그인 프레임워크를 살펴보고 플러그인에서 메소드를 작성하는 것과 함수를 작성하는 것 간의 차이에 대해 설명했다. 특정 요소를 인수로 받아서 작업을 수행해야 하는 경우에는 항상 메소드를 사용해야 한다. 그리고 메소드를 호출한 사람이 페이지 요소를 제공해야 한다. 이에 반해 작업을 수행할 페이지에 있는 요소를 이미 알고 있기 때문에 특정 요소에 관심이 없는 경우에는 함수를 사용해야 한다. 이 경우에는 함수를 작성한 플러그인 개발자가 페이지 요소를 제공해야 한다. 두 가지 플러그인은 둘 다 유효한 플러그인이며 단지 플러그인의 요구에 따라 달라진다. 마지막으로 기본 옵션을 설정하는 기본적인 방법과 사용자가 옵션을 직접 제공하도록 허용하는 방법을 살펴보았다.

기사의 마지막 단계에서는 플러그인에 고급 기능을 추가하여 플러그인의 기능을 향상시켰다. 이 단계에서는 클로저를 추가하여 전체 플러그인을 닫았다. 이렇게 하면 private 함수와 public 함수를 효과적으로 작성할 수 있다. 플러그인 내에서 내부적으로 호출하는 함수는 플러그인 외부에서 호출할 수 없도록 private 함수로 설정했다. 또한 사용자가 한 장소에서 고유한 기본값을 정의함으로써 코딩 작업을 쉽게 처리할 수 있도록 지원하기 위해 기본값을 플러그인 사용자에게 노출하는 방법에 대해서도 살펴보았다.

마지막으로 예제 웹 애플리케이션에서 플러그인을 사용해 보면서 플러그인의 동작을 확인했다. 이 기사의 마지막 부분에서는 심혈을 기울여서 만든 이 플러그인을 jQuery 플러그인 커뮤니티 사이트에서 많은 사용자가 이용하고 있는 JavaScript 라이브러리로 게시했다

+ Recent posts