본문 바로가기
개발일기/프론트언어

[HTML5] 브라우저에서 HTML이 처리되는 과정

by 프로그래머콩 2019. 7. 4.

그림 한큐로 설명




아래는 자세히...


브라우저에서 네트워크를 처리하는 컴포넌트가 서버와 통신을 통해서 데이터를 가져옵니다.
이렇게 가져온 HTML 데이터는 분석 과정을 거치게 되는데, 이 과정을 파싱이라하고, 이결과를 화면에 그려주는 작업을 렌더링 이라고 합니다.


웹 브라우저에서 UI 백엔드는 운영체제간의 통신을 담당합니다.




 - DOM트리를 구성한다는것 : HTML데이터를 메모리로
이과정은 마크업 언어로 만들어진 문자열을 브라우저가 메모리에서 다루기 위해서 HTML 파서로 파싱하고 결과를 로딩 한다는 것. 메모리에 만들어진 객체를 document라고 하고, 완성된 구조를 DOM이라고 함. DOM트리 예시는 그림에...
(HTML은 구조를 가지는 데이터이므로 메모리에서는 이 구조를 표현하는 데 적합한 트리구조의 DOM을 만들어 내는 것.)


- HTML 데이터를 화면에 그릴수 있도록 : 렌더트리
HTML이 파싱되는 동안 브라우저가 하는 또 다른 작업은 실제로 화면을 구성하는 렌더링 엔진을 통해서 화면에 나오는 구조를 만들어 내는 것. 이렇게 만들어지는 구조를 렌더트리 라고 함.
DOM이 완성되면 이것을 바로 화면에 보여주는 것이 아니라, 렌더링 엔진이 화면을 구성할 때 빠르게 하기위해 HTML과 CSS를 고려하여 현재 화면에 나와야 하는 것만을 선별해서 화면에 뿌려주어야함. 
결과적으로(쉽게말하자면) HTML로 만들어진 모든 데이터는 메모리에 로딩되어 사용되는데, 이것을 DOM이라 부르고, 화면에 보이는 요소들만 추려서 만들어진 부분을 렌더 트리라 부름.


-TIP : CSS가 HTML 문서의 위쪽에 있어야 하는 이유
CSS를 이용해 렌더링 엔진이 화면에서 어떤 방식으로 처리하는가 기준을 빨리 제공할수록 렌더 트리를 구성하기가 쉬워지기 때문.
브라우저가 전달받는 HTML은 항상 맨 위에서부터 차례대로 처이됨. 따라서 렌더링 엔진이 참고할 수 있는 부분을 위쪽에 두게 되면 화면에서의 '선별 기준'을 미리 파악할 수 있음. 실제로 렌더링 엔진이 본격적으로 작업하는 부분이 바로 화면에 나오는 <body> 태그이기 때문에 특별한 경우가 아니라면 <body> 태그가 나오기전에 CSS를 지정하는 것이 일반적.


-레이아웃 처리와 페인팅
렌더링 엔진은 DOM이 구성되는 것과 거의 동시에 작업을 실행.(조금이라도 빠르게 보여주기 위한 노력이라고 생각될 수 있음). 화면에 보여질 요소들을 파악한 렌더트리는 이제 CSS를 이용해 화면의 어떤 위치에 보여주어야 하는지를 결정하는데, 이작업이 레이아웃을 구성하는 작업.


렌더링 엔진은 구성된 렌더트리를 이용해서 화면에 그려주는 작업을 진행함.------>화면을 구성하는 그림판같은개념


렌더링 엔진은 렌더 트리를 구성할 때 CSS의 참견을 받는데, 이런 과정을 통해 HTML은 완전히 다른 형태의 페이지를 보여줄 수 있음. CSS의 참견이 많고 세밀 할수록 다양한 변화가 가능하지만, 여기에는 브라우저마다 CSS를 처리하고 사용하는 속성(Property)이 다르다는 나쁜 소식도 있음.


- 자바스크립트는 언제 해석되고 실행되는가?
간략한예로 브라우저는 HTML 데이터를 만나면 HTML 파서를 이용해서 파싱을 함.  앞서 말한것과 같이 DOM트리와 렌더 트리를 구성하는 작업도 같이 실행됨. 여기서 <script>문을 추가했다고 가정.
HTML데이터는 DOM트리로 구성되어 나중에 처리되는 반면에 <script>태그의 내용은 바로 해석(Interpret)되어 실행(Execute)됨(이는 자바스크립트 해석기가 일을 처리함). 자바스크립트 엔진은 <script>태그를 만나면 안에 있는 내용을 그즉시 해석하고 실행함.


<body>
<script>
alert("AAAA");
</script>
BBBB


</body>


와 같은 태그가 있다고 가정하면, 실행시켜보면 BBBB라는 문자열이 아직 출력되지 않은 상태에서 경고창("AAAA")이 먼저 실행됨.


경고 창이라는 장치는 일종의 블로킹형태와 같이 동작함. 이 코드는 경고창을 화면에 띄우면 사용자가 블록 상태를 해제하기까지(경고창을 닫을때까지) 브라우저는 HTML 나머지 처리를 멈추게됨(다만, 브라우저의 창은 운영체제에 의해서 제어되기때문에 브라우저의 크기변경이나 이동 등은 무관)


- 자바스크립트가 코드의 마지막 부분에 위치하게 된 이유
예전에는 CSS와같이 HTML의 위쪽에 두는 것이 일반적이었지만 요즘은 하단에 위치함.
자바스크립트는 그 실행 자체가 모든 작업을 멈추고 처리를 하는 방식으로 동작하기 때문에 가능하면 DOM이나 렌더 트리 구성을 최대한 진행한 다음에 자바스크립트를 진행하는 컴포넌트가 동작하도록 하는 것.


JQuery를 이용하는 경우에 $(document).ready() 메서드를 이용하는 것이 DOM 구성이 끝난 상태에 이벤트를 감지하고 동작해서 자바스크립트의 실행으로 인한 성능 저하를 막으려는 것과 비슷한 맥락으로 이해할 수 있음.




- TIP2 : CSS3와 자바스크립트
(  결론  : 속도 =  CSS > javascript, 이유는 ? javascript를 이용할때보다 css3를 이용할때 브라우저 내부에서 거치는 과정이 적기때문 ) 
자바스크립트를 이용해 HTML의 요소를 화면상에서 이동해야하는경우 ->CSS3와 접촉 (1)
CSS3자체적으로 애니메이션 효과를 사용 -> javascript와 CSS3 접촉 X                     (2)
(1)의 경우는 자바스크립트 엔진이 로직을 처리하고 렌더링 엔진에 처리를 부탁하는 구조. 문제는 이때 브라우저 엔진 내부의 의사소통에 생각보다 많은 자원을 사용하게 된다는 것.
(2)의 경우는 자바스크립트를 이용해서 동작하는 것 보다 자원의 소모를 줄여줄 수 있음. 또한 렌더링 엔진들은 CSS3의 경우 GPU를 통해서 동작할 수 있기 때문에 더 나은 성능을 기대할 수 있음.




그림구성 : 나
참고 도서 : 상상력과 HTML5,CSS3, JavaScript로 빚는 모바일웹/출판사 프리렉/강요천 지음








+ 추가




Best Practices for Speeding Up Your Web Site
웹 사이트 속도 개선 방법


