본문 바로가기

Develop/HTML & JavaScript & CSS

jQuery 제대로 사용하기(jQuery Best Practice)

요즘 웹을 개발하는데 있어서 Angular나 ember등의 프레임워크를 코어하게 사용하지 않는한 jquery는 매우 기본적인 라이브러리가 되었습니다.

하지만 개발을 하면서 jquery를 그냥 아무렇게나 막 사용하는 경우가 종종 있습니다. 

예를 들면 코드상에서 엘레먼트를 선택함에 있어 매번 

$('div').show();

$('div').toggleClass('wow');

jquery의 셀렉터를 이용해 인자를 선택하는데 이런경우 매번 인자를 찾아 나서기 때문에 성능에 좋지 못합니다. 

그래서 이경우에는 변수로 만들어 사용하는 방법을 쓰는것을 권장합니다.

var allspans = $("li ul span");

allspans.show();

allspans.toggleClass("bubu");

 

이러한 것들 외에도 몇가지 Bad Practice가 있습니다. 그래서 JQuery를 분석하기에 앞서 Jquery에서 권장하는 Best Practice를 알아보겠습니다.

 

1. Loading Jquery

jquery를 호출하는 경우에는 반드시 CDN을 포함해서 사용합니다. 

**CDN을 사용하면 몇가지 이점이 있습니다.

1. 다른 도메인을 사용하는 것입니다.

일반적으로 브라우저는 동일한 도메인에서는 동시에 4개의 파일을 다운로드 받습니다. 다서번째 부터는 순서대로 하나씩 다운로드를 받습니다. 하지만 다른 도메인을 사용한 CND의 경우에는 이러한 영향을 받지않아 동일한 도메인에서 4개가 받는 순간 동일한 순간에 파일을 불러옵니다. 

 

2. 파일이 캐쉬됩니다.

Jquery의 경우에는 웹상의 대부분의 사이트에서 사용이 됩니다. 그렇기 떄문에 사용자는 여러분의 사이트에 이미 방문했던 적이 있을 확률이 높습니다. 그러므로 jquery가 이미 브라우저에 캐싱이 되어 있다면 재방문시에 다시 다운받는 일은 없을것입니다.

 

3. 대용량의 인프라를 제공한다.

여러분의 서버가 굉장이 좋을 수도 있습니다. 그런데.. 구글 보다 좋을까요? MS는요? 아니면 Yahoo는?? 내서버가 아무리 좋아도 이들 업체들보다는 환경이 좋지 못합니다. 그냥 이런데서 끌어다가 쓰는것이 좋지않는 네트워크 환경에서도 좋은 성능을 내는 방법일것입니다.

 

4. 데이터 센터의 분산.

만약에 여러분의 메인 서버는 서울에 있다고 합시다. 그런데 미국이나 유럽의 사용자가 여러분의 서비스에 접근을 합니다. 이경우에는 CDN 제공자들은 이미 로콜화가 되어 있기때문에 메인서버에서 끌어다가 쓰는 것보다 좋은 성능을 제공합니다.

 

5. 버전컨트롤이 용이합니다.

때때로 특정 버전의 CSS, javascript 라이브러리를 필요로 하는 경우가 있습니다. 이경우 편리하게 몇글자만 추가하면 원하는 버전의 파일을 받을수가 있습니다.

 

6. 사용분석이 가능합니다.

다수의 유료CDN업체들은 일반적으로 바이트 단위로 요금을 청구합니다. 그래서 사용자 입장에서는 사용량을 체크할수있습니다.

 

7. 성능을 향상하고 경제적입니다.

CDN은 대역폭을 절약하고 성능을 향상시키고 호스팅 비용을 절감할수 있습니다.

 

2. Jquery Variable

Jquery 객체의 경우에는 변수이름 앞에 $ 를 사용해서 만들어 사용하세요. 한번 만들어둔 변수는 캐쉬되어 다시 찾는 일이 발생하지 않아 성능을 향상시킵니다.

var $myDiv = $("#myDiv");

$myDiv.click(function(){...});

 

3. Selectors

