jQuery 퍼포먼스 향상을 위한 Tips And Tricks

이 글은 jQuery Proven Performance Tips And Tricks 를 보고 공부한 점을 정리한 글입니다. 원문의 내용과 똑같은 부분도 있고 제가 알고 있는 내용을 따로 덧붙인 부분도 있으니 원문을 꼭 보시기 바랍니다.

——————————————————————————————————————–

jQuery Proven Performance Tips And Tricks

퍼포먼스 향상 패턴을 사용하는 것이 왜 중요한가요?

  • 프로그램 코드는 간단 명료해야지 엉성하게 만들면 안됩니다.
  • 좋은 코딩 습관은 문제를 해결하는데 적합한 방법을 제공합니다.
  • 퍼포먼스에 좋은 패턴을 사용하지 않고 코딩을 한다면 브라우저가 그 만큼 일을 더 많이 하게 됩니다.
더 많은 일 = 더 많은 메모리 사용 = 느린 프로그램

..

퍼포먼스 향상 Tip 1

항상 최신 버전의 jQuery를 사용하세요

  • 가능하다면 항상 최신 버전의 jQuery를 사용하세요
  • 최신 버전은 이전 버전에 비해 성능이 향상되고 버그가 수정된 경우가 많습니다.
  • jQuery 파일을 최신 버전으로 교체해도 문제가 생기는 경우는 많지 않은편입니다.

퍼포먼스 향상 Tip 2

셀렉터를 어떻게 사용하느냐에 따라 성능이 향상됩니다.

  • 모든 셀렉터가 같은 방법으로 jQuery 객체를 생성하는 것은 아닙니다.
  • jQuery는 셀렉터를 사용하는 다양한 방법을 제공하지만, 각 방법별로 내부적 처리가 다르기 때문에 어떤 것은 빠르고 어떤 것은 느립니다.
  • jQuery는 DOM 노드를 탐색할 때, 가능하면 브라우저 내장 메서드를 사용해서 탐색해서 속도를 향상시키지만 브라우저 내장 메서드가 지원이 안되는 경우 모든 DOM 노드를 순회해야 하기 때문에 느려집니다.