출처 - 야후 개발자 페이지(https://developer.yahoo.com/performance/rules.html)
Best Practices for Speeding Up Your Web Site - Yahoo Developer Network

developer.yahoo.com

Best Practices for Speeding Up Your Web Site웹 사이트 속도 개선 방법

야후 개발자 페이지에 있는 내용을 번역한 것이고, 굵게 표시된 몇가지 항목은 yslow로 측정할 수 있습니다. 웹 페이지가 브라우저에 어떻게 로드되어 클라이언트에 구성되고 동작하는지 이해를 하면 어떻게 구성하는게 웹 페이지 속도가 빠른지 생각하기 쉽겠죠.
Make Fewer HTTP Requests
Use a Content Delivery Network (CDN) 
Add Expires or Cache-Control Header 
Gzip Components 
Put Stylesheets at the Top
Put Scripts at the Bottom 
Avoid CSS Expressions 
Make JavaScript and CSS External 
Reduce DNS Lookups 
Minify JavaScript and CSS
Avoid Redirects  
Remove Duplicate Scripts 
Configure ETags 
Make Ajax Cacheable 
Flush the Buffer Early 
Use GET for AJAX Requests
Postload Components 
Preload Components 
Reduce the Number of DOM Elements 
Split Components Across Domains
Minimize the Number of iframes 
Avoid 404s 
Reduce Cookie Size 
Use Cookie-free Domains for Components
Minimize DOM Access 
Develop Smart Event Handlers
Choose <link> over @import
Avoid Filters 
Optimize Images 
Optimize CSS Sprites 
Don't Scale Images in HTML 
Make favicon.ico Small and Cacheable 
Keep Components under 25K 
Pack Components into a Multipart Document
Avoid Empty Image src 




1. HTTP 요청을 최소화하라
tag: content
사용자(End-user) 응답시간의 80%는 초기 로딩부분에서 소요된다. 그 중 대부분의 시간은 모든 페이지 구성요소(images, syltesheets, scripts, Flash 등)의 다운로딩에 소요된다. 구성요소의 수를 줄이면 페이지를 구성하는데 필요한 HTTP 요청 수를 줄일 수 있다. 이것은 페이지의 속도를 빠르게 만드는 핵심요소이다.
페이지의 구성요소를 줄이기 위한 한 가지 방법은 페이지의 디자인을 단순화하는 것이다. 그러나 빠른 응답 시간을 가지면서 풍부한 콘텐츠가 포함된 페이지를 구축할 수 있는 방법은 무엇일까? 여기에 풍부한 구성요소가 포함하면서, HTTP요청의 수를 감소시킨 페이지를 구축할 몇 가지 방법이 있다.
Combined files(파일결합)은 모든 scripts를 하나의 script로 결합시키고 모든 CSS를 하나의 stylesheet로 결합하여 HTTP 요청 수를 줄이기 위한 하나의 방법이다. Scripts와 stylesheets가 모든 페이지에 걸쳐 다양하게 있다면 파일결합은 어려운 작업이 되겠지만 해당 부분을 수정한다면 분명 응답시간을 향상시켜줄 것이다.
CSS Sprites는 이미지 요청 수를 줄이기 위해 선호되는 방법이다. 배경 이미지들을 하나의 이미지로 결합하고 이미지를 표현하는 속성인 CSS background-image 또는 background-position을 사용하는 것이다. 
Image maps는 여러 개의 이미지를 하나의 이미지로 결합시키는 방법이다. 전체 크기는 동일하지만, HTTP 요청 수를 줄이는 것은 페이지의 속도를 향상시켜주기 때문이다. Image maps는 메뉴바(navigation bar)와 같이 페이지에서 이미지들이 인접할 때에만 효과적이다. 하지만 Image maps의 좌표처리방법은 표현법이 지루하고 오류가 발생하기 쉽다. 또한 메뉴바에 대한 Image maps사용은 접근성이 떨어지므로 그렇게 권장하지는 않는다.
Inline images는 실제 페이지에서 이미지 데이터를 포함시키기 위한 데이터(URL 체계)를 사용하는 방법이다. 이것은 HTML 문서의 크기를 증가시킬 수 있다. Inline image들을 stylesheets(cached)에 결합하는 것은 HTTP 요청을 줄이고 페이지 크기의 증가를 피할 수 있는 하나의 방법이다. 현재 Inline image들은 아직 모든 주요 브라우저들에서 지원되지 않는다.
위에 열거한 방법이 페이지의 HTTP 요청 수를 줄이는 것은 가장 기본적인 단계이다. 이것은 첫 방문자에게 성능개선을 제공하기 위한 가장 중요한 지침이다. Tenni Theurer의 블로그에 포스팅된 ‘Browser Cache Usage-Exposed!’(브라우저 캐시 사용)과 마찬가지로, 40-60%의 일일 사이트 방문자들은 비어있는 캐시의 상태로 사이트를 방문한다. 이렇게 첫 방문자를 위해 페이지의 속도를 빠르게 만드는 것은 더 나은 사용자 경험(UX)을 제공하기 위한 핵심요소이다.


2. CDN을 사용하라
tag: server
웹서버에 대한 사용자의 접근성은 응답시간이 가장 중요하다. 지리적으로 분산된 서버에 콘텐츠를 배포하면 사용자의 관점에서 빠르게 페이지를 읽어올 수 있을 것이다.
지리적으로 분산된 서버에 콘텐츠를 구현하기 위한 첫 번째 단계는 분산 아키텍처에서 작동시키기 위해 웹 응용프로그램을 재설계하면 안 된다. 응용프로그램에 따라 아키텍처를 변경하면 서버위치를 통해 실시간 동기화되는 세션상태와 데이터베이트 트랜젝션을 복제하는 힘든 작업도 포함시킬 수 있다. 사용자와 컨텐츠 사이의 거리를 줄이기 위한 시도는 이 단계에서 작업지연 및 실패로 이어질 수 있다.
사용자 응답시간의 80-90%는 페이지의 모든 구성요소(images, syltesheets, scripts, Flash 등)를 다운로딩 하는데 소요된다는 것을 기억하라. 이것은 모든 작업에 있어 황금률이다. 응용프로그램 아키텍처를 재설계하는 어려운 작업보다 정적 콘텐츠를 분산시키는 것이 더 효과적이다. 이것은 응답시간을 크게 감소시키고 CDN(content delivery networks)의 구성을 더 쉽게 만들기 때문이다.
CDN은 사용자에게 보다 효율적으로 컨텐츠를 제공하기 위해 여러 지역에 걸쳐 분산된 웹서버의 집합체이다. 특정 사용자에게 컨텐츠를 전달하기 위해 일반적으로 고객의 네트워크에서 가장 가까운 서버를 측정하여 선택된다. 예를 들어,고객이 요청 시에 가장 적은 네트워크 홉(network hops)과 가장 빠른 응답시간의 서버가 선택된다.
일부 대형 인터넷 업체들은 자체 CDN 서비스를 소유하지만, CDN 서비스를 전문적으로 제공하는 업체를 이용하는 것은 비용 면에서 효과적이다. 벤처기업이나 개인 웹사이트의 경우, CDN 서비스 비용은 매우 비싸지만 타겟 고객이 점점 더 커지고 글로벌화 되어감에 따라 CDN 서비스는 빠른 응답시간을 제공하기 위해 반드시 필요한 부분이다. CDN으로의 전환은 웹사이트의 속도를 급격히 향상시킬 수 있는 비교적 쉬운 방법이라고 할 수 있다.


3. Expires 혹은 Cache-Control를 해더에 추가하라
tag: server
이 규칙에는 두 가지 관점이 존재한다.
정적 components: Expires header의 미래 설정 시점까지의 “Never expire” 정책 수행
동적 components: 조건적 요청의 브라우저를 수행하기 위한 적절한 Cache-Control header의 사용
웹 페이지에 더 많은 scripts, stylesheets, images, Flash를 사용하는 것은 웹페이지 디자인을 더욱 더 풍부하게 만들 것이다. 페이지를 처음 방문하는 사람이 얼마나 많은 HTTP 요청들을 수행해야 할지 모르지만 Expires header를 사용함에 따라 임시저장 가능한 구성요소(components cacheable)를 만들 수 있다. 이것은 이후 페이지뷰에 대한 불필요한 HTTP 요청을 방지한다. Expires headers는 대부분 이미지와 함께 사용하지만, scripts, stylesheets, Flash 등의 모든 구성들에 사용되어야 한다.
브라우저는 웹 페이지 로딩속도를 빠르게 하고 HTTP 요청 수 및 크기를 줄이기 위해 캐시(cache)를 사용한다. 웹서버는 고객에게 구성요소(component)를 얼마나 오래 캐시에 담아둘 것인지를 알려주기 위해 HTTP 응답에 Expires header를 사용한다. 다음은 이 응답이 2010년 4월 15일까지 유효한 브라우저를 알리는 미래 시점의 Expires header이다.
Expires: Thu, 15 Apr 2010 20:00:00 GMT
만약 서버가 Apache라면, 현재 날짜를 기준으로 만료일을 설정하는 ExpiresDefault 지시어를 사용하여라. 다음 예제는 요청시간으로부터 10년을 만료일로 설정해준다.
ExpiresDefault “access plus 1 years”
미래시점의 Expires header를 사용하면 사용자가 이미 사이트를 방문한 후에만 페이지뷰에 영향을 끼친다. 사용자가 처음 사이트를 방문하여 브라우저의 캐시가 비어있는 경우에는 HTTP 요청개수에 영향을 주지 않는다. 따라서 이 성능향상은 사용자가 얼마나 자주 준비된 cache(클라이언트 캐쉬)를 가지고 페이지를 열람하는가에 따라 달라진다. 미래시점의 Expires header를 사용함으로써, 사용자의 인터넷 접속을 통한 단일바이트의 전송없이 브라우저에 의해 캐시되며 이후 페이지방문 시 재사용된 구성요소들의 수를 증가시킬 수 있다.


4. Gzip으로 압축하라
tag: server
네트워크를 통해 HTTP 요청과 응답을 전송하는데 걸리는 시간은 초반 로딩을 설계하는 엔지니어의 결정에 의해 크게 줄일 수 있다. 고객의 대역폭 속도, ISP(Internet service provider), peering 교환(ISP간의 트래픽 교환 방식) 지점과의 접근성 등은 개발팀의 통제를 벗어난 요소들이다. 하지만 응답시간에 영향을 주는 다른 변수들이 있다. 그 중 하나인 압축은 HTTP 응답의 크기를 줄임으로써 응답시간을 줄일 수 있는 하나의 방법이다.
HTTP/1.1로 시작하는 웹클라이언트는 HTTP 요청에서 Accept-Encoding header를 가진 압축지원을 나타낸다.
Accept-Encoding: gzip, deflate
만약 웹서버가 요청에서 이 header를 본다면, 고객에 의해 작성된 방법 중 하나를 사용하여 응답을 압축할 수 있을지도 모른다. 웹서버는 요청에서 Content-Encoding header를 통해 웹클라이언트에게 알려준다.
Content-Encoding: gzip
Gzip은 현재 가장 대중적이고 효과적인 압축방법이다. 이것은 GNU 프로젝트와 RFC 1952에 의해 표준화된 개발법이며 유일한 다른 압축형식은 deflate이지만, 이것은 덜 대중적이고 효과적이지 못하다.
일반적으로 gzipping은 약 70%까지의 응답크기를 줄일 수 있다. 오늘날 인터넷 트래픽의 약 90%는 gzip을 지원하는 브라우저들를 통해 이동되며 만약 Apache를 사용한다면 gzip에 설정된 모듈은 서버의 버전에 따라 결정되기도 한다. 예를 들어 Apache 2.x는 mod_deflate를 사용하지만 Apache 1.3은 mod_gzip을 사용한다.
브라우저와 프록시에 관해 알려진 몇 가지 문제점은 브라우저가 예상한 것과 압축된 콘텐츠와 관련해 받은 무언가에서 불일치를 야기할 수도 있다는 것이다. 다행히도 이런 문제는 오래된 브라우저 사용이 감소함에 따라 줄어들고 있다. 또한 Apache 모듈은 적절하고 다양하게 응답하는 headers들을 자동적으로 추가해서 도움이 될 수 있다.
서버는 파일유형에 따라 압축대상을 선택할 수 있지만 일반적으로 이 결정에는 많은 제약이 따른다. 대부분의 웹사이트들은 자신들의 HTML 문서를 압축한다. 그 압축툴은 scripts와 stylesheets를 압축하는 데에도 매우 유용한 것들이지만 많은 웹사이트들은 이 기회를 놓치고 있다. 이미지와 PDF 파일은 이미 압축이 되어있기 때문에 압축할 필요가 없지만 XML이나 JSON을 포함한 텍스트 응답을 압축하는 데에는 유용하다. CPU를 낭비할 뿐 아니라 잠재적으로 파일크기를 증가시킬 수 있기 때문이다.
가능한 많은 파일 형식들을 압축하는 것은 페이지 무게를 줄이고 사용자 경험(UX)을 촉진하는 가장 쉬운 방법이다


5. 상단에 스타일시트를 넣어라
tag: CSS
문서의 HEAD에 stylesheets를 이동하면 페이지 로딩속도가 빨라지는 것을 알 수 있다. 이것은 HEAD에 stylesheets를 놓는 것이 순차적으로 페이지를 로딩하도록 만들어 주기 때문이다.
성능에 관심이 있는 구성요소 엔지니어들은 점진적으로 페이지를 읽어오길 원한다. 즉, 브라우저는 가능한 한 빨리 어떠한 내용이라도 표시할 수 있기를 원한다. 이것은 많은 콘텐츠를 가진 페이지와 느린 인터넷 접속 사용자에게 특히 중요하다. 사용자에게 진행률 표시기(progress indicator)와 같은 시각적 피드백 제공의 중요성은 연구와 함께 잘 입증되어 왔다. 브라우저가 페이지를 점진적으로 읽어올 때, 상단, 메뉴바(navigation bar), 로고 등은 페이지를 기다리는 사용자에게 시각적 피드백을 제공하는 역할을 하고 이것은 전반적인 사용자경험(UX)을 향상시킨다.
문서 하단에 stylesheets를 놓으면 IE를 포함한 많은 브라우저에서 점진적으로 페이지를 불러오지 못하는 문제가 있다. 이런 브라우저들은 스타일이 변경되면 페이지의 요소들을 다시 그려야하는 것을 방지하기 위해 렌더링을 막고 있다. 마치 사용자는 빈 페이지를 보게 될 것이다.
HTML표준은 명확하게 stylesheets가 페이지의 Head에 포함할 것을 기술한다. "[LINK]는 여러번 나타나더라고 문서의 head에만 있어야한다." 어떠한 대안도 빈 화면과 정의되지 않은 콘텐츠의 flash 등의 위험을 감수할 가치가 없다. 최적의 솔루션은 HTML표준에 따라 문서의 Head에서 stylesheets를 읽어오는 것이다.


6. 하단에 스크립트를 넣어라
tag: javascript
scripts로 야기될 수 있는 문제는 바로 병렬 다운로드를 방해한다는 것이다. HTTP/1.1 특징은 브라우저가 호스트 별로 두 개 이하의 요소를 병렬 다운로드 하지 않도록 구성할 것을 제시한다. 만약 여러 호스트에서 이미지를 제공한다면 병렬(parallel)로 두 개 이상의 다운로드를 받을 수 있다. 그렇지만 위의 문제로 인해 스크립트가 다운로드 되는 동안 브라우저는 서로 다른 호스트 이름이라 하더라도 다운로드를 시작하지 않을 것이다.
어떤 상황에서 하단에 scripts를 이동하는 것은 쉽지 않다. 예를 들어, 만약 script가 페이지 내용의 일부를 삽입하기 위해 document.write를 사용한다면, 페이지의 하단으로 이동할 수 없다. 이것은 많은 문제점을 남기지만, 대부분의 경우 이런 상황에서의 차선책을 가지고 있다.
종종 대안으로 제시되는 것은 지연된(deffered) 스크립트를사용하는 것이다. DEFER의 속성은 스크립트가 document.write를 포함하지 않음을 나타내고, 브라우저가 렌더링을 계속할 수 있는 정보를 제공한다. 하지만, Firefox는 DEFER 속성을 지원하지 않는다. IE에서 사용할 수 있지만, 만족스럽지는 않을 것이다. 만약 스크립트를 지연시킬 수 있다면, 페이지 하단으로의 이동도 가능할 것이다. 그러면 웹페이지 로드 속도를 빠르게 만들어줄 것이다.


7. CSS의 표현을 피하라
tag: css
CSS expressions는 동적으로 CSS 속성을 설정하는 위험하지만 강력한 방법이다. IE5부터 지원되지만, IE8로 시작되는 버전에서는 사용하지 않을 것을 권장한다. 예를들어, 배경색상은 CSS expressions를 사용하여 매시간 다른 설정이 가능하다.
background-color:expression((new Date()).getHours()%2 ? "#B8D4FF":"#F08A00" );
위와 같이, 표현방법은 JavaScript 방식을 사용한다. CSS 속성은 JavaScript 방식을 처리한 결과값으로 설정한다. 표현방법은 다른 브라우저에 의해 무시되므로, 여러 브라우저를 통해 일관된 표현 생성을 필요로 하는 IE의 속성을 설정하는 것은 유용하다.
Expressions의 문제는 CSS가 대부분 사람들이 생각한 것보다 더 자주 사용된다는 것이다. 페이지를 읽어오거나(render) 크기를 재설정(resize) 또는 스크롤 하거나 심지어 사용자가 마우스로 페이지를 이동할 때에도 사용된다. CSS expression에 카운터(counter)를 추가하면 CSS expression이 사용되는 빈도를 파악할 수 있다. 페이지 주변을 마우스로 이동하는 것으로 10,000번 이상이 사용될 수 있다.
CSS expression의 사용횟수를 감소시키는 방법 중 하나는 뚜렷한 값(CSS expression으로 대체되는)으로 스타일 속성을 설정할 수 있는 expression을 한 번 사용하는 것이다. 만약 스타일 속성을 페이지 전반에 걸쳐 동적으로 설정해야 한다면, CSS expressions 대신에 이벤트 헨들러를 사용하는 것이 대안이 될 수 있다. 반드시 CSS expressions를 사용해야 한다면, 수천 번 이상 사용될 수 있으며 페이지 성능에 영향을 줄 수도 있다는 사실을 기억해야 한다.


8. JavaScript와 CSS는 외부 파일로 만들어라
tag: javascript, css
성능 규칙들의 대부분은 외부 컴포넌트가 어떻게 관리되는지를 다룬다. 그러나 이전에 좀 더 기본적인 질문이 필요하다. JavaScript와 CSS를 반드시 외부 파일로 만들어야 하는지 또는 페이지 내에서 인라인 되어야하는가?
외부 파일을 사용하면 일반적으로 빠른 페이지를 생성한다. 왜냐하면 JavaScript와 CSS 파일은 브라우저에 의해 캐시되기 때문이다. HTML 문서에 인라인된 JavaScript와 CSS는 HTML 문서를 요청할 때마다 다운로드 된다. 이것은 필요한 HTTP 요청 수를 감소시키지만 HTML 문서의 크기를 증가시킨다. 반면, external files에 있는 JavaScript와 CSS가 브라우저에 의해 캐시된다면, HTML 문서의 크기는 HTTP 요청수의 증가 없이 감소될 것이다.
핵심요소는 external JavaScript와 CSS components가 요청된 HTML 문서 수에 비례해 캐시되는 빈도이다. 이 요소는 측정하기 어렵지만, 다양한 측정방법을 사용하여 계량화할 수 있다. 만약 사용자가 사이트에서 세션당 여러 개의 페이지뷰를 갖고, 많은 페이지가 동일한 스크립트와 styelsheets를 재사용한다면, 캐시된 외부 파일을 통해 장점을 얻을 수 있다.
많은 웹사이트들은 이러한 척도 중간에 있다. 이러한 사이트의 경우, 최적의 솔루션은 일반적으로 JavaScript와 CSS를 외부 파일로서 배포하는 것이다. 인라인이 더 좋을 수 있는 예외로 Yahoo의 프론트 페이지와 My Yahoo! 같은 홈페이지가 있다. 세션당 페이지뷰가 없거나 혹은 한 개를 가진 홈페이지는 인라인된 JavaScript와 CSS 가 최종 사용자 응답 시간을 더 빠르게 할 수 있다.
일반적으로 많은 페이지뷰의 처음에 오는 프론트 페이지에 대해 외부 파일을 사용한 캐시 장점을 이용하는 것 말고도 HTTP 요청 감소에 영향을 주는 여러 기술들이 존재한다. 그 중 한 가지는 프론트 페이지에 JavaScript와 CSS를 포함하는 것이지만, 페이지 로딩이 완료되면 동적으로 외부 파일을 다운로드 한다. 다른 후속 페이지는 이미 브라우저에 캐시된 외부파일을 참조할 것이다.


9. DNS 조회를 줄여라
tag: content
DNS(Domain Name System)는 마치 전화번호부가 사람이름을 전화번호로 매핑(mapping)하는 것처럼 호스트 네임을 IP 주소로 매핑(mapping)시킨다. 브라우저에 www.yahoo.com을 입력하면 브라우저에 연결된 DNS resolver는 해당 서버의 IP주소를 조회한다. DNS는 비용이 부과된다. 일반적으로 주어진 호스트 네임에 대한 IP 주소를 조회하기 위해 DNS당 20-120 milliseconds(ms: 1000분의 1초)가 소요된다. 브라우저는 DNS 조회가 완료될 때까지 해당 호스트 네임으로부터 어떤 것도 다운로드 할 수 없다.
DNS 조회는 성능향상을 위해 임시저장(cache)된다. 이 캐싱은 사용자의 ISP 또는 로컬 네트워크에 의해 유지되는 특정 캐시 서버에서 발생하지만, 개인사용자의 컴퓨터에서 나타나기도 한다. DNS 정보는 운영체제의 DNS cache(Microsoft Windows의 “DNS 고객 서비스”)에 남겨진다. 대부분의 브라우저는 운영체제의 캐시와는 별도로 자신의 캐시를 소유한다. 브라우저가 DNS 기록을 자신의 캐시에 저장하는 한, 그 기록(record)에 대해 운영체제를 방해하지 않는다.
IE는 레지스트리 DnsCacheTimeout 등록 설정을 기본으로 30분 동안 DNS 조회를 캐시한다. Firefox는 network.dnsCacheExpiration 환경설정에 의해 1분 동안 DNS 조회를 캐시한다. (Fasterfox는 이것을 1시간으로 변경한다)
클라이언트의 DNS 캐시가 비어있을 때(브라우저, OS 모두), DNS 조회 수는 웹페이지의 고유한 호스트 네임의 수와 동일하게 된다. 이것은 page URL, images, script files, stylesheets, Flash 객체 등을 포함한다. 따라서 고유한 호스트 네임수를 줄이면 DNS 조회 수도 줄어든다.
고유한 호스트 네임의 수를 줄이는 것은 페이지에 발생하는 병렬 다운로드의 양을 줄일 수도 있다. DNS 조회를 피하는 것은 응답시간을 줄일 수 있지만, 병렬 다운로드를 줄이는 것은 응답시간을 증가시킬지도 모른다. 따라서 이 컴포넌트를 적어도 2개 이상으로 나누되, 4개이상의 호스트 네임을 사용하지 말 것을 권장한다. 이것은 DNS 조회를 줄이는 것과 높은 수준의 병렬 다운로드를 허용하는 것 사이에 적당한 절충안을 만들어줄 것이다.


10. JavaScript와 CSS를 축소해라
tag: javascript, css
축소화(minification)는 로드시간을 개선하기 위해 코드에서 불필요한 문자들(characters)을 제거하여 JavaScript와 CSS의 사이즈를 줄이는 방법이다. 축소화하면 불필요한 공백문자(공백, 줄 바꿈, tab)뿐 아니라 모든 코멘트 역시 제거된다.JavaScript의 경우, 다운로드 파일의 크기가 줄어들기 때문에 축소화는 응답시간의 성능을 개선시킨다. Javascript를 축소하는 2가지 대중적인 툴은 JSMin과 YUI 압축기이다. YUI 압축기는 CSS도 축소할 수 있다.
Obfuscation(난독화 또는 우회기법)은 소스코드에 적용될 수 있는 획기적인 최적화 방안이다. 이것은 축소화보다 복잡하며 난독화(Obfuscation)의 결과로 오류를 생성할 가능성이 높다. 미국 웹사이트 TOP10의 설문조사 결과에 따르면, 난독화는 25%의 크기를 감소시키는 것과 비교하여 축소화은 21%의 감소됐다. Javascript 난독화은 높은 수치의 감소를 가져오지만, JavaScript 축소화으로 축소하는 것이 위험이 적다.
외부 스크립트와 styles을 축소하는 것 외에도, 인라인된 <script>와 <style> 구문 역시 축소가 가능하다. 스크립트와 스타일을 압축하더라도 그것들을 최소화하면 5%이상의 크기를 더 줄일 수 있다. JavaScript와 CSS의 사용이 증가함에 따라 코드를 축소하는 것은 많은 절감을 가져올 것이다.


11. 리다이렉션을 피해라
tag: content
재전송(redirects)은 301 및 302 상태 코드를 가지고 확인할 수 있다. 다음은 301 응답의 HTTP 헤더의 예이다.
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html 
브라우저는 자동으로 경로속성에 지정된 URL로 넘어간다. 재전송에 대한 모든 정보는 해더에 있다. 응답한 본문은 일반적으로 비어있지만 301과 302 응답 모두 실제로 캐시되지 않는다. (Expires나 Cache-Control과 같은 추가 헤더 제외) 메타 태그의 refresh와 JavaScript는 다른 URL로 사용자에게 전송되는 다른 방법이다. 그래도 반드시 재전송을 해야 한다면, 먼저 뒤로 가기 버튼이 제대로 작동하는지 확인한 후에 표준 3xx HTTP 상태 코드를 사용할 것을 권장한다.
가장 중요하게 기억해야 할 것은 재전송은 사용자경험(UX)을 느리게 만든다는 것이다. 사용자와 HTML 문서 사이에 재전송을 삽입하는 것은 페이지에 있는 모든 것들을 지연시킨다. 그 이유는 HTML 문서가 도착하기 전까지는 페이지의 아무것도 출력되지 않을 수 있으며, 어떤 요소도 다운로드를 시작할 수 없기 때문이다. 가장 최악인 것은 재전송은 자주 일어나지만 웹 개발자들은 일반적으로 그것을 인식하지 못한다는데 있다. 그것은 '/'가 URL(반드시 1개를 가져야만 하는)로부터 빠져있을 때 발생한다. 예를 들어, http://astrology.yahoo.com/astrology로 이동하면 301응답 경과를 받고, http://astrology.yahoo.com/astrology/ 로 재전송된다. 이것은 Apache 를 사용하는 경우 Alias, mod_rewrite, DirectorySlash 지시어를 사용함으로써 해결된다.
오래된 웹사이트에서 새로운 웹사이트로의 접속은 재전송의 일반적 사용이다. 이 외에도 웹사이트의 서로 다른 부분들을 연결하는 것이나 특정조건(브라우저 유형, 사용자 계정 유형 등)에 따라 재전송하는 것도 포함된다. 두 개의 웹사이트를 연결하기 위해 재전송을 사용하는 것은 간단하면서도 추가 코드를 요구하지 않는다. 재전송은 개발자들에게 복잡성을 줄일 수 있지만, 사용자경험(UX)을 감소시킨다. 재전송에 대한 다른 대안은 두 코드 경로가 같은 서버에 호스트되는 경우, Alias와 mod-rewrite를 사용하는 것이다. 도메인 이름변경을 위해 재전송을 사용한다면 Alias 또는 mod_write를 결합하여 CNAME(DNS record: 하나의 도메인 이름을 다른 것으로 가리키는 별칭도메인을 생성)를 생성하면 된다.


12. 중복 스크립트를 제거하라
tag: javascript
한 페이지에 동일한 JavaScript 파일을 두 번 포함시키는 것은 성능에 영향을 미친다. 이것은 생각만큼 특이한 경우가 아니다. 미국 웹사이트 상위 10위 중 두 사이트가 중복 스크립트를 포함한다는 것을 보여준다. 팀의 크기와 스크립트의 수, 두 가지 주요 요소는 단일 웹페이지에서 스크립트 복제가능성을 증가시킨다. 이는 복제 스크립트는 불필요한 HTTP 요청과 JavaScript 실행의 생성으로 인해 성능에 영향을 준다.
불필요한 HTTP 요청 수는 IE에서 발생하지만 Firefox에서는 일어나지 않는다. IE에서, 외부 스크립트가 두 번 포함되고 캐시되지 않는다면, 그것은 페이지 로딩을 하는 동안 두 개의 HTTP 요청을 생성한다. 비록 스크립트가 캐시에 있더라도 사용자가 페이지를 다시 로딩할 때 추가 HTTP 요청은 일어난다.
불필요한 HTTP 요청의 추가 생성은 스크립트를 여러 번 읽도록 시간을 낭비한다. 이 중복된 JavaScript의 실행은 스크립트의 캐시 가능 여부에 관계없이 Firefox와 IE 모두에서 발생한다.
같은 스크립트를 두 번 포함하지 않게 하려면 템플레이팅 시스템에서 스크립트 관리 모듈을 실행하는 것이다. 스크립트를 넣기 위한 일반적인 방법은 HTML 페이지에 SCRIPT 태그를 사용하는 것이다.
<script type="text/javascript" src="menu_1.0.17.js"></script>
PHP에서의 대안은 insertScript라는 함수를 작성하는 것이다.
<?php insertScript("menu.js") ?>
여러 번 삽입되는 동일한 스크립트를 예방하는 것 외에도, 이 함수는 의존성 검사, 미래 시점의 Expires header 지원을 위한 스크립트 파일 이름에 버전 번호를 추가하는 것와 같은 다른 문제들도 처리할 수 있을 것이다.


13. ETags를 설정하라
tag: server
Entity tags(ETags)는 브라우저의 캐시에 있는 컴포넌트가 원본 서버에 있는 컴포넌트와 일치하는지를 확인하기 위해 웹서버와 브라우저가 사용하는 메커니즘이다. (“entity”는 “component”의 다른 단어이다: images, scripts, stylesheets 등) ETags는 마지막 수정날짜보다 더 유동적인 검증들에 대한 메커니즘을 제공하기 위해 추가된다. ETag는 유일하게 컴포넌트의 특정 버전을 식별하는 문자열이다. 유일한 제약은 문자열 형식을 이용하는 것이다. 원본 서버는 ETag 응답 헤더를 사용하여 컴포넌트의 ETag를 지정한다.
HTTP/1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 12195
나중에 브라우저가 컴포넌트를 확인해야만 한다면, 원본서버에서 ETag를 통과시키기 위해 If-None-Match 헤더를 사용해라. 만약 ETags가 일치한다면, 다음 아래 예로 304 상태코드는 12195 bytes로 응답을 줄이고 반환된다.
GET /i/yahoo.gif HTTP/1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-457e1c1f"
HTTP/1.1 304 Not Modified
ETags는 사이트를 호스팅하는 특정서버에서만 보여지도록 만들어진 속성을 사용하도록 설계되었기 때문에 브라우저가 한 서버로부터 원본 컴포넌트를 얻고 나중에 다른 서버에서 해당 컴포넌트를 확인하려고 할 때 일치하지 않는다는 문제점이 있다. 웹사이트가 요청을 처리하기 위해서는 서버를 클러스터 구성으로 하는 것이 일반적이다. 기본적으로 Apache와 IIS 모두 여러 대의 서버를 이용하여 웹 사이트의 계속되는 유효성 테스트를 획기적으로 줄일 수 있도록 ETag에 데이터를 삽입한다. 
Apache 1.3과 2.x에 대한 ETag 형식은 inode_size_timestamp이다. 비록 특정 파일이 여러 서버에 를 통해 동일한 디렉토리에 상주하고, 동일한 파일크기, 권한, timestamp 등을 가지더라도, 그 파일의 inode는 서버마다 다르다.
IIS 5.0 및 6.0은 ETags와 유사한 문제를 가지고 있다. IIS에서 ETags에 대한 형식은 Filetimestamp:ChangeNumber이다. ChangeNumber는 IIS 구성변경을 추적하는 데 사용하는 카운터이며, 웹사이트 뒤에 위치한 모든 IIS 서버들은 다른 ChangeNumber를 가지고 있다.
완벽히 동일한 컴포넌트에 대해서, Apache 및 IIS에 의해 생성된 ETags는 서버마다 일치하지 않을 것이라는 최종결과를 보여준다. 만약 ETags가 일치하지 않는다면, 사용자는 효율적인(작고 빠른) 304 응답(ETags에 의해 설계된)을 받지 않는다. 대신에 사용자는 컴포넌트에 대한 모든 데이터 외에 일반적인 200 응답을 받을 것이다. 오직 한 서버에서 웹사이트를 호스트하는 경우는 문제가 되지 않지만 여러 서버에서 웹사이트를 호스팅 한다면, ETag 설정을 기본으로 Apache나 IIS을 사용해야 할 것이다. 또한 사용자의 페이지는 점점 느려지고 서버의 부하가 높아지며, 보다 큰 대역폭을 소모할 뿐 아니라, 프록시는 콘텐츠를 효율적으로 캐시하지 못할 것이다. 비록 컴포넌트가 먼 미래시점의 Expires header를 가지더라도 사용자가 Reload(재로드) 또는 Refresh(새로고침)을 누를 때마다 조건부의 GET 요청은 계속 이루어진다. 만약 ETags가 제공하는 유연한 검증 모델을 활용하지 않는다면, ETag를 완전히 제거하는 편이 낫다. 마지막으로 수정된 header는 component의 timestamp에 따라 확인된다. 그리고 ETag를 제거하면 응답과 후속 요청 모두에서 HTTP 헤더 크기를 줄일 수 있다. 이 Microsoft Support article은 ETags를 어떻게 제거하는지 설명하고 있다. Apache에서의 ETags 제거는 설정 파일에 간단히 다음 줄을 추가함으로써 이루어진다.
FileETag none


14. Ajax를 캐싱할 수 있도록 만들어라
tag: content
Ajax의 장점 중 하나는 백엔드의 웹서버로부터 정보를 비동기식으로 요청함으로써 사용자에게 즉각적인 피드백을 제공한다는 것이다. 그러나 Ajax를 사용하면 사용자는 반환을 위해 비동기의 JavaScript와 XML 응답들을 기다리며 시간을 낭비하지 않을 것이라는 것을 보장할 수 없다. 여러 애플리케이션에서 Ajax가 어떻게 사용되는지에 따라 사용자가 기다림을 지속할 것인지가 결정된다. 예를 들어, 웹기반의 이메일 클라이언트에서 사용자는 그들의 검색 기준에 일치하는 모든 이메일 메시지들을 찾기 위해 Ajax 요청들에 대한 결과값을 기다리게 될 것이다. “asynchronous(비동기의)”이란 “instantaneous(즉시 일어나는)”을 의미하지 않는다.
성능향상을 위해 이런 Ajax 응답들을 최적화하는 것은 중요하다. “Add an Expires or a Cache-Control Header”에서 다뤘듯이(앞부분 참고), Ajax 성능을 향상시키기 위한 가장 중요한 방법은 응답들이 캐시 되도록 만드는 것이다. 다른 규칙들도 Ajax에 적용된다.
Gzip Components
Reduce DNS Lookups
Minify JavaScript
Avoid Redirects
Configure ETags
예를 들어 살펴보자면, 웹 2.0 이메일 클라이언트는 자동완성(autocompletion) 기능에 대해 사용자의 주소록을 다운로드하기 위해 Ajax를 사용할지도 모른다. 만약 사용자가 이메일 웹 애플리케이션을 사용한 이후 주소록을 수정하지 않았다면, Ajax 응답이 미래 시점의 Expires 또는 Cache-Control header로 캐시가능하게 만들어진 경우에 한해 이전 주소록 응답은 캐시로부터 읽어올 수 있을 것이다. 브라우저는 언제 이전에 캐시된 주소록 응답을 사용할 지, 아니면 새로운 주소록을 요청할 지 반드시 통보해야 한다. 이것은 마지막으로 사용자가 주소록을 수정한 것을 나타내는 주소록 Ajax URL에 timestamp를 추가하여 수행할 수 있다. (ex. &t=1190241612) 만약 주소록이 마지막 다운로드 이후 수정되지 않았다면, timestamp는 동일하며 주소록은 별도의 HTTP 왕복이 제거된 브라우저의 캐시로부터 불러올 수 있을 것이다. 만약 사용자가 주소록을 수정했다면, timestamp는 새로운 URL이 캐시된 응답과 일치하지 않는다는 것을 확신하고, 브라우저는 업데이트된 주소록 항목을 요청할 것이다.
비록 Ajax 응답들이 동적으로 생성되고 오직 한 명의 사용자에게 적용될지라도, Ajax응답들은 여전히 캐시될 수 있다. 이렇게 하면 웹 2.0 애플리케이션을 빠르게 만들 수 있을 것이다.


15. buffer를 빨리 비워라
tag: server
사용자들이 페이지를 요청할 때, 백엔드 서버와 HTML 페이지를 함께 연동하는 것은 200~500ms 의 시간이 걸릴 수 있다. 브라우저는 도착하는 데이터를 기다리는 동안 작동하지 않는다. PHP는 flush() 기능을 가지고 있다. 그것은 일부 준비된 HTML 응답을 브라우저로 전송하는 것을 승인함으로써, 브라우저는 백엔드가 HTML 페이지의 나머지가 연동되는 동안 먼저 컴포넌트를 불러올 수 있다. 그로 인한 장점은 주로 바쁜 백엔드와 한가한 프론트엔드에서 나타난다.
HEAD에 대한 HTML이 보통 더 쉽게 생성되기 때문에 HEAD 바로 다음에 플러시를 사용하는 것이 적합하다. 그것은 백엔드가 동작하는 동안, 브라우저가 병렬로 연동하기 위해 필요한 CSS 및 JavaScript 파일들을 포함하는 것을 허락한다.
예)
... <!-- css, js -->
</head>
<?php flush(); ?>
<body>
... <!-- content -->
Yahoo search는 이 기술을 사용해 실제 사용자 테스트와 점을 증명하는 연구로 변화하였다.


