Java Script

저자
니콜라스 자카스 지음
출판사
한빛미디어 | 2011-09-25 출간
카테고리
컴퓨터/IT
책소개
자바스크립트 성능 최적화를 위한 방법을 제시하는 『Java Sc...
가격비교


자바스크립트를 사용하면서 놓치고 갔던 부분들을 다시 한번 확인할 수 있었던 책이다.
이게 왜 그렇게 되는건지, 왜 이게 최적화인지 한번쯤 집고 넘어갈 수 있었다.

아래는 책 내용 중에서 간략하게 정리를 해보았다.

소스나 설명 부분이 빠져서 이해가 안가는 부분은 책을 참고해야한다.

  • 모든 <script> 캐그를 가능한 한 <body> 태그의 마지막에 배치
    • 스크립트를 내려받거나 실행하는 동안에는 다른 자원을 내려받을 수 없기 때문
  • <script> 태그의 수를 제한하기를 권장
    • <script> 태그를 만날 때마다 페이지 렌더링이 차단되기 때문
    • 인라인 코드나 외부 파일에 있는 코드 모두 해당하는 얘기
    • 2kb 파일 4개 < 100kb 파일 하나 , 페이지에서 참조하는 외부 자바스크립트 파일의 수를 줄이는 것이 좋음

  • 식별자가 스코프 체인의 깊숙한 곳에 있을수록 읽기/쓰기 접근 시간은 느려짐
  • 함수 안에 있는 지역 변수에 가장 빠르게 접근 / 전역 변수에 접근하는 것은 가장 오래 걸림
  • 프로토타입 체인도 동일함
  • location.href(제일 빠름) < widow.location.href < window.location.href.toString()(제일 느림)
    • 이러한 속성이 인스턴스 멤버가 아니라면 각 마침표(.)에서 프로토타입 체인을 검색할 때마다 멤버를 해석하는 데 필요한 시간이 늘어남
  • 객체멤버는 필요할때만 써야함 - 함수 하나에서 객체 멤버값을 두번 이상 읽을 이유가 없음

  • DOM 스크립팅
    • DOM 요소 접근만으로도 많은 비용이 든다.
    • 비효율적인 컬렉션을 줄여야 한다.
    • for 문에 length를 넣게 되면 매순간 쿼리를 다시 실행해야 하므로 느림.
    • var allDivs = document.getElementsByTagName('div');

      for ( var i = 0 ; i < allDivs.length ; i++ ) {

      /* 매 순간 length를 체크하기 때문에 비효율적 */

      }


      for ( var i = 0, len = allDivs.length ; i < len ; i++ ) {

      /* 한번만 체크하기 때문에 효율적 */

      }

    • 컬렉션 요소에 접근할 때의 지역 변수
    • var coll = document.getElementsByTagName('div');

      var len = coll.length;

      var name = "";

      var el = null;


      for ( var count = 0 ; count < len ; count++ ) {

      /*

      // 제일 느림

      name = document.getElementsByTagName('div')[count].nodeName;

      name = document.getElementsByTagName('div')[count].nodeType;

      name = document.getElementsByTagName('div')[count].tagName;

      */

      /*

      // 빠름

      name = coll[count].nodeName;

      name = coll[count].nodeType;

      name = coll[count].tagName;

      */

      // 제일 효율적인 방법

      el = coll[count];

      name = el.nodeName;

      name = el.nodeType;

      name = el.tagName;

      }

    • 선택자 API - querySelectorAll("#id a")가 제일 빠름
      • querySelectorAll() : 문서 전체를 쿼리
      • querySelector() : 쿼리 결과의 첫 번째 노드만 반환

  • 리플로우와 리페인트 최소화하기
    • DOM과 스타일 변경을 하나로 묶어 적용해야함
    • ( el.style.borderLeft, el.style.border... -> el.style.cssText 혹은 el.className )
    • DOM 변경 한 번에 처리하기
      • 요소를 숨기고 변경한 후 다시 드러내기 ( display none -> element 처리 -> block )
      • 현재 DOM 바깥에 문서 조각을 만들어 변경한 후 문서에 복사
      • 현재 요소를 문서 밖의 노드에 복사해서 사본을 변경한 후 원래 요소를 대체
    • 브라우저는 리플로우가 적게 일어날수록 응답성이 좋아집니다.

  • for-in 루프는 다른 루프(for, while, do-while)에 비해서 느리다.
  • 루프 속도 결정
    • 루프 몸체에서 하는 일
    • 반복 횟수
  • 루프 최적화
    • 객체 멤버와 배열 항목 검색을 줄여라. (el.length -> len = el.length 지역 변수화)
    • 루프 순서를 거꾸로 하라(감소 연산자 사용 --). 조건문을 0 즉, false로 평가하게 하여 속성검색(조건문 비교)을 최소화하는 효과를 얻는다.

  • 문자열 병합
    • + 연산자 : str = "a" + "b" + "c";
    • += 연산자 : str = "a"; str += "b"; str += "c";
    • array.join() : str = ["a","b","c"].join("");
    • string.concat() : str = "a"; str = str.concat("b","c");
    • 적은 문자열을 합칠때는 비슷한 속도, 하지만 문자열 길이와 개수가 늘어나면 차이가 있음
    • 테스트를 통해 알아보기를 추천
  • 정규 표현식 - 책을 참조해서 쭉 봐야할듯.. (간단하게 정리하기에는 책이 자세하게 나와있음)

  • "자바스크립트가 1초 이상 실행된다면 아마도 그 코드는 잘못된 것이다." - 자바스크립트를 개발한 브렌든 에이히(Brendan Eich)
  • 하지만 자바스크립트 동작 하나에 걸리는 시간은 최대 0.1초
    • 인터페이스가 사용자의 입력에 0.1초 안에 반응한다면 사용자는 "사용자 인터페이스의 요소를 직접 조작하는 기분"을 느낀다고 함 - 사용성 전문가인 제이콥 닐슨(Jakob Nielsen)
  • 스크립트가 동작 중일 때 UI는 사용자의 조작에 맞게 업데이트되지 않는다.
    • 페이지의 모양보다는 동작이 더 우선되므로 자바스크립트 작업이 실행되는 동안 사용자의 입력으로 발생한 UI 업데이트는 자동으로 건너뜁니다. 따라서 스크립트가 실행 중일 때 버튼을 누르면 버튼의 onclick 핸들러는 실행되지만 버튼이 눌린 모습은 결코 표현되지 않습니다.
    • 타이머를 이용한 배열 처리 - 반복문에서 하는 일을 연속된 타이머로 지정하여 처리, 배열을 처리할 때 타이머를 쓰게 될 경우 총 실행 시간이 증가하지만 UI 스레드가 풀려서 브라우저가 잠기는 현상을 막을 수 있다.
    • 작은 함수의 연속으로 분할하기 - 각 메서드에 별개의 타이머를 이용해서 전체 함수를 여러 단계의 더 작은 작업으로 분할
    • 웹 워커 API - 브라우저 UI 스레드의 시간을 사용하지 않으면서 코드를 실행