jquery가 셀렉터를 Sizzle 을 사용하면서 몇가지 선택자를 사용하는데 팁이 있습니다.

1. ID를 통한 엘레먼트에 대한 접근은 언제든 가능합니다. 그리고 이 방법이 document.getElementById()를 사용하기 때문에 빠르게 엘레먼트에 접근할수 있는 방법입니다.

 

2. 클래스로 엘레먼트에 접근할경우에는 타입을 제외시키고 접근하세요

var $products = $("div.products"); // SLOW

var $products = $(".products"); // FAST

 

3. ID > child 의 구조로 엘레먼트를 접근하는 경우에는 나열하지말고 해당 엘레먼트를 find() 함수를 사용하여 접근하세요.

// BAD, Sizzle selector engine을 사용하는데 좋지 못한 성능을 만들어 냅니다.

var $productIds = $("#products div.id");

 

// GOOD, #products document.getElementById()를 통해 이미 엘레먼트가 선택되었기에 이를 통해 Sizzle selector engine이 바로 하위엘레먼트에 접근을 할수 있습니다.

var $productIds = $("#products").find("div.id");

 

4. 셀렉터를 할경우에는 두리뭉실하게 선택해서 세밀하게 접근하는 방법을 권장합니다.

$("div.data .gonzalez");

이러한 접근 보다는 

$(".data td.gonzalez");

이러한 접근이 좋습니다.

왼쪽은 덜 세밀하게 오른쪽은 세밀하게!!

 

5.  과도하게 세밀한 접근은 피하세요

 $(".data table.attendees td.gonzalez");

 // Better: 가능하다면 중간의 엘레멘트는 빼고 접근하는것이 좋습니다. 가능하다면!!!

$(".data td.gonzalez");

 

6. Context를 함께 제공해라.

// SLOWER 이경우에는 페이지 전체를 돌면서 .class 인자를 찾습니다.

$('.class');

하지만

// FASTER  #class-container 아래에서 만 .class 인자를 찾습니다..

$('.class', '#class-container');

 

7. Universal 한 셀렉팅을 피하세요.

$('div.container > *'); // BAD

$('div.container').children(); // BETTER

 

8. 보이지 않는 Universal 한 셀렉팅을 피하세요.

$('div.someclass :radio'); // BAD

이경우에는 * 를 사용한 셀렉터가 적용되고 있습니다.

$('div.someclass input:radio'); 

 

9. ID를 통한 엘레먼트의 접근의 경우에는 오직 ID만을 통해 접근하세요.

$('#outer #inner'); // BAD

$('div#inner'); // BAD

$('.outer-container #inner'); // BAD

$('#inner'); // GOOD, only calls document.getElementById()

 

4. DOM Manipulation

1. 이미 존재하는 엘리먼트를 조작하는 경우에는 그 부분을 먼저 때내어서 작업을 하고 이를 다시 원래 위치에 붙입니다.

var $myList = $("#list-container > ul").detach();

$myList.appendTo("#list-container");

list-container아래의 ul 엘리먼트에 어떠한 작업을 할경우에는 이를 list-container에서 먼저 분리시켜 작업을 하고 다시 이를 list-container에  append 시키는 방법을 사용합니다.

 

2. 특정엘리먼트들이 추가 되는 경우는 매번 그 엘리먼트를 추가 하지 말고 한번에  append나 array.join() 을 사용해서 추가합니다.

// BAD

var $myList = $("#list");

for(var i = 0; i < 10000; i++){

    $myList.append("<li>"+i+"</li>");

}

-> 이경우 매번 엘리먼트들이 추가 되고 있습니다.

// GOOD

var $myList = $("#list");

var list = "";

for(var i = 0; i < 10000; i++){

    list += "<li>"+i+"</li>";
    
}

$myList.html(list);

 -> 이전 방식보다는 좋은 방법입니다. 해당 엘리먼트를 문자열로 받아 한번에 추가 시키는 방법입니다.

// EVEN FASTER

var array = []; 

for(var i = 0; i < 10000; i++){

    array[i] = "<li>"+i+"</li>"; 

}

$myList.html(array.join(''));