16. AJAX 요청시 GET을사용해라
tag: server
Yahoo 메일 팀은 XMLHttpRequest를 사용할 때, POST가 브라우저에서 2단계(headers를 전송하고 그 후에 데이터를 전송)의 과정으로 수행된다는 것을 발견했다. 그래서 많은 양의 쿠키를 갖고 있는 경우를 제외하고, 데이터 전송을 위해 TCP 패킷 한 개만 필요로 하는 GET을 사용하는 것이 최상의 방법이다. IE에서 최대 URL의 길이는 2K이므로, 2K 이상의 데이터를 전송한다면 GET을 사용하지 못할 수도 있다.
흥미로운 부작용은 포스팅된 어떤 데이터도 갖고 있지 않은 POST는 GET처럼 행동한다는 것이다. HTTP의 표준에 따라, GET은 정보검색을 의미하므로 데이터를 전송해서 서버 측에 저장되게 하지말고, 오직 데이터를 요청할 때만 GET을 사용하는 것이 이치에 맞다.


17. 로드 후 구성요소
tag: content
페이지를 자세히 살펴보게 되면 “처음에 페이지를 읽어오기 위해 절대적으로 요구되는 것”이 무엇인지에 의문을 가진다. 콘텐츠와 컴포넌트의 나머지는 기다릴 수 있다.
JavaScript는 onload 이벤트 전후의 분할을 위한 이상적인 후보이다. 예를 들어, 만약 드래그 앤 드롭과 애니메이션을 실행하는 JavaScript 코드와 라이브러리를 갖고 있다면, 초기 렌더링 후에 페이지의 드래깅 요소가 오기 때문에 그것들은 기다릴 수 있다. Post-loading에 대한 후보를 찾기 위한 또 다른 장소는 fold 아래 숨겨진 콘텐츠(사용자의 동작 이후에 나타나는 콘텐츠)와 이미지들을 포함한다.
YUI Image Loader를 이용하면 fold된 이미지들을 지연시킬 수 있고, YUI GET 유틸리티는 즉시 JS와 CSS를 포함하는 가장 쉬운 방법이다. Firebug의 Net Panel이 실행되는 Yahoo의 홈페이지는 좋은 예가 된다.
성능목표는 다른 웹 개발 모범사례와 비슷할 때 가장 이상적이다. 이런 경우, 점진적 강화의 아이디어는 JavaScript를 지원받을 때 사용자경험(UX)을 향상시킬 수 있지만, 페이지가 JavaScript 없이도 작동하는지 반드시 확인해야 한다는 것을 명시한다. 그래서 페이지가 정상적으로 작동하는지 확인한 후에, 약간의 post-loaded scripts(드래그 앤 드롭 및 애니메이션과 같은 부가프로그램을 제공하는)로 그것을 향상시킬 수 있다.