동적 <script> 태그 삽입

var scriptEl = document.createElement('script');
scriptEl.src = "http://DOMAIN/js/anyJS.js";
document.getElementsByTagName('head')[0].appendChild(scriptEl);

  • 다른 도메인에 있는 스크립트 호출 가능
  • 매개변수는 GET 요청만 가능
  • 응답 헤더에 접근하거나 응답 전체를 텍스트로 변환해서 접근 불가
  • 응답 자체가 실행 가능한 자바스크립트 형태로 구성되어야함

  • AJAX 처리시 데이터 포맷
    • XML, JSON, HTML, 커스텀 포맷
    • 커스텀 포맷을 통해서 split 하여 사용한다면 매우 큰 데이터를 정말 짧은 시간에 클라이언트로 받아서 처리가능
    • 가벼운 포맷은 문자열로 구분한 커스텀 포맷과 JSON
  • 고성능 AJAX 구현
    • 데이터 포맷
      • 평범한 텍스트와 HTML - 클라이언트의 CPU 부담을 줄일 수 있음
      • JSON - 가볍고 빨리 파싱할 수 있으며(문자열이 아니라 네이티브 코드로 취급되었을 때) 상호 운영성이 높음
      • 문자로 구분한 커스텀 포맷은 매우 큰 데이터 집합에서도 매우 가볍고 가장 빨리 파싱할 수 있지만 서버 측에서 포맷하고 클라이언트 측에서 파싱할 때 별도로 프로그래밍해야 함
    • 데이터 요청
      • XHR - 페이지와 같은 도메인에 데이터 요청시 가장 유연하고 세밀. 받아오는 모든 데이터를 문자열로 취급하므로 파싱 시간이 늦음
      • 동적 <script> 태그 삽입 - 페이지와 다른 도메인에 데이터를 요청할 수 있고 자바스크립트와 JSON을 네이티브 코드처럼 실행. 인터페이스가 덜 견고하며 헤더나 응답 코드를 읽을 수 없음
      • Multipart XHR - 요청의 수를 줄일 수 있고 요청 한 번으로 여러 타입의 파일을 다룰 수 있지만 받아온 자원을 캐시하지 않는다는 단점

  • 프로그래밍 사례
    • 이중 평가를 피하라 - eval(), Function() 생성자, setTimeout(), setInterval() / eval()이나 Function()은 가능한 피하도록 해라. setTimeout()과 setInterval()은 함수를 권장
    • 객체/배열 리터럴을 사용하라 - 문법적으로는 문제가 없지만 리터럴이 더 빠르다
      • 리터럴을 사용하지 않고 객체를 만들고 할당하는 방법
        var obj = new Object();
        obj.name = "Michel";
        obj.math = 80;

        var arr = new Array();
        arr[0] = "Michel";
        arr[1] = 80;
      • 리터럴 (권장 - 더 빠름)
        var obj = {
            "name" : "Michel",
            "math" : 80
        };

        var arr = ["Michel", 80];
    • 작업을 반복하지 마라
      • 게으른 로딩 - 어떤 정보가 필요할 때까지 아무 일도 하지 않는다. 각 메서드를 처음 호출할 때 이벤트 핸들러를 붙이거나 제거하기 적합한 방법을 확인, 그 후 적합한 방법만 포함한 새 함수로 원래 함수를 덮어쓴다. 함수를 처음 호출할 때 마지막 단계는 새 함수에 원래 매개변수를 넘겨서 실행. (매우 흥미로운 방식이다!!! - 책 소스를 참조)
      • 조건에 따른 미리 읽기 - 감지 작업을 나중에 실행하지 않고 스크립트를 불러올 때 바로 실행하도록 바꾼 것. 페이지를 불러오자마자 함수를 실행하고 페이지 주기 동안 자주 실행할 때 최적
    • 빠른 부분을 이용하라
      • 비트 연산자
      • 내장 메서드




Posted by Mooki
,