웹사이트 최적화 (펌)

2013. 11. 19. 15:01 Posted by 초절정고수






웹사이트 최적화

웹사이트 성능과 최적화

제이콥 닐슨은 웹사이트의 반응 시간을 다음과 같이 평가한다.

  • 0.1초 - 사용자의 동작에 해당 기능이 바로 반응한다고 느끼는 시간
  • 1초 - 불필요하게 오래 기다리지 않았다고 느끼는 시간. 시간이 1초 이상 걸리면 컴퓨터의 동작에 이상이 생겼다고 생각하게 된다.
  • 10초 - 사용자가 집중력을 잃지 않는 최대 시간

제이콥닐슨은 웹페이지의 반응시간이 되도록 1초 이내여야 사용자 경험에 긍정적인 영향을 미친다고 분석했다.

브라우저 동작 방식을 기반으로 한 최적화

브라우저가 어떤 단계로 동작하는지, 단계별로 얼마나 시간이 걸리는 지 정의한 연구 활동이 W3C의 내비게이션 타이밍 명세다. 다음 그림은 내비게이션 타이밍 명세에서 브라우저가 사용자의 요청을 처리하는 순서를 정리한 프로세싱 모델에서 웹페이지 최적화와 관련 있는 단계를 정리한 것이다.


[브라우저가 사용자 요청을 처리하는 순서]
  • 서비스 이동 단계: 사용자가 웹 서비스를 이용하다 다른 주소로 이동할 때 브라우저가 제일 먼저 실행하는 단계다. 프로세싱 모델의 Prompt for unload에 해당한다.
  • 리다이렉트 단계: 사용자가 요청한 URL에서 다른 URL로 다시 보내는 단계다. 프로세싱 모델의 redirect에 해당한다.
  • 애플리케이션 캐시 확인 단계: 브라우저의 캐시에 데이터가 있는지 확인하는 단계다. 프로세싱 모델의 App Cache에 해당한다.
  • 네트워크 통신 단계: 브라우저가 네트워크와 통신해서 웹페이지와 구성요소를 다운로드하는 단계다. 네트워크 통신 단계에 해당하는 프로세싱 모델의 단계는 DNS, TCP, Request, Response 등이다.
  • 브라우저 처리 단계: 다운로드한 웹페이지와 구성 요소로 웹페이지를 화면에 그리는 단계다. 프로세싱 모델의 Processing과 onLoad에 해당한다.

| 서비스 이동 단계

사용자가 웹 서비스를 이용하다 다른 주소로 이동할 때 브라우저가 제일 먼저 실행하는 단계다. 다른 주소로 이동하기 전에 보고 있던 페이지에서 실행하는데, 브라우저 성능과 직결된다.

웹서비스를 이용하면 페이지가 표시될 때 우리도 모르게 이벤트가 할당되고 이때 메모리를 조금씩 사용한다. 그런데 이 메모리를 더 이상 사용하지 않을 때,즉 페이지를 떠날 때는 메모리를 해제해야 한다. 이 메모리 해제 작업이 서비스 이동 단계에서 실행하는 작업 가운데 하나다.

필요없는 메모리 해제를 담당하는 가비지 컬렉션 기능이 제대로 동작하지 않으면 브라우저가 응답 없음 상태가 되거나 실행 속도가 급격히 느려진다. 웹 페이지에서 동시에 많은 변수가 생성되고 처리되는 동안 브라우저에서 허용한 임계치를 넘었을 때 가비지 컬렉션이 동작하는데, 가비지 컬렉션이 동작하면 스크립트 실행이 중단된다. 가비지 컬렉션이 완료되기 전까지는 스크립트가 동작하지 못해 페이지가 느려지는 것이다.

서비스 이동 단계의 작업은 브라우저 내부에서 자동으로 실행한다. 만약 별도로 처리하려면 beforeunload 이벤트를 활용할 수 있다. beforeunload 이벤트를 활용하면 메모리 해제를 담당하는 모듈을 만들 수 있다.

서비스 이동 단계에서 일어나는 성능 문제를 개선하려면 필요 없는 변수나 객체를 삭제하고, 이벤트를 해제해 메모리를 관리해야 한다.