18. 로드 전 구성요소
tag: content
Preload는 post-load의 반대개념처럼 보일지 모르지만, 실제로는 다른 목적을 가지고 있다. 요소를 선로딩함으로써 브라우저가 정지상태인 시간의 이점을 활용할 수 있고, 미래에 필요한 요소들(images, styles, scripts와 같은)을 요청할 수 있다. 이 방법은 사용자가 다음 페이지를 방문할 때, 대부분의 요소들은를 캐시에 이미 가질 수 있고, 페이지는 사용자에게 훨씬 빨리 로드될 것이다.
Preloading의 몇몇 종류에 대해 알아보자.
무조건 preload - onload가 작동되자마자, 바로 여분의 요소들을 가져온다. 스프라이트 이미지가 onload를 어떻게 요청하는지에 대한 예로 google.com을 확인해라. 이 스프라이트 이미지는 google.com의 홈페이지에 필요한 것은 아니지만, 그것은 연속적인 검색결과 페이지에 필요하다.
조건부 preload - 사용자의 작업을 기반으로 사용자가 다음에 어디를 향하고 있는지에 대한 경험에 근거한 추측을 만들고, 그에 따라 preload를 해라. Search.yahoo.com에서 검색창에 입력을 시작하면, 일부 추가 요소들의 요청방법을 볼 수 있다.
예상된 preload - 재설계를 시작하기 전에 미리 preload를 해라. 종종 재설계후 사람들이 "새로운 사이트는 멋지지만, 이전보다 느려졌어"라는 말을 한다. 문제는 사용자들이 기존 사이트에 캐시를 가지고 방문했지만, 새로운 사이트는 항상 비어있는 캐시 상태로 방문한 것이기 때문일 수도 있다. 재설계를 하기 전에 몇 가지 요소들을 preloading하여 부작용을 완화시킬 수 있다. 기존 사이트는 브라우저가 정지상태인 시간을 사용할 수 있고, 새로운 사이트에서 사용될 이미지들 및 스크립트를 요청할 수 있다.