ID & Element 셀렉터가 가장 빠릅니다.
  • $(‘#Element, form, input’)
  • 이 셀렉터들은 내부적으로 브라우저 내장 메서드인 getElementById(), getElementsByTagName()을 사용하기 때문에 가장 빠릅니다.
  • 꼭 셀렉터의 경우가 아니더라도 코딩을 할 때 가능한 경우 브라우저 내장 메서드를 사용하는 것이 가장 빠릅니다.
Class 셀렉터는 느립니다.
  • $(‘.element’)
  • 클래스를 선택할 수 있는 브라우저 내장 메서드인 getElementsByClassName()은 IE5~8에서는 지원되지 않습니다.
  • 그래서 클래스 셀렉터를 사용하면 IE5~8에서는 느립니다. 브라우저 내장 메서드 대신 jQuery가 직접(모든 DOM 노드 순회) 클래스를 찾기 때문입니다.
Pseudo & Attribute 셀렉터는 가장 느립니다.
  • $(‘:visible, :hidden’)
  • $(‘[attribute=value]’)
  • 이 셀렉터는 브라우저의 내장 메서드가 없기 때문에 jQuery가 직접 찾아야 해서 느립니다.
  • 최신 브라우저들 중 CSS 셀렉터로 DOM 탐색이 가능한 querySelector(), querySelectorAll() 을 지원하는 브라우저에서는 빠릅니다.
  • Pseudo & Attribute 는 매우 편리하지만 가능 느릴수 도 있기 때문에 사용에 주의를 기울여야 합니다.

퍼포먼스 향상 Tip 3

Parent and Children 셀렉터를 사용할 때 좋은 패턴에 대해 알아봅니다. 아래 패턴에서 $parent는 $(‘#parent’) 를 담아놓은 변수입니다. $(‘#parent’) 을 미리 처리했으므로 여기에 수행되는 시간은 고려하지 않는다는 의미입니다.

  1. $(‘.child’, $parent).show();    // context
  2. $parent.find(‘.child’).show();    // find()
  3. $parent.children(‘.child’).show();    // immediate children
  4. $(‘#parent > .child’).show();    // child combinator selector
  5. $(‘#parent .child’).show();    // class selector
  6. $(‘.child’, $(‘#parent’)).show();    // created context
1) $(‘.child’, $parent).show();
  • 이 패턴은 내부적으로 $parent.find(‘.child’).show(); 으로 변환되어 처리됩니다.
  • 가장 빠른 방법에 비해 ~5-10% 느립니다.
2) $parent.find(‘.child’).show();
  • 이 방법이 가장 빠릅니다.
  • 여기에 대한 설명은 곧 하겠습니다.
3) $parent.children(‘.child’).show();
  • 이 방법은 내부적으로 $.sibling 과 자바스크립트의 nextSibling() 메서드를 사용합니다.
  • 가장 빠른 방법에 비해 ~50% 느립니다.
4) $(‘#parent > .child’).show();
  • jQuery는 내부적으로 Sizzle 이라는 CSS 셀렉터 엔진을 사용하는데 Sizzle은 셀렉터를 오른쪽에서 왼쪽으로 읽어서 사용합니다.
  • 즉, .child 를 먼저 다 찾고 그 중에서 부모가 #parent 를 가진 것을 찾는 식입니다.
  • 가장 빠른 방법에 비해 ~70% 느립니다.
5) $(‘#parent .child’).show();
  • 4번과 마찬가지로 .child를 찾고 #parent를 찾습니다.
  • 내부적으로 .find() 메서드도 사용합니다.
  • 가장 빠른 방법에 비해 ~77% 느립니다.
6) $(‘.child’, $(‘#parent’)).show();
  • 내부적으로 $(‘#parent’).find(‘.child’) 로 동작합니다.
  • 2번 가장 빠른 방법과 비슷하게 생겼지만 2번 방법은 미리 $(‘#parent’)를 계산해 놓은 $parent 변수를 사용하고 있어서 더 빠릅니다.
  • 가장 빠른 방법에 비해 ~23% 느립니다.
가장 빠른 방법은 2) $parent.find(‘.child’).show(); 입니다. $parent 는 $(‘#parent’) 를 변수에 담아놓은 것입니다. 이런 패턴을 캐싱이라고 합니다. 즉, 자주 사용할 jQuery객체를 변수에 담아놓아서 나중에 사용할 때 다시 jQuery 객체를 만드는데 사용할 시간을 절약하는 것입니다.
무엇보다 getElementById 와 같은 브라우저 내장 메서드를 사용하는 것이 가장 빠릅니다. 가능하다면 이 것을 사용하세요
위에서 .find() 메서드를 많이 사용하고 있는데 .find() 메서드는 위에서 아래로 재귀적으로 탐색을 한다는 사실도 알아두세요

퍼포먼스 향상 Tip 4

꼭 필요한 경우가 아니라면 jQuery를 사용하지 마세요

  • $() 를 한번 수행할 때마다 당연히 시간이 소요됩니다. 필요하지 않은 경우라면 안쓰는 것이 성능향상에 좋습니다. jQuery 대신 “보통의” 자바스크립트 코드를 사용하세요
  • jQuery로 작성된 소스코드에 보통 자바스크립트 코드를 사용하면 안될 것 같은 느낌이 드신다면 전혀 걱정하지 않으셔도 됩니다. jQuery도 그냥 자바스크립트로 만들어진 함수입니다. jQuery와 보통 자바스크립트를 섞어쓰셔도 아무 문제가 없습니다.
$('a').bind('click', function () {
	console.log('You clicked: ' + $(this).attr('id'));
});

이렇게 작성하시지 마시고

$('a').bind('click', function () {
	console.log('You clicked: ' + this.id);
});

이렇게 작성하세요. function () {} 안에서 사용할 수 있는 this 는 a 태그가 만든 DOM 객체입니다. 여기에서 id 어트리뷰트에 접근하기 위해 $(this).attr(‘id’); 를 할 필요는 없습니다. 그냥 this.id 와 같이 DOM 객체 – 프로퍼티 접근방법을 사용하시면 되고, 더 빠릅니다.

퍼포먼스 향상 Tip 5

캐싱(Caching) 을 사용하세요. 위에서도 잠깐 말씀드렸지만 캐싱은 단지 나중에 다시 사용할 것 같은 “좀 시간이 걸렸던 작업” 을 변수에 저장해서 나중에 사용할 때는 그 시간이 걸렸던 작업을 다시 안해도 되게 해주는 패턴입니다.

이 글에서 말하는 캐싱은 셀렉터를 사용해 jQuery 객체를 만드는 작업을 캐싱하는 것입니다.

var parents = $('.parents'),	// caching
	children = $('.parents').find('.child'),	// bad
	kids = parents.find('.child');	// good

위 코드에서 children 변수는 캐싱해놓은 parents 변수를 사용하지 않고 $(‘.parents’)를 또 만들었습니다. kids 변수는 캐싱해놓은 parents 변수를 사용하고 그래서 children 보다 빠릅니다.

var foo = $('.item').bind('click', function () {
	foo.not(this).addClass('bar')
				.removeClass('foobar')
				.fadeOut(500);
});

위 코드는 $(‘.item’) 을 foo 라는 변수에 캐싱하고 있습니다. 그리고 $(‘.item’) 에 click 이벤트 핸들러로 등록해 놓은 함수안에서 foo 변수를 사용하고 있습니다. item 이라는 클래스를 가진 엘리먼트들이 클릭되었을 때 자기 자신만 빼고 나머지 item 들에 대해 addClass, removeClass, fadeOut 하는 코드입니다. 캐싱을 통해 성능도 향상되고 소스코드도 더 간결해 보이는 것 같습니다.

퍼포먼스 향상 Tip 6

메서드 체이닝을 사용하세요

var parents = $('.parents').doSomething().doSomethingElse();
  • 위 코드에서 doSomething() 메서드를 실행한 다음에 연달아서 doSomething() 메서드를 실행하고 있습니다. 이 것을 메서드 체이닝 또는 그냥 체이닝이라고 합니다.
  • $(‘.parents’).doSomething(); $(‘.parents’).doSomethingElse(); 이렇게 두 번 한것과 같은 실행을 하면서 속도는 더 빠릅니다.
  • 대부분의 jQuery 메서드들은 체이닝을 지원합니다. 체이닝이 가능해지는 원리는 간단합니다. $(‘.parents’).doSomething(); 의 실행결과 리턴값이 $(‘.parents’) 이기 때문에 거기에 연달아서 .을 찍고 doSomethingElse(); 를 실행해도 되는 것입니다.
  • 코드도 짧아지고 가독성도 높아집니다. 아래의 코드처럼 엔터를 몇 번 쳐주면 가독성은 더 좋아집니다.
// Without chaining
$('#notification').fadeIn('slow');
$('#notification').addClass('.activeNotification');
$('#notification').css('marginLeft', '50px');

// With chaining
$('#notification').fadeIn('slow')
				.addClass('.activeNotification')
				.css('marginLeft', '50px');

퍼포먼스 향상 Tip 7

이벤트 위임(event delegation)을 사용하세요 이벤트 위임은 이벤트의 bubble 속성을 사용한 것입니다. A 엘리먼트에 이벤트 핸들러를 등록하고 싶을 때 그 엘리먼트에 바로 붙이지 않고 그보다 상위 엘리먼트에 B에 등록합니다. 이벤트가 A에서 발생했어도 B로 bubble 되어 올라가는데 이때 B에 등록된 핸들러에서 A에서 발생한 것인지 살펴보고 맞으면 이벤트 핸들러를 실행하는 방식이 이벤트 위임입니다.

몇몇 엘리먼트에 이벤트 핸들러를 등록할 때는 이벤트 위임이 별로 효과가 없습니다. 하지만 어떤 ul에 li가 100개 있는데 li에 click 이벤트 핸들러를 등록하는 경우가 있다면, li 하나하나마다 등록해서 이벤트 핸들러 100개를 만드는 것보다 ul에 이벤트 핸들러 하나를 등록하는 것이 성능면에서 좋을 것입니다.

jQuery에서 엘리먼트에 이벤트 핸들러를 등록하는데 사용되는 메서드는 엘리먼트에 직접 등록하는 .bind() 메서드, 이벤트 위임을 사용하는 .live() 메서드, .delegate() 메서드가 있습니다. 이때 .live() 보다는 .delegate() 가 성능면에서 더 좋습니다.

최근에 나온 jQuery 1.7 버전에는 이벤트를 등록할 수 있는 새로운 메서드인 .on() 메서드가 나왔습니다. 그리고 .bind(), .live(), .delegate() 메서드 모두 jQuery 내부적으로는 .on() 메서드를 사용하게 소스코드가 바뀌었습니다. 그래서 jQuery 1.7 이상 버전을 사용하신다면 여러 메서드들 중 하나를 선택하실 필요없이 그냥 .on() 메서드를 사용하시면 됩니다.

퍼포먼스 향상 Tip 8

DOM 에 노드를 추가하거나 삭제하는 작업은 최소화하는 것이 좋습니다.

  • DOM 접근은 (성능면에서) 비용이 많이 드는 작업입니다.
  • 이 것은 .append(), .insertBefore(), .insertAfter() 등의 메서드 사용을 최소화 하면 성능이 향상된다는 것을 의미합니다.
  • 정보의 저장/조회 용도라면 .text(), .html() 보다 .data() 를 사용하는 것이 좋습니다.

Tip 1: 더 나은 .append() 사용법

  • .append() 는 한 번만 사용할 수 있는 방법을 찾으십시오
  • append를 여러 번 하는 것은 한 번 사용하는 것보다 90% 까지 느려질 수 있습니다. 그리고 캐싱되지 않은 jQuery 객체에 append 한다면 추가로 20%까지 느려집니다.

Tip 2: .detach() 를 사용하세요

  • .detach() 는 노드를 DOM에서 제거하면서 캐싱합니다. 그래서 나중에 다시 DOM에 넣을 수 있습니다.
  • 이벤트 핸들러가 많이 걸려있는 노드를 DOM에서 제거했다가 나중에 다시 넣는다면, 이벤트 핸들러를 다시 등록해줘야 할텐데 .detach() 를 사용하면 그냥 다시 DOM에 넣기만 하면 됩니다. .detach() 로 DOM에서 삭제하면 노드에 붙은 이벤트 핸들러 정보를 그대로 가지고 있기 때문입니다.
  • .detach() 를 사용하지 않은 경우보다 60%까지 빠릅니다.
// .detach() example
$('p').click(function () {
	$(this).toggleClass('off');
});

var p;
$('button').click(function () {
	if (p) {
		/* ..additional modification */
		p.appendTo('body');
		p = null;
	} else {
		p = $('p').detach();
	}
});

Tip 3: 더 나은 .data() 사용법

  • 보통 .data() 메서드는 $(‘#elem’).data(key, value); 와 같은 형태로 사용합니다.
  • 이보다는 $.data(elem, key, value); 처럼 사용하는 것이 더 빠릅니다. 여기서 첫 번째 인자인 elem은 var elem = document.getElementById(‘#elem’); 과 같이 만들어진 DOM 객체를 의미합니다.
  • (jQuery 객체).data(..) 형태로 사용하는 것보다 $.data(DOM객체, ..) 형태로 사용하는 것이 더 빠르다는 의미입니다.
  • 위에서 보신 것처럼 var elem; 을 DOM 객체로 만드는 수고가 한 번 더 들지만, 만약 미리 만들어 놓은 상황이라면 $.data() 를 사용하는 것이 더 빠릅니다.

퍼포먼스 향상 Tip 9

loop를 잘 사용하세요 – 반복문, jQuery 에서는 $.each() 와 .each()

  • jQuery의 $.each() 와 .each() 보다 자바스크립트 본래의 for, while 문이 더 빠르다는 것을 알고 계신가요?
  • jQuery의 loop는 편리하지만 항상 최고의 성능을 발휘하는 것은 아닙니다.
  • 그리고 꼭 jQuery의 loop 가 아니더라도 프로그래밍을 할 때 가능하다면 반복문은 피하는 것이 좋습니다. loop는 느립니다.

퍼포먼스 향상 Tip 10

꼭 필요한 경우가 아니라면 jQuery 객체를 만들지 마세요

  • 개발자들은 대체 가능한 더 간단한 방법이 있는 경우에도 jQuery 객체를 만들어 쓰곤 합니다. Tip 4 에서 살펴봤던 것처럼 $(this).attr(‘id’) 보다는 this.id 가 더 빠릅니다.
  • $.method() 형태가 $.fn.method() 보다 빠릅니다. Tip 8 에서 살펴본 것처럼 $.data() 가 .data() 보다 빠릅니다. 그래서 가능하다면 이렇게 사용하면 좋습니다.
  • 하지만 $.data() 를 사용하기 위해서는 역시 Tip 8 에서 살펴본 것처럼 DOM 객체를 미리 만들어 놔야 하므로, 상황에 따라 가능할 경우 그렇게 하면 됩니다.

보너스 Tip

DRY 하세요~ Don’t Repeat Yourself – 같은 코드를 반복적으로 사용하지 않는 방법을 연구하면 좋습니다.

간단한 예제 소스를 보겠습니다.

// Let's store some default values to be read later
var defaultSettings = {};
defaultSettings['carModel'] = 'Mercedes';
defaultSettings['carYeasr'] = 2012;
defaultSettings['carMiles'] = 5000;
defaultSettings['carTint'] = 'Metallic Blue';

// Non-DRY code
$('.someCheckbox').click(function () {
	if (this.checked) {
		$('#input_carModel').val(defaultSettings.carModel);
		$('#input_carYear').val(defaultSettings.carYear);
		$('#input_carMiles').val(defaultSettings.carMiles);
		$('#input_carTint').val(defaultSettings.carTint);
	} else {
		$('#input_carModel').val('');
		$('#input_carYear').val('');
		$('#input_carMiles').val('');
		$('#input_carTint').val('');
	}
});

// DRY code
var props = ['carModel', 'carYear', 'carMiles', 'carTint'];

$('.someCheckbox').click(function () {
	var checked = this.checked;

	/*
		Non-DRY code 에서는 무엇을 반복하고 있나요?
		1. input_ .. 형태로 생긴 field 명 들
		2. 값을 설정하기 위한 $('#input_ .. ').val(defaultSettings. .. );
		3. 값을 초기화하기 위한 $('#input_ .. ').val('');

		DRY code 에서는 어떻게 바꿀 수 있나요?
		1. input_ .. 형태로 생긴 field 명 들을 자동으로 생성
		2. $.each 로 loop를 돌면서 key를 받아서 defaultSettings[key] 형태로 사용
		3. 삼항연산자를 사용해서 - checked ? 설정 : 초기화 - 코드를 간략하게 만듬
	*/

	$.each(props, function (i, key) {
		$('#input_' + key).val(checked ? defaultSettings[key] : '');
	});
});

..

  • 신현

    담아가겠습니다^^

    • Anonymous

      감사합니다..^^

  • http://www.facebook.com/sungjoon.um Sungjoon Um

    즐겨찾기 해두고 잘 보고 있습니다.^^ 좋은 도움이 되어요

    • codefactory

      감사합니다..^^

  • 경일

    jquery 를 주로 사용하고 있는데 도움이 되는 글이네요~
    감사합니다~

    • codefactory

      감사합니다..^^

  • Pingback: jQuery 퍼포먼스 향상을 위한 Tips And Tricks | WebBrain()

  • Pingback: jQuery 퍼포먼스 향상을 위한 Tips And Tricks | Auctionpro()

  • Davincl

    블로그에 담아 갈게요~ 유용한 글귀네요~

    • codefactory

      감사합니다..^^

  • orion_203

    출처를 밝히고 제 블로그에 퍼 갑니다. 미리 감사합니다.

    • codefactory

      감사합니다..^^

  • djpk

    가장 잘 정리된 글이라 퍼갑니다. 유용한 글 감사합니다~

  • sjkim

    너무 좋은 글이라 블로그에 담아갈게요~

    • codefactory

      감사합니다..^^

  • 꾸스~

    너무 도움되는글입니다. 조만간 퍼가겠습니당!!!

    • codefactory

      감사합니다..^^

  • 똥광이

    감사합니다. 좋은글이요, 펌펌 할께요!

  • 유상훈

    감사합니다~ 혹시.. 제코드 봐주실수잇을까요..
    위에 코드 보고 작성한건데요..

    탭 화면 구현하려고 한거에요..

    제가 궁금한건.. 아래와 같은 클릭이벤트에서

    this는 어떤걸 가르키는 건지 모르겟어요..

    또한 $(this).addClass(“active”).css(“color”, “darkred”); 부분은 어쩔수 없이..

    $(this)를 썻는데 더 좋은 방법이 잇을까요??

    리스트형
    썸네일정보형
    지도형

    var tab = this.element.tab.tabType.on(‘click’, function(e) {
    tab.not(this).removeClass(“active”).css(“color”, “#333”);
    $(this).addClass(“active”).css(“color”, “darkred”);
    });

    • codefactory

      안녕하세요

      var tab = this.element.tab.tabType.on(‘click’, function(e) {
      tab.not(this).removeClass(“active”).css(“color”, “#333”);
      $(this).addClass(“active”).css(“color”, “darkred”);
      });

      저도 위와 같은 소스코드는 처음 봐서 해석이 잘 안되고 있습니다.
      this 는 function 안에서 쓰이는 키워드라서
      this.element.tab.tabType 이 무엇인지 해석이 되려면 이 코드를 감싸고 있는 function 이 어떻게 생겼는지 알아야 할 것 같습니다.

      그리고 $(this).addClass(“active”).css(“color”, “darkred”); 에 대해 말씀드리면 클릭이벤트 핸들러 function 내부에 있는 $(this) 는 클릭된 엘리먼트 자체에 대한 jQuery 객체입니다. addClass 를 사용하려면 jQuery 객체여야 하므로 이 부분은 특별히 더 좋은 방법이 생각나지는 않습니다.

      별로 도움이 못되어 드린 것 같아 죄송합니다.

  • Jiyeol Park

    안녕하세요 질문이 있는데 이 부분이 지금 여기서 말씀하시는 주제와 관련있을지 모르겠지만

    jquery 극초보자구요, html css까지만 할줄압니다.
    header_bg라는 id의 div(메뉴임)가 화면 상단으로부터 위치가 200px 내려왔을때
    화면 위로 70px 감춰져있던 메뉴가 0으로 애니메이션 슬라이드로 내려오게 만들었거든요
    근데 이게 딜레이가 엄청 심하네요; 코드상에 오류는 없는데 뭐가 문제인지 모르겠어요
    div의 css는 top:-70px; height:70px; 그리고 z-index를 다른 div와 겹치지 않게 적당히 높게 설정했습니다.
    특히 맥에서 트랙패드로 하면 거의 사용못할정도로 딜레이가 생겨요 ;;

    $(document).ready(function(
    ){ $(window).on(“scroll”,function(
    ){
    var hs = $(window).scrollTop();
    if (hs>=200){
    $(“#header_bg”).animate({ top:’0px’
    }); } else {
    $(“#header_bg”).animate({ top:’-70px’ });
    } });
    });

    무엇이 문제일까요?? …

    • codefactory

      안녕하세요, 답글이 너무 늦어서 죄송합니다.
      올려주신 소스코드를 살펴보았는데, 제가 어떤 점이 문제이고, 어느 부분을 수정하시면 좋을지 파악을 못하였습니다.
      혹시 가능하시다면 해당 페이지의 전체 소스코드를 알려주실 수 있으신지요?
      물론 그걸 보고도 모를 수도 있지만, 혹시나 하는 마음에 말씀드립니다.
      감사합니다.

    • WangKooN

      codefactory님 좋은글 잘 보고 갑니다.
      그리고 도움이 될까하고 댓글 남겨 봅니다.위에 코드만 보았을때 아래와 같이 animate에 stop()을 넣어주셔야 될것 같네요~
      $(“#header_bg”).stop().animate({ top:0 });

  • 지난가는 아이티

    좋은글고맙습니다.잘보고갑니다.도움이많이되었어요:)

    • codefactory

      네, 감사합니다..^^

  • https://nobodj.space nobodj

    아, 유익하네요. 감사합니다. 잘보고 가요 ~