-> 조금더 좋은 방법입니다. 추가 엘리먼트를 배열에 담아 배열을 join() 을 사용해서 한번에 추가 시키는 방법으로 이중에서 가장 좋은 성능을 제공합니다.

 

3. 존재하지 않는 엘리먼트를 대상으로 작업하지 마세요.

// BAD

$("#nosuchthing").slideUp();

 -> 만약에 #nosuchthing 가 존재하지 않는 엘리먼트라면  .slideUp() 이 발생하기 전에 쓸데 없이 다른 함수가 3개가 더실행이 됩니다. 존재하지 않는 엘리먼트를 대상으로 리소스를 낭비하는 꼴입니다.

// GOOD

var $mySelection = $("#nosuchthing");

if ($mySelection.length) {

    $mySelection.slideUp();

}

 

5. Event

1. 페이지당 하나의 document ready 핸들러를 사용하세요.

 

2. 콜백함수를 사용할때 익명함수를 사용하기 보다는 이름을 지어 콜백함수로 사용하는 것이 디버깅을 할때 좋습니다.

$("#myLink").on("click", function(){...}); // BAD
 // GOOD

function myLinkClickHandler(){...}

$("#myLink").on("click", myLinkClickHandler);

 

3. 익명함수는 디버깅이 어렵습니다. document ready 함수를 사용함에도 익명함수는 사용하지 마세요.

$(function(){ ... }); // BAD: 이 함수는 다시 재사용을 할수가 없는 함수 입니다.
// GOOD

$(initPage); // or $(document).ready(initPage);

function initPage(){

    // Page load event where you can initialize values and call other initializers.

}

-> 각 페이지에 init() 함수를 만들어 사용하는것을 권장합니다.

 

4. 외부라이브러리나 파일을 사용하는 경우에는 document ready 핸들러 이전에 모두 설정하세요.

<script src="my-document-ready.js"></script>

<script>

// Any global variable set-up that might be needed.

$(document).ready(initPage); // or $(initPage);

</script>

 

5. HTML 안에 inline javascript는 매우 구식입니다. 하지마세요.

<a id="myLink" href="#" onclick="myEventHandler();">my link</a> 
$("#myLink").on("click", myEventHandler); // GOOD


6. 가능하다면 이벤트의 콜백으로 사용되는 함수는 따로 이름을 지정하여 사용하세요. 이렇게 하면 이벤트를 붙였다가 필요없을때는 제거 할수도 있고 사용성이 더욱 높아집니다.

$("#myLink").on("click.mySpecialClick", myEventHandler); // GOOD

$("#myLink").unbind("click.mySpecialClick");

 

6. Ajax

1. .getJson(), .get() 등 이러한 방식으로 데이터를 부르는 것 보다는 $.ajax() 를 사용한 방법을 권장합니다.

2. https 를 사용하는 사이트에서 http를 통한 요청을 하지 마세요. 스키마less 한 url을 권장합니다.

3. PUT 메서드를 요청할때 url에 파라메터가 들어가지 않게 하세요.

$.ajax({

    url: "something.php?param1=test1&param2=test2",

    ....

}); 

이거 보다는 

$.ajax({

    url: "something.php",

    data: { param1: test1, param2: test2 }

});

 

4. 특정 데이터타입을 명시해 주세요. 이래야 어떤 데이터타입으로 동작중인지 쉽게 알수 있습니다.

5. delegeted events를 사용하세요.

이벤트 Delegation에 대하여 잠깐 알아보고 넘어가겠습니다.

<ul id="list">

        <li><a href="http://domain1.com">Item #1</a></li>

        <li><a href="/local/path/1">Item #2</a></li>

        <li><a href="/local/path/2">Item #3</a></li>

        <li><a href="http://domain4.com">Item #4</a></li>

</ul>
$( "#list a" ).on( "click", function( event ) {

    event.preventDefault();

    console.log( $( this ).text() );

});

a를 클릭하면 해당 text를 화면에 찍는 함수입니다. 이 함수는 일단 완벽하게 동작을 합니다. 그럼 여기에 다른 기능을 추가해보겠습니다.