19. DOM 요소의 수를 줄여라
tag: content
복잡한 페이지는 더 많은 바이트를 다운로드 하는 것을 의미하고, 또한 JavaScript에서 느린 DOM 접근을 의미한다. 예를 들어, 만약 이벤트 헨들러를 추가하려는 페이지에 500 또는 5000 DOM 요소를 통해 루프를 돌려서 처리한다면 차이가 생긴다.
DOM 요소들의 높은 숫자는 콘텐츠의 제거없이 페이지의 마크업과 함께 개선되어야만 하는 무언가가 있다는 것을 말한다. 당신은 레이아웃을 위해 중첩된 table을 사용하고 있는가? 당신은 단지 레이아웃 문제를 해결하기 위해 더 많은 div를 삽입하고 있는가? 아마 마크업을 할 수 있는 이론상의 보다 정확한 방법이 있을 것이다.
YUI CSS 유틸리티는 레이아웃에 큰 도움이 된다: grid.css는 전체 레이아웃에 도움이 될 수 있고, font.css와 reset.css는 브라우저의 기본서식을 완전히 제거하는 데 도움이 될 수 있다. 이것은 마크업에 대해 새롭게 생각해 볼 수 있는 기회이다. 예를 들어, 새로운 줄을 렌더링할 때가 아니라 이론상으로 이치에 맞을 때에만 <div>를 써라.
DOM 요소의 수는 콘솔에 아래와 같이 입력하는 것으로 쉽게 테스트가 가능하다.
document.getElementsByTagName('*').length
그렇다면, 얼마나 많은 것이 DOM 요소들이 많다는 것을 의미하는가? 좋은 마크업을 가진 유사한 페이지들을 확인해보자. 예를 들어, Yahoo의 홈페이지는 굉장히 바쁜 웹사이트 중 하나이지만 여전히 700 요소 이하를 가지고 있다.