| 리다이렉트 단계

사용자가 요청한 URL에서 다른 URL로 다시 보내는 단계다. 쉰게 볼 수 있는 리다이렉트는 SNS에서 사용하는 단축 URL이다.

리다이렉트가 발생하면 상태 코드로 '301'이나 '302'를 반환한다. 리다이렉트가 발생하면 어떤 자원도 다운로드하지 않으며 브라우저에 일시적으로 빈 페이지가 보인다. 그렇기 때문에 의도하지 않게 리다이렉트가 발생한다면 바로 잡아야 한다.

리다이렉트 관련한 흔히 저지르는 실수 가운데 하나는 URL뒤에 슬래시(/)를 넣지 않아 302 redirect가 발생하는 것이다. 두번째 실수는 웹페이지를 이용한 리다이렉트다.

1.<meta http-equiv="refresh" content="1; url=http://www.naver.com/">

이 코드에는 두가지 성능 문제가 있다. 첫번째는 이 코드를 실행시킬 별도의 페이지를 거쳐야 리다이렉트된다는 점이다. 두번째는 최종 페이지에 도착했을 때 캐시가 설정된 리소스임에도 불구하고 조건부 GET 요청이 이뤄진다는 점이다. 조건부 GET 요청은 브라우저에 캐싱된 리소스를 사용하기 전에 해당 리소스를 사용해도 되는지 서버에 물어 보는 것이다. 이때 헤더의 If-Modified-Since 정보를 이용해 서버에 요청을 보낸다. 리소스가 수정되지 않았다면 '304 Not Modified' 코드를 받아 캐시에 있는 리소스를 사용한다. 자원이 수정됐다면 서버로부터 리소스를 다운로드 한다.

캐시의 만료 날짜를 설정했다면 서버의 확인을 거치지 않고 바로 캐싱된 리소스를 사용할 수 있는데, 메타태그로 리다이렉트하면 조건부 GET 요청으로 불필요한 서버 통신이 발생해 응답 속도가 느려진다.

웹 페이지의 주소뿐 아니라 이미지와 시타일시트, 자바스크립트와 같이 페이지를 구성하는 요소도 리다이렉트될 수 있다. HttpWatch로 301이나 302 상태 코드가 발생하는 요소들을 찾아서 바로 잡아야 한다.

| 애플리케이션 캐시 확인 단계

리다이렉트 작업을 마치고 HTTP 요청을 처리하기 위한 준비를 마쳤다면 브라우저는 맨 먼저 서버로 요청을 보낸다. 서버에서 응답이 오면 개별 요소(이미지, 스타일시트, 자바스크립트 등)가 사용자 PC에 있는지 캐시 데이터를 찾는다. 별도로 설정하지 않았다면 윈도우 운영체제에서는 C:\Documents and Settings\{User}\Local Settings\Temporary Internet Files 디렉터리에 캐시 데이터가 있다. 캐시 데이터의 종류에는 쿠키, 이미지, 스크립트, 스타일시트 등이 있다.

캐시 데이터가 있는 이유는 무엇보다도 사용자가 동일한 페이지를 다시 방문했을 때 브라우저와 서버 사이에 통신을 하지 않고 캐시에 있는 자원을 사용하겠다는 것이다. 다시 방문한 사용자에게 좀 더 빠른 응답 속도를 제공하려면 애플리케이션 캐시를 잘 활용해야 한다.

| 네트워크 통신 단계

프로세싱 모델의 DNS, TCP, Request, Response는 모두 네트워크 통신에 관련된 단계다. 네트워크 비용을 줄이는 첫번째 방법은 Expire 설정이나 Cache Control 속성을 이용해 사용자 웹 페이지에 다시 왔을 때 캐시를 사용하는 방법이다. 두번째 방법은 스타일시트나 자바스크립트와 같이 파일을 합쳐서 서비스해도 문제가 없는 리소스를 합쳐 하나의 링크로 제공해 요청 횟수를 줄이는 방법이다.

| 브라우저 처리 단계