$( "#list a" ).on( "click", function( event ) {

    event.preventDefault();

    console.log( $( this ).text() );    

});

$( "#list" ).append( "<li><a href='http://newdomain.com'>Item #5</a></li>" );

 

이경우에는 위의 리스너를 마치고 새로운 엘리먼트를 추가하였습니다. 이제 화면에는 

<ul id="list">

        <li><a href="http://domain1.com">Item #1</a></li>

        <li><a href="/local/path/1">Item #2</a></li>

        <li><a href="/local/path/2">Item #3</a></li>

        <li><a href="http://domain4.com">Item #4</a></li>

        <li><a href="http://domain5.com">Item #5</a></li>

</ul>

5번째 새로운 엘리먼트가 추가되었습니다. 그리고 item #5를 클릭해보았습니다. 그런데 item $5 라는 글자가 찍혀야 하지만 아무런 동작을 하지 않습니다. 왜그런것일까요??

일단 우리가 위에 함수를 만든 방식을 directly event handler 라고 부릅니다. 이 경우 이미 추가 되기 전의 엘리먼트를 대상으로만 이벤트들이 바인딩되는데 다르게 표현하면 on메서드가 호출될 당시에 존재하는 엘리먼트를 대상으로만 동작을 한다는 말입니다.

그러면 동적으로 추가되는 엘리먼트에는 이벤트를 추가 할수가 없는 것일까요? 

이러한 경우를 대비해서 delegated event handler라는 것이 존재합니다.

우선 우리는 이벤트가 이벤트가 어떻게 동작을 하는지 알아볼 필요가 있습니다. 우리가 a 태그를 클릭을 하면 

<a> -> <li> -> <ul #list> -> <div #container> -> <body> -> <html> -> document root

이런 순서로 이벤트가 일어나게 됩니다.

즉 하나의 엘리먼트에 클릭을 하면 결국에는 전체 도큐먼트의 바디를 대상으로 이벤트가 발생하게 됩니다. 이를 이벤트 bubbling 또는 이벤트 propagation 이라고 합니다. 이러한 일이 일어나느 것을 막기 위해 우리는  delegated event handler를 사용합니다.

$( "#list" ).on( "click", "a", function( event ) {

    event.preventDefault();

    console.log( $( this ).text() );

});

이런식으로 표현을 하면 됩니다.이렇게 한다면 추가된 a 태그에도 이벤트가 바인딩되어 원하는 결과를 만들수가 있습니다. 그리고 두번째 인자인 a 의 경우에는 여러 조건들을 추가 하여 표현이 가능합니다.

$( "#list" ).on( "click", "a[href^='http']", function( event ) {

    $( this ).attr( "target", "_blank" );

});

 

6. Promise interface를 사용하세요.

Promise라는 것은 싱글스레드를 기반으로 동작하는 javascript의 경우에는 순차적으로 코드들이 실행이 됩니다. 그래서 하나의 동작이 마쳐야 다음 동작이 진행이 되고 이전동작이 완료되지 않은 경우에는 프로그램 전체가 멈추는 일이 발생을 합니다. 즉 모든 코드가 동기적으로 움직인다는 말입니다. 

하지만 우리는 비동기적인 움직임을 원합니다. 해당함수가 동작하고 그동작이 완료되면 그때 그결과를 기반으로 다른 동작을 진행하는 그런 비동기적인 동작을 해야하는 경우가 많이 존재합니다. 특히 ajax의 경우에는 훨씬 더 비동기성이 중요해 집니다.

jquery에서는 이러한 일을 담당해줄 deferred라는 메서드가 있습니다. 그리고 $.ajax는 기본적으로 이 deferred를 반환합니다. 그리고 deferred.resolve를 통해서 전달받은 인자를 넘겨줍니다. 이에 대한 자세한 내용은

http://www.html5rocks.com/ko/tutorials/es6/promises/#toc-async

이곳을 참조한다면 조금더 깊이 있는 이해가 가능할것입니다.

아무튼 우리는 $.ajax의 deferred를 기반으로 

var jqxhr = $.ajax({ ... });