20. 구성요소들을 도메인별로 분리하라
tag: content
요소들을 분할하는 것은병렬 다운로드를 극대화할 수 있다. DNS 조회의 불이익 때문에 2-4개 이상의 도메인을 사용하고 있지는 않은지 확인해라. 예를 들어, www.example.org에서 HTML과 동적 콘텐츠를 호스트할 수 있고, static1.example.org와 static2.example.org 사이의 정적 구성 요소를 분할할 수 있다.
자세한 내용은 Tenni Theurer와 Patty Chi의 "Maximizing Parallel Downloads in the Carpool Lane"를 참조해라.


21. iframe의 수를 최소화해라
tag: content
Iframes는 HTML 문서가 상위 문서에 삽입될 수 있도록 한다. 효과적으로 사용될 수 있도록 iframes이 어떻게 작동하는지 이해하는 것은 매우 중요하다.
<iframe> 장점
badges나 ads와 같이 느린 third-party content를 원활하게 함
Security sandbox (보안 sandbox)
scripts를 병렬로 다운로드 함
<iframe> 단점:
비어있더라도 비쌈
page onload를 차단함
Non-semantic (비의미론적인)


22. 404 오류를 피해라
tag: content
HTTP 요청은 비용이 발생하므로 HTTP 요청을 만들고 쓸모 없는 응답(404 Not Found)을 얻는 것은 전적으로 불필요하며 아무 이득 없이 사용자경험(UX)을 느리게 만들 것이다.
일부 사이트들은 유용한 404s(ex. “Did you mean X?”)를 가지고 있다. 이것은 사용자경험(UX)에는 도움이 되지만, 한편으로는 서버 자원(데이터베이스와 같은 기타 등등)을 낭비시키는 단점을 가진다. 외부 JavaScript에 대한 링크가 잘못되고, 그 결과값이 404일 경우는 특히 좋지 않다. 처음에 이 다운로드는 병렬 다운로드를 차단할 것이다. 그 다음, 브라우저는 마치 그것이 JavaScript 코드인 것처럼 그 안에 사용 가능한 무언가를 찾기 위해 404 응답 데이터를 분석할지도 모른다.