프로세싱 모델의 Processing과 onLoad에 해당하는 브라우저 처리 단계는 서버에서 받은 HTML과 이미지, 스타일시트를 조합해 사용자가 실제로 보는 화면을 만드는 단계다. 서버에 요청한 요소가 모두 도착하면 브라우저는 DOM을 생성하기 시작한다. 그리고 DOM이 존재하는 그 시점에 DOMContentLoaded 이벤트나 onload 이벤트가 발생한다. 하지만 두 이벤트는 발생 시점이 다르다.

onload 이벤트는 DOM에서 기본적으로 제공하는 이벤트로 문서에 있는 모든 이미지, 스타일시트, 자바스크립트 등이 모두 다운로드될 때마다 발생한다. 이와는 달리 DOMContentLoaded 이벤트는 기본적으로 DOM 생성에만 관련돼 있다.즉, DOM이 로딩되고 난 직후에 발생한다.

많은 양의 이벤트를 바인딩해야 하고 이미지나 스타일시트의 개수가 많은 페이지를 개발한다면 onload 이벤트보다는 DOMContentLoaded 이벤트를 이용하는 게 좋다.

기본적인 웹 사이트 최적화 방법

HTTP 요청 최소화

HTTP 요청 최소화는 최적화에서 가장 기본이면서도 중요한 부분이다. 다운로드해야 하는 구성 요소의 개수를 줄이는 것은 가장 효과가 크고 중요한 최적화 방법이다.

CSS 스프라이트 기법 활용

이미지를 많이 사용하면서도 HTTP 요청을 최소화하는 방법 가운데 하나가 CSS 스프라이트 기법이다. CSS 스프라이트 기법은 이미지 여러 개를 하나로 만들고 스타일시트에서 background-position 속성을 설정해 필요한 부분의 이미지만 보여 주는 기술이다.

하나로 합친 CSS 스프라이트 이미지를 사용할 때는 다음 예제와 같은 형식으로 스타일 시트를 작성한다.