jqxhr.done(successHandler);

jqxhr.fail(failureHandler);

done이라든지 fail같은 함수를 사용한다면 비동기적으로 원하는 결과를 얻을수 있습니다.

 

7. sample Ajax template

ajax를 사용시 권장드리는 방법입니다.

var jqxhr = $.ajax({

    url: url,

    type: "GET", // 기본은 GET 방식이고 원하는 POST, PUT,DELETE로 변경가능합니다.

    cache: true, // 기본은 true이고, false의 경우에는 오직 데이터 타입이 'script' and 'jsonp' 을 대상으로 합니다.

    data: {}, // 리퀘스트시 넘기는 값을 저장합니다.

    dataType: "json", // 특정데이터 타입을 적습니다.

    jsonp: "callback", // JSONP 요청시 콜백 파라메터를 적습니다.

    statusCode: { // 특정 에러코드를 매핑하고 싶다면 사용합니다.

        404: handler404,

        500: handler500

    }

});

jqxhr.done(successHandler);

jqxhr.fail(failureHandler);

 

7. Effects and Animations

1. 제한적이고 일관성있는 접근으로 애니메이션을 사용하세요.

2, 지나치게 많은 에니메이션을 사용하지 마세요

 - 간단한 show/hide, toggle, slideUp/ slideDown 이벤트를 토글을 하려는 대상에 사용하세요.

 - 에니매이션의 속도는 함수 정의시 같이 정의하세요.

 

8. Plugins

1. 플러그인을 선택할때는 지속적인 업데이트가 이뤄지고, 문서화가 잘되어 있고, 커뮤니티가 활발한 것을 선택하세요.

2. 플러그인이 사용하는 jquery의 버전이 현재 사용하고 있는 버전과 호환이 되는지 확인하세요.

 

9. Chaining

1. 해당엘리먼트에 여러 메서드가 동작하는 경우에는 체이닝을 시켜 동작시키는 것이 좋습니다.

$("#myDiv").addClass("error").show();

2. 3개이상의 메서드가 체이닝되는 경우에는 복잡도가 매우 증가하기 시작합니다. 라인별로 정의하여 사용하는 것을 권장합니다.

$("#myLink")

    .addClass("bold")

    .on("click", myClickHandler)

    .on("mouseover", myMouseOverHandler)

    .show();

3. 체이닝이 긴 경우에는 이를 변수화 시키면 캐시되기때문에 다시 사용시 용이 합니다.

 

10. Miscellaneous

1. 파라메터들은 객체화 시켜 사용합니다.

$myLink.attr("href", "#").attr("title", "my link").attr("rel", "external"); // BAD, 3 calls to attr()

이러한 방식보다는 

$myLink.attr({

    href: "#",

    title: "my link",

    rel: "external"

});


2. CSS를 jquery코드상에서 적용하시 마세요. 필요시 css 클래스를 정의하여 사용하세요.

$("#mydiv").css({'color':red, 'font-weight':'bold'}); // BAD
.error { color: red; font-weight: bold; } /* GOOD */

$("#mydiv").addClass("error"); // GOOD


3. jquery는 계속적으로 발전하고 있는 라이브러리입니다. 불필요한 메서드들은 끊임없이 제거되고 이를 보완할 새로운 메서드들이 계속 추가 됩니다. 

http://api.jquery.com/category/deprecated/

이곳을 확인하여 제거된 메서드는 사용하기 않도록합니다.

 

4. jquery는 만능이 아닙니다. 여전히 native javascipt가 더 좋은 성능을 만들어 내는 부분이 있습니다. 예를 들면

$("#myId"); 이러한 엘리먼트에 대한 접근은 여전히 document.getElementById("myId") 이를 통한 접근 보다 느립니다. 

 

 

 

이상으로 jQuery Best  Practice에 대해서 알아보았습니다. 위의 내용들을 생각하고 코드를 만든다면 원하는 성능을 만들어 낼 수 있을것입니다.



출처: https://yubylab.tistory.com/entry/Jquery-제대로-사용하기?category=575633 [Yuby's Lab.]