23. 쿠키사이즈를 줄여라
tag: cookie
HTTP 쿠키들은 인증 및 개인화와 같은 다양한 이유로 사용된다. 쿠키들에 대한 정보는 웹 서버와 브라우저 사이의 HTTP 헤더에서 교환된다. 쿠키들의 크기를 가능한 한 작게 유지하는 것은 사용자의 응답시간에 미치는 영향을 최소화하기 위해 매우 중요하다.
자세한 내용은 Tenni Theurer and Patty Chi의 "When the Cookie Crumbles"를 참조해라. 이 연구의 실질적 정보는 다음과 같다:
불필요한 쿠키들을 제거해라
사용자의 응답시간에 미치는 영향을 최소화하기 위해 쿠키들의 크기를 가능한 한 작게 유지해라
다른 서브도메인들이 영향을 받지 않도록 적절한 도메인 수준에서 쿠키설정을 주의해라.
적절한 만료 날짜를 설정해라. 이른 만료날짜를 설정하거나 설정하지 않으면, 사용자의 응답시간 개선을 위해 쿠키를 즉시 제거해라.



24. 컴포넌트는 쿠키가 없는 도메인을 사용하라
tag: cookie
브라우저가 정적 이미지에 대한 요청을 하고 쿠키들을 함께 보낼 때, 쿠키들은 서버에서 아무런 영향을 주지 않는다. 그래서 이 쿠키들은 별다른 이유 없이 네트워크 트래픽만을 생성한다. 정적 구성요소들이 cookie-free 요청들과 함께 요구되고 있는지 반드시 확인해야 한다. 서브도메인을 생성하고 거기에 모든 정적 구성요소들을 호스트 해라.
만약 도메인이 www.example.org이라면, static.example.org에 정적 구성요소들을 호스팅 할 수 있다. 그러나 www.example.org와 반대로 만약 최상위 도메인 example.org에 이미 쿠키들을 설정했다면, static.example.org로의 모든 요청은 쿠키들을 포함할 것이다. 이 경우, 완전히 새로운 도메인을 구입할 수 있고, 거기에 정적 구성요소들을 호스트 할 수 있다. 또한 이 도메인은 cookie-free 상태로 유지될 수 있다. Yahoo는 yimg.com을 사용, YouTube는 ytimg.com을 사용하고 Amazon은 images-amazon.com 등을 사용한다.
Cookie-free 도메인에서 적정 구성요소들을 호스팅 하는 것의 또 다른 이점은 일부 프록시가 쿠키들과 함께 요청된 구성요소들을 캐시하는 것을 거부할 수도 있다는 것이다. 같은 맥락으로, 홈페이지에 example.org 또는 www.example.org의 사용을 망설이고 있는 중이라면, 쿠키에 미치는 영향을 고려해라. www를 생략하는 경우는 *.example.org의 쿠키들을 작성하는 것 밖에 할 수 없지만, 성능을 위해 www 서브도메인을 사용하고 그 서브도메인에 대한 쿠키들을 작성하는 것이 최선의 방법이다.


25. DOM 접근을 최소화해라
tag: javascript
자바 스크립트로 DOM 요소들을 접근하는 것은 느리므로, 더 빠른 응답 페이지를 가지려면 다음 사항들을 반드시 실행해라.
접근했던 요소에 대한 참조를 캐시해라
노드들을 “오프라인”으로 업데이트하고 그것들을 트리에 추가하라
JavaScipt로 레이아웃을 고정하는 것을 피해라
더 자세한 정보는 YUI theatre의 Julien Lecomte이 작성한 "High Performance Ajax Applications"를 참조해라.


26. 이벤트 핸들러를 잘 개발하라
tag: javascript
가끔 페이지들은 너무 자주 실행되는 DOM 트리의 서로 다른 요소들에 붙여진 너무 많은 이벤트 핸들러 때문에 반응속도가 느리다고 느껴진다. 이 때, 이벤트 delegation을 사용하는 것은 좋은 접근 방법이다. 만약 한 개의 div안에 10개의 버튼을 가진다면, 각 버튼마다 하나의 핸들러를 붙이는 대신 div 래퍼에 오직 하나의 이벤트 핸들러를 사용해라. 이벤트는 버블업하므로 이벤트를 인식할 수 있을 것이며 어떤 버튼이 어디서부터 시작됐는지 알아낼 수 있을 것이다.
또한 DOM 트리로 무언가를 시작하기 위해 onload 이벤트를 기다릴 필요는 없다. 주로 당신이 필요한 모든 것은 트리에서 접근하기를 원하는 사용 가능한 요소뿐이다. 따라서 모든 이미지들이 다운로드 되는 것을 기다리지 않아도 된다. DOMContentLoaded는 onload 대신에 사용할지도 모르는 이벤트이지만 모든 브라우저에서 사용이 가능할 때까지는 onAvailable method를 가지고 있는 YUI Event 유틸리티를 사용할 수 있다.
더 자세한 정보는 YUI theatre의 Julien Lecomte이 작성한 "High Performance Ajax Applications"를 참조해라.


27. @import 보다는 <link>를 선택하라
tag: css
이전 소개됐던 방법 중 하나는 점진적 렌더링을 위해 CSS가 페이지의 상단에 와야만 한다는 것이다. IE에서 @import는 태그를 페이지의 하단에 사용하는 것과 같으므로 최적의 사용방법이 아니다.


28. CSS AlphaImageLoader Filter를 피해라
tag: css
IE 소유의 AlphaImageLoader filter는 IE 7 미만 버전에서 PNGs의 반투명한 색채의 문제점을 해결하는 것을 목표로 하고 있다. 이 필터의 문제점은 이미지가 다운로드 되는 동안에 렌더링을 차단하고 브라우저를 멈추게 만든다는 것이다. 이것은 또한 메모리 소모를 증가시키고, 이미지 마다가 아닌 아닌 요소마다 적용되므로 문제는 더욱 가중된다.
가장 좋은 방법은 AlphaImageLoader를 아예 사용하지 않고, 대신 선명도는 떨어지지만 IE에서 정상적으로 작동하는 PNG8을 사용하는 것이다. 만약 AlphaImageLoader를 반드시 사용해야 한다면 IE7 이상의 사용자들에게는 적용되지 않도록 언더바 hack _filter를 사용해라.