1./* CSS 스프라이트용 이미지를 설정한다. */
2..menu_list li a {background:url(http://example.com/some.png) no-repeat}
3. 
4./* background-position 속성의 왼쪽과 위쪽 기준을 설정해 보여 줄 이미지를 지정한다. */
5..menu_list li a.me {background-position:0 0;}   /*첫번째 아이콘*/
6..menu_list li a.me {background-position:0 -52px;}   /* 52픽셀 아래에 있는 두번째 아이콘 */

헤더에 만료 날짜 추가

헤더에 만료 날짜를 추가하는 이유는 웹페이지를 구성하는 이미지, 스타일시트 파일, 자바스크립트 파일 등을 사용자 컴퓨터의 캐시에 저장해서 재사용하기 위해서다.

만약, 만료 날짜 전에 수정사항이 있어 파일을 변경해야 한다면 파일 이름을 변경하거나 파일 이름 뒤에 쿼리스트링을 추가해 새로 추가된 파일임을 알려야 바로 반영된다.

1.//방법1: 파일 이름을 변경한다.
2.<script type="text/javascript" src="some_20120622.js"></script>
3. 
4.//방법2: 쿼리스트링을 추가한다.
5.<script type="text/javascript" src="some.js?20120622""></script>

브라우저에서 캐싱된 파일을 이용할지 서버에 요청할지 판단하는 기준은 파일이름과 인터넷 주소다. 그렇기 때문에 위와 같이 작업해서 파일 이름이나 파일의 주소를 바꾸지 않으면 계속같은 파일로 인식하고 사용자 컴퓨터에 있는 파일을 로딩한다.

자바스크립트 파일 통합

성능을 높이는 방법중 하나로 여러 개의 자바스크립트 파일을 하나의 파일로 합쳐 파일 개수를 최소화하는 것이다. 웹사이트의 성능을 개선할 때는 파일의 용량보다 파일의 개수가 더 중요하다. 웹페이지의 성능을 높이는 제일 좋은 방법은 파일의 개수를 줄여 HTTP 요청을 최소화하는 것이다.

파일크기 최소화

| Gzip 압축을 이용한 파일 크기의 최소화

점점 커지는 자바스크립트 파일과 스타일시트 파일의 크기를 줄이는 가장 효과적이고 쉬운 방법은 파일을 압축하는 것이다. 아파치 웹서버에서 파일을 압축하는 대표적인 인코딩 방식으로 Gzip 방식을 사용한다. 보통 이미지 파일은 이미 압축돼 있기 때문에 압축하지 않고, 스타일시트 파일과 자바스크립트 파일을 압축한다. 스티브 사우더는 파일 크기가 1~2KB 이상일 때 압축할 것을 권장한다. Gzip으로 압축해 전송하면 평균 70% 정도 파일 크기가 작아지는 효과를 볼 수 있다.

렌더링 성능 향상

렌더링 성능 향상의 목표는 페이지를 요청했을 때 사용자가 대기하는 시간을 최대한 줄여서 체감 속도를 높이는 것이다.

아래는 브라우저가 어떤 순서로 마크업을 파싱해서 화면에 보여주는지 기본적인 흐름이다.

  1. 1. HTML 파싱과 Dom트리 구성
    사용자가 페이지를 요청하면 네트워크를 통해 마크업을 받아 온다. 그리고 나서 마크업 문자열을 토큰 형태로 잘라서 트리를 구축하고 파싱 작업을 시작한다. 그런 다음 DOM 트리를 생성한다.
  2. 2. 렌더 트리 구성(DOM + 스타일 규칙)
    DOM 트리를 생성한 다음 바로 화면을 그리지 않는다. 스타일시트의 정보를 적용해야 하기 때문이다. DOM 트리 정보와 스타일시트의 스타일 규칙을 결합해 렌더트리(Render Tree)를 만든다. display:none 속성처럼 DOM 트리에는 있지만 화면에 보이면 안되는 요소를 걸러낸 결과가 렌더 트리다.
  3. 3. 렌더트리의 배치
    최종적으로 스타일규칙에 따라 각 요소를 화면의 어디에 배치할지 좌표를 설정한다.
  4. 4. 렌더트리 그리기
    요소의 좌표가 설정되면 브라우저에 순차적으로 화면을 그린다. 이때 사용자는 화면을 조금씩 보게 된다.

| 스타일시트와 자바스크립트 배치를 이용한 성능 향상

스타일시트 파일은 페이지 제일 위쪽에 놓고 자바스크립트 파일은 페이지 맨 아래쪽에 놓아야 한다. 브라우저 렌더링 단계에 따르면 사용자에게 화면을 보여 주기 전에 렌더 트리를 생성해야 하는데, 이때 스타일시트 파일이 반드시 필요하다. 스타일시트 파일을 최대한 빨리 다운로드해야 하는 이유다.

자바스크립트 파일을 페이지 아래에 놓아야 하는 가장 큰 이유는 파일을 다운로드해서 실행하기 전까지 브라우저가 DOM 파싱도 중지하고 아무것도 렌더링하지 않기 때문이다. 따라서 자바스크립트 파일은 </body> 태그 바로 위에 놓는 것이 좋다.

| 마크업 최적화

인터넷 익스플로러에서는 <table> 태그를 렌더링할 때 표안에 있는 텍스트와 이미지 등을 모두 파싱할 때까지 화면에 표를 그리지 않는다. 그러므로 페이지 전체의 레이아웃을 <table> 태그로 구성하는 것을 피해야 한다. 또한 전체 태그의 개수(보통 1000개 이하를 권장)를 줄이는 것도 중요하지만 중첩된 태그를 최소로 하는 것이 더 중요하다.

성능을 높이는 코드 스타일

객체의 생성, 초기화 성능

| 배열의 생성, 초기화 성능 비교

배열은 생성자 혹은 리터럴 형식([])을 사용해 객체를 생성할 수 있다.

1.// Array() 생성자를 사용한 배열 생성
2.var arr = new Array();
1.// 리터럴 형식으로 배열 생성
2.var arr = [];

리터럴 형식을 사용한 경우에 여러 브라우저에서 좀 더 좋은 성능을 보인다.

배열의 각 요소에 데이터를 할당하는 방법에는 접근자 []를 사용하는 방법과 push() 메서드를 사용하는 방법이 있다.

1.// 접근자를 사용한 데이터 할당
2.var arr = [];
3. 
4.for(var i=0;i<1000;i++){
5.arr[i] = i;
6.}
1.// push() 메서드를 사용한 데이터 할당
2.var arr = [];
3. 
4.for(var i=0;i<1000;i++){
5.arr.push(i);
6.}

크롬을 제외한 대부분의 브라우저에서 접근자를 사용한 데이터 할당이 push() 메서드를 사용한 데이터 할당보다 성능이 더 좋다.

최적화 방법
배열을 사용할 때는 리터럴 형식으로 객체를 생성하고 Array.push() 메서드보다는 접근자[]를 사용해 데이터를 추가하는 코드를 작성하는 것이 좀 더 최적화된 배열 사용법이다.

| 오브젝트(Object) 객체의 생성, 초기화 성능 비교

오브젝트 객체도 객체를 생성하고 초기화하는 방법으로 리터럴을 사용하는 방법과 생성자를 사용하는 방법이 있다.

1.// 리터럴을 사용한 오브젝트 객체 생성
2.var obj = {};
1.// 생성자를 사용한 오브젝트 객체 생성
2.var obj = new Object();

큰 차이는 없지만 리터럴을 사용하는 방법이 약간의 더 좋은 성능을 보인다.

오브젝트 객체를 초기화할 때는 .연산자를 이용한 방법과 []연산자를 이용한 방법이 있다.

01.// .연산자를 이용한 데이터 삽입
02.var obj = {};
03. 
04.obj.a = 1;
05.obj.b = 2;
06.obj.c = 3;
07.obj.d = 4;
08.obj.e = 5;
09.obj.f = 6;
01.// []연산자를 이용한 데이터 삽입
02.var obj = {};
03. 
04.obj["a"] = 1;
05.obj["b"] = 2;
06.obj["c"] = 3;
07.obj["d"] = 4;
08.obj["e"] = 5;
09.obj["f"] = 6;

safari를 제외한 대부분의 브라우저에서 비슷한 성능을 보였다. 단 safari에서는 .연산자가 더 좋은 성능을 보였다.

최적화 방법
배열에서와는 다르게 어느 한 쪽의 코드가 더 성능이 좋다고 말할 수 없을 정도로 비슷한 성능을 보였다.

스코프 체인 탐색과 성능

런타임 환경에서 자바스크립트의 실행 성능을 저해하는 요인이 변수, 객체, 함수 등의 메모리상의 위치를 찾는 탐색 작업이다.

| 스코프 체인이란?

함수를 실행하면서 변수, 객체 등에 접근해야 할 때 객체에 접근하기 위한 객체의 참조를 특정한 공간에 저장해 둔다. 이 공간이 바로 스코프 체인이다.

스코프 체인의 구성 요소에는 활성화 객체와 전역 객체가 있다. 함수 내부에서만 접근할 수 있는 함수의 지역변수나 this, arguments 객체 등은 스코프 체인의활성화 객체에 포함되며 함수 외부에서도 접근할 수 있는 window, document, 전역함수, 전역변수와 같은 속성은 스코프 체인의 전역 객체에 포함된다.

window, document 등의 전역객체는 웹페이지의 자바스크립트가 동작하는 모든 시간동안 존재하며, 함수 실행시 함수에서 전역속성을 탐색하는데 사용된다. 반면 활성화 객체는 함수가 실행되는 동안에만 존재하며, 함수 내부에서 자주 사용하는 데이터가 모여 있는 만큼 최우선으로 탐색하는 대상 객체가 된다.

실행문맥은 함수가 동작하는 환경을 나타내며, 브라우저 내부에서 사용되는 객체다. 실행문맥은 함수가 실행될 때 새로 생성되고 함수가 종료될 때 소멸되며 함수의 스코프 체인에 대한 참조를 가지고 있게 된다. 함수는 어떤 속성에 접근해야 할 때 실행문맥을 통해 스코프 체인에 접근한다.

실행문맥은 자신과 연관된 함수의 스코프 체인을 참조하고 있으며, 함수에서 접근해야 할 어떤 속성의 탐색 경로는 '실행문맥>스코프체인>활성화객체>스코프체인>전역 객체'와 같이 구성된다.

다음은 파라미터 값이 0인지 판별하는 isZero() 함수의 코드이다.

1.function isZero(num){
2.var res = (num === 0);
3.return res;
4.}
5. 
6.var result = isZero(0);

isZero() 함수에 있는 구문을 실행할 때 함수의 파라미터 변수인 num과 함수의 지역변수로 선언된 res에 대해 해당 변수의 메모리에 접근해야 하는데, 둘 다 활성화 객체에 포함돼 있는 속성이기 때문에 '실행문맥>스코프 체인>활성화 객체'의 경로로 탐색해 접근한다. 전역 객체는 탐색하지 않는다. 만약 window, document 등의 전역변수에 접근해야 한다면 '실행문맥>스코프 체인>활성화 객체>스코프 체인>전역 객체'의 경로로 속성을 탐색했을 것이다. 즉, 활성화 객체를 먼저 탐색한 후 찾는 속성이 없을 때는 스코프 체인에 참조돼 있는 다음 탐색 대상인 전역 객체를 탐색하게 된다.

만약 함수가 중첩될 경우에는 중첩이 깊어질수록 활성화 객체는 함수의 중첩된 깊이만큼 생성된다. 즉, 3번 중첩된 함수에서 가장 안쪽의 함수는 스코프 체인에 3개의 활성화 객체를 갖게 되는 것이다. 스코프 체인의 최상위에는 현재 실행 중인 가장 안쪽에 중첩된 함수의 활성화 객체를 참조하며, 그 뒤로 바깥쪽 방향으로 중첩된 함수의 순서대로 각 함수의 활성화 객체를 참조하게 된다. 그리고 마지막으로 전역 객체를 참조하게 된다.

이 경우가장 안쪽의 함수에서 전역 속성에 접근할 때는 '실행문맥>스코프체인>활성화객체1>스코프체인>활성화객체2>스코프체인>활성화객체3>스코프체인>전역 객체'와 같이 긴 탐색 경로를 거쳐야 한다. 이러한 탐색 경로를 줄임으로써 실행 시간을 단축하고 자바스크립트 성능을 향상시킬 수 있다.

| 지역변수를 활용한 스코프 체인 탐색 성능 개선

첫 번째로 탐색하는 활성화 객체에 찾고자 하는 속성이 있는 경우 추가로 발생할 수 있는 다른 활성화 객체, 전역 객체를 탐색하는 과정을 줄여 성능을 향상시킬 수 있을 것이다.

01.// 함수 내에서 전역 스코프 변수에 직접 접근하는 예제
02.window.htmlstring = [];
03. 
04.function makeList(){
05.htmlstring.push("<ul>");
06.for(var i=0;i<100;i++){
07.htmlstring.push("<li>value: " + i + "</li>");
08.}
09. 
10.htmlstring.push("</ul>");
11.}
12. 
13.makeList();

makeList() 함수가 실행되면 함수 내부에서 htmlstring, i 속성에 접근하기 위해 스코프 체인을 탐색한다. 이때 htmlstring 객체를 찾기 위해 활성화 객체에 먼저 접근해서 탐색하지만 찾지 못하고, 다시 전역 객체를 탐색해서 찾아야 한다. 다음과 같이 코드를 수정해 성능을 높일 수 있다.

01.// 함수 지역변수로 참조해 전역 스코프 변수에 접근하는 예제
02.window.htmlstring = [];
03. 
04.function makeList(){
05.var htmlstr = htmlstring;
06.htmlstring.push("<ul>");
07.for(var i=0;i<100;i++){
08.htmlstring.push("<li>value: " + i + "</li>");
09.}
10. 
11.htmlstring.push("</ul>");
12.}
13. 
14.makeList();

위 코드에서 var htmlstr = htmlstring; 부분이 성능 개선의 핵심이다. 전역객체에 존재하는 htmlstring 속성을 makeList() 함수의 지역변수에 저장해 활성화 객체에서 바로 찾을 수 있게 한 것이다. 물론 var htmlstr = htmlstring; 구문을 실행할 경우 최초 한 번만 '실행문맥>스코프 체인>활성화 객체>스코프 체인>전역 객체'와 같은 탐색경로를 거치지만 그 이후에는 활성화 객체에 저장된 htmlstr 속성으로 전역변수인 htmlstring 객체에 접근할 수 있으므로 활성화객체를 거쳐 전역 객체까지 탐색할 필요가 없어진다.

최적화 방법
전역객체에 속하는 window, document, 전역함수, 전역변수와 같은 속성은 함수의 지역변수로 저장해 탐색경로를 줄여 성능을 향상 시킬 수 있다.

| 프로토타입 체인

자바스크립트의 모든 객체의 인스턴스는 new 연산자로 생성할 수 있으며, 생성된 인스턴스 객체는 생성자의 프로토타입(prototype)을 참조하게 된다.

1.var obj = new Object();     //obj - 인스턴스 객체, Object - 생성자 함수

인스턴스 객체가 원본 객체 생성자 함수의 프로토타입 속성을 탐색할 때도 탐색을 위한 체인이 생성되는데, 이를 프로토타입 체인이라 한다.

위 코드에서 Object는 자신의 프로토타입을 참조하며, var obj = new Object(); 구문이 실행되면 obj는 Object의 프로토타입을 상속 받는다. 이 과정에서 탐색 경로가 길어질 수 있으며, 탐색 경로의 거리에 따라 프로토타입 체인에서도 스코프 체인에서와 같은 성능 저하가 발생할 수 있다. 그러므로 프로토 타입에 존재하는 속성을 사용할 때 지역변수에 담아서 사용한다면 불필요한 탐색 과정을 줄여 성능을 높일 수 있다.

| 그 외 스코프 체인 탐색 성능에 영향을 미치는 요소

with 구문과 try-catch 구문은 스코프 체인 탐색에서의 성능 저하가 발생할 수 있으므로 되도록 사용을 자제한다.

반복문과 성능

| 반복문의 성능 비교

아래는 배열을 초기화하는 코드와 성능 비교를 위한 4개의 반복문을 테스트하는 코드이다.

1.//배열을 초기화하는 코드
2.arr = [];
3.for(var i=0;i<400;i++){
4.arr[i] = i; 
5.}
1.//Code1 - for 구문
2.for(var i=0, len=arr.length;i<len;i++){
3.arr[i]++;   
4.}
1.//Code2 - for in 구문
2.for(var i in arr){
3.arr[i]++;   
4.}
1.//Code3 - while 구문
2.var i=0, len = arr.length;
3.while(i<len){
4.arr[i]=i;
5.i++;
6.}
1.//Code4 - do while 구문
2.var i=0, len = arr.length;
3.do{
4.arr[i]=i;
5.i++;
6.}while(i<len);

테스트는 다음 주소에서 확인할 수 있다. http://jindo.dev.naver.com/jsMatch/index.html?d=35

결과를 보면 for-in 구문은 IE 이외의 브라우저에서는 두드러질 정도로 성능이 좋지 않다. 빠른 응답시간을 구현하려면 되도록 for-in 구문을 사용하지 않는 것이 좋다.

for-in 구문은 인자로 주어진 배열을 배열이 아닌 일반 객체로 취급하며, 반복시점마다 객체의 모든 속성을 무작위로 탐색하여 현저하게 느려진다.

for-in 구문은 그 목적 자체가 객체의 속성을 탐색하는 것이다. 그런 이유로 모든 속성이 순차적으로 정렬돼 있어 선형적인 색인으로 접근할 수 있는 배열보다는 속성의 이름이 제각각이라 색인으로는 접근할 수 없는 객체의 속성을 탐색하는 데만 사용하길 권장한다.

| for, while, do-while 구문의 최적화

다음은 배열의 모든 요소를 탐색하는 다양한 형식의 반복문이다.

1.//for구문을 이용한 배열 탐색
2.var arr=[...];
3.for(var i=0;i<arr.length;i++){
4....
5.}
1.//while구문을 이용한 배열 탐색
2.var arr=[...];
3.var i=0;
4.while(i<arr.length){
5....
6. 
7.i++;
8.}
1.//do while구문을 이용한 배열 탐색
2.var i=0;
3.do{
4....
5. 
6.i++;
7.}while(i<arr.length);