29. 이미지를 최적화하라
tag: images
디자이너가 웹페이지에 대한 이미지 생성을 완료하면, 웹서버에 이미지들을 올리기 전에(FTP) 확인할 수 있는 몇 가지 것들이 존재한다.
이미지 색상들의 숫자에 상응하는 팔레트 크기를 사용하면 GIFs를 체크하고 볼 수 있다. Imagemagick를 이용하면 identify -verbose image.gif를 사용해서 확인하기가 쉽다. 팔레트에서 4가지 색상과 256 색상 슬롯을 사용하는 이미지를 보면 개선의 여지가 있다.
GIFs를 PNGs로 변환해보고, 저장이 되었는지 확인해라. 개발자들은 종종 브라우저에서 제한된 지원으로 인해 PNGs의 사용을 망설이지만, 이것은 이미 과거의 얘기이다. 유일한 진짜 문제는 PNGs 진짜 색상의 알파투명도이지만, GIFs는 진짜 색상이 아니며 다양한 투명이미지 또한 지원하지 않는다. 그러므로 GIF가 할 수 있는 것들은, 팔레트 PNG(PNG8) 또한 할 수 있다. (애니메이션 제외). 이 간단한 imagemagick 명령은 PNGs를 안전하게 사용할 수 있게 한다.
convert image.gif image.png
우리는 PiNG에게 기회를 주었다.
모든 PNGs에서 pngcrush(또는 최적화된 다른 PNG tool)를 실행하라
ex) pngcrush image.png -rem alla -reduce -brute result.png
모든 JPEGs에서 jpegtran을 실행하라. 이 도구는 회전과 같이 손실 없는 JPEG 작업을 수행하고 최적화를 위한 사용이 가능하며, 이미지들로부터의 다른 쓸모 없는 정보(EXIF 정보와 같은)와 코멘트를 제거할 수 있다.
jpegtran -copy none -optimize -perfect src.jpg dest.jpg


30. CSS 스프라이트를 최적화하라
tag: images
스프라이트에서의 이미지를 수직이 아니라 수평으로 정렬하면 파일사이즈가 더 작아진다.
스프라이트에서 유사한 색상을 결합하는 것은 색상 카운트를 낮게 유지하는데 도움이 된다. PNG8에 맞춰 이상적인 256 색상 이하로 설정
“mobile-friendly” 하며, 스프라이트에서 이미지들 사이에 큰 간격을 두지 않는다. 이것은 파일 크기에는 별로 영향을 미치지 않지만 사용자 에이젼트에게 이미지를 픽셀 지도로 압축하기 위해 적은 메모리를 요구한다. 100X100 이미지가 10,000 픽셀일 때, 1000X1000은 1,000,000픽셀이다.


31. HTML에서 이미지의 크기를 줄이지 마라
tag: images
HTML에서 이미지의 넓이와 높이를 설정할 수 있다는 이유로, 필요한 것보다 더 큰 이미지를 사용하지 마라. 
<img width="100" height="100" src="mycat.jpg" alt="My Cat" />
만약 위와 같은 구문을 원한다면 이미지(mycat.jpg)는 500X500px 크기의 이미지를 작게 하는 것 보다 100X100px 크기의 이미지를 넣어야 한다.


32. favicon.ico파일은 작고 캐시 가능하게 만들어라
tag: images
favicon.ico는 서버의 루트에 머무는 이미지이다. 당신이 신경 쓰지 않더라도 브라우저는 계속 그것을 요구하기 때문에 404 Not Found에 해당하지 않도록 하는 것이 좋다.
또한 같은 서버에 있기 때문에 쿠키들은 요청 시마다 발송된다. 이 이미지는 또한 다운로드 순서를 방해한다. 예를 들어, IE에서 onload에 추가 컴포넌트를 요청할 때, 파비콘은 해당 추가 컴포넌트들 이전에 다운로드 될 것이다.
Favicon.ico가 가진 단점을 보완하기 위해 다음 사항을 확인해라.
가능한 한 1K이하로 작게 해라.
변경설정 후에는 이름변경이 불가능하므로 원하는 시점의 Expires header를 설정하라. 아마 미래 시점(약 몇 달 뒤)의 Expires header를 안전하게 설정할 수 있을 것이다. 명확한 결정을 위해 현재 favicon.ico의 마지막 수정날짜 확인이 가능하다.
Imagemagick는 작은 파비콘 이미지들을 생성할 수 있게 해준다.


33. 25K이하의 구성요소들로 유지해라
tag: mobile
이러한 제한은 아이폰이 25K보다 큰 구성요소를 캐시하지 않는 것에 기인한다. 이것은 압축되지 않은 사이즈라는 것을 기억해라. gzip으로는 충분하지 않을 수도 있기 때문에 minification(축소)하는 것은 중요하다.
좀 더 자세한 정보를 원한다면 Wayne Shea and Tenni Theurer의 "Performance Research, Part 5: iPhone Cacheability - Making it Stick"를 참조해라.


34. 다중문서에 구성요소들을 묶어라
tag: mobile
HTTP 요청들은 비용이 발생한다는 것을 명심해라. 이메일의 첨부파일과 같이 다중문서에 구성요소들을 묶는 것은 한 개의 HTTP 요청에 여러 개의 구성요소들을 불러오는데 도움이 된다. 사용자가 이 기술을 사용할 때, 사용자 에이젼트가 이러한 기능을 지원하는지 먼저 확인해라.(아이폰은 지원하지 않음)


35. 이미지의 빈 src속성값을 피해라
tag: server
빈 문자열 src 속성을 가진 이미지가 하나 이상 발생할 것으로 예상한다. 이것은 아래 두 가지 형태로 나타난다.
1.straight HTML
<img src="">
2.JavaScript
var img = new Image();
img.src = "";
두 가지 양식은 동일한 효과를 불러 일으킨다: 브라우저는 서버에 다른 요청을 만든다.
IE는 페이지가 위치한 directory를 요청한다.
Safari와 Chrome은 그것들의 실제 페이지를 요청한다.
Firefox 3와 이전 버전들은 Safari와 Chrome과 같은 동작을 수행하지만, 버전 3.5가 이 문제([bug 444931])를 다룬다. 그리고 더 이상 요청을 보내지 않는다.
Opera는 빈 이미지 src가 발생할 때, 아무것도 수행하지 않는다.



왜 이런 동작들은 좋지 않을까?
 
1. 많은 양의 예상치 못한 트래픽(특히, 하루에 100만개의 페이지뷰를 생성하는 페이지들에 대해)을 전송함으로써 서버는 정지상태가 된다.
2. 서버는 절대 보여지지 않을 페이지를 생성하는 사이클을 컴퓨팅 하는 것으로 낭비된다.
3. 사용자 데이터는 오류가 생길 가능성이 있다. 만약 요청이 추적상태에 있다면, 쿠키 또는 다른 방식으로 데이터를 파괴할 가능성이 있다. 비록 이미지 요청이 이미지를 반환하지 않더라도, 모든 쿠키들을 포함하는 브라우저에 의해 모든 헤더들은 읽고 수락될 수 있다. 응답의 나머지가 버려지는 동안에, 손상은 이미 완료될 지도 모른다. 
이 행동의 근본 원인은 URI 해상도가 브라우저에서 실행되는 방식 때문이다. 이것은 RFC 3986- Uniform Resource Identifiers에 정의되어 있다. 빈 문자열이 URI로 발생되면, 그것은 상대적인 URI로 간주되며, 5.2 섹션에 정의된 알고리즘에 따라 해결된다. 구체적인 예를 들자면, 5.4 섹션에 등록된 빈 문자열이다. IE가 부정확하게 실행되는 동안, 명백히 RFC 2396 - Uniform Resource Identifiers 사양(이것은 RFC 3986에 의해 더 이상 사용되지 않음)의 초기버전과 일맥상통하는 Firefox, Safari, 그리고 Chrome 섹션은 모두 사양마다 정확하게 빈 문자열을 해결할 것이다. 그래서 기술적으로, 브라우저는 상대적 URIs를 해결하기 위한 것들을 수행 중이다. 이 문맥에서의 문제는, 빈 문자열은 명확히 의도적이지 않다는 것이다.
HTML 5는 섹션 4.8.2에서 추가요청을 하지 않는 브라우저를 지시하기 위해 태그의 src 속성의 설명을 추가한다:
src 속성은 반드시 존재해야 하고, 페이지 및 스크립트 되지 않으며 양방향이 아니고, 선택적으로 애니메이션된 이미지 자원을 참조하는 유효한 URL을 반드시 포함해야 한다. 만약 요소의 기본 URI가 문서의 주소와 동일한 경우, src 속성값은 빈 문자열이어서는 안 된다.
브라우저는 추후에 이런 문제를 가지지 않아야 하겠지만, <script src=""> 및 <link href="">에 대한 그런 조항은 갖고 있지 않다. 브라우저가 실수로 이 동작을 구현하지 않도록 확실히 하기 위해 그것을 조정할 수 있는 시간이 아마 있을 것이다.
이 규칙은 Yahoo의 JavaScript 전문가, Nicolas C. Zakas에 의해 창안되었다. 자세한 내용을 보려면 그의 기사 "Empty image src can destroy your site"를 참조해라.


출처: http://nuts84.tistory.com/25 [Nut's]