요즘은 너무 광대역폭 빠른 응답속도를 자랑하는 네트워크 망을 가져버린 상황인 지금은 크게 문제가 안되는듯 보이지만.
외국에선 아직도 회선 상태가 좋지 않다보니 이런 테스트도 아직도(?) 하는가보다. (사실 -_-;; 가장 중요한것 아닌가!!!!!...이런 무개념을!! *쿨럭~*)
월스트리트 저널의 홈페이지를 기준으로 테스트를 했는데.
여기서 한가지 재미있는 사실은.. 대역폭의 감소보다 대기시간이 길어질 수록 로딩 시간이 더욱더 크게 벌어졌다는것이다.
대기시간이 길어지면서 패킷을 잃게 되고 다시 재전송을 하는데 소비되는 시간이 생각보다 크다는 것이다.
하지만 요즘 사용하는 대부분의 브라우저는 페이지 구성에 필요한 요소들을 다중으로 전송이 가능하다. 그렇다면 그와 연관된 모든 요소들을 한번에 땡겨오면 크게 차이가 나지 않아야 정상이 아닌가?
그렇다면 왜 저런 결과가 나왔을까?
본문에서는 기본적으로 HTML 소스를 읽어 오면서 그와 연관된 소스들을 다운로드 할 수 있어야 하는데, 문제는 "연관된 모든 요소들" 을 브라우저는 모르기 때문이다! 라고 이야기 한다. HTML 을 모두 해석한 뒤 DOM tree 를 해석하고, 그 다음에 <img> 태그와 같은 외부 소스를 요청하는 것들을 처리하기 시작한다.
문제는 외부 Javascript 파일을 로딩하면서 발생한다. Script 가 호출이 되기 전까진 HTML 을 해석하는 과정이 모두 중지된다. 여기에 document.write() 와 같은 부분이 끝 마치기 전까진 말이다. 이런것들이 대기시간을 더욱더 길게 만드는 것이다.
스크립트 로딩하는 동안 메인 해석기( Main Parser) 가 멈춰져 있는 동안. 별도의 해석기 ( Side parser )기 가 나머지 HTML 소스를 기반으로 다른 외부 파일들(이미지,CSS,JS등 )을 호출을 하게 하여 대기시간에 따른 로딩시간 저하 현상을 최소화 하게 한다.
그 차이를 좀더 명확하게 보여주자면 다음 그래프를 보아라! 괜찮은 구조인거 같다.. 사실 대역폭과 대기시간은 물리적인 문제라 S/W 적으로 해결하는데는 많은 어려움이 따르기 마련이지만 저정도 수준이면 상당히 의미있는것이 아닌가 한다.
1. HTTP 요청횟수를 최소화 하라.. (Make fewer HTTP requests) 요청하는 요소들 (Components) 를 최소화 하는것이 빠른 페이지 로딩에 도움이 된다. 이는 다수의 HTTP 요청에 의한 오버헤드(overhead) 를 줄여주기 때문이다. 이를 위해선 CSS/Javascript 등을 통합하여 사용 하거나 이미지는 CSS 스프라이트(Sprites) 를 이용하는게 좋다.
CSS 스프라이트 (CSS sprites) 라는 것은 예전에 게임 등에서 도 이용했던 방법인데 하나의 이미지를 로딩하여 해당 이미지의 일정 부분 만 출력하되 롤오버나 이미지 변환등을 구현할때 해당 이미지의 background-position 을 이동하면서 비슷한 효과를 내는걸 말한다.
2. CDN(Contents Delivery Network) 을 이용하라 ( Use a Contents Delivery Network) 정적 요소( 이미지나 js 등 특별히 변화되는 파일이 아닌 모든것들..) 을 최종 사용자에게 빠르게 전달하기 위해서 사용자의 네트웍과 가장 근접한 네트웍에서 해당 컨텐츠를 전달하는 것이다....
하지만 실질적으로 이정도 규모를 제공할려면 역시 돈이 좀.;;
3. 캐쉬 제어 . ( Add Expires header or Cache-Control ) 일반적인 정적요소들 ( 이미지,js , css 혹은 내용이 변화 될리 없는 정적 문서 및 파일...) 은 캐쉬 유지시간을 아주 길게 잡아주거나 혹은 계속 캐쉬 하게 함으로써 . 브라우저에서 다시 해당 파일을 서버에 요청하지 않도록 한다. 물론 변경을 해야 할때는 해당 파일명을 바꾼다던지 해서 새로이 캐쉬를 하면 된다..
동적으로 변화 되는 부분에 대해서는 정책에 따라 캐쉬를 제어 하는데. 헤더 (Header) 의 Cache-Control 을 이용하거나 혹은 브라우저에서 If-Modified-Since 헤더(Header) 를 이용하여 갱신을 하도록 한다.
한마디로 동적 컨텐츠에 대해선 일정한 정책(policy) 을 세워 가급적 캐쉬 이용을 유도하는 방향으로 처리하는게 좋다는것이다.
4. 압축하라!! ( Gzip Components ) 서버에서 압축해서 보내면 브라우저에서 알아서 압축을 풀게 된다. 요즘 사용하는 모든 브라우저는 기본적으로 제공하니 크게 문제 될리는 없을것이다. (설마 아직도 IE3.01 이런걸 쓰는 사람들이??..) 가급적 모든 요소들( Components ) 을 압축전송을 하는게 좋다.
하지만 -_- 첨부 파일 다운로드 등을 시도할때 gzip 으로 압축해서 보내는 경우 문제가 생기기도 하는데 이럴땐 적절히 대응하는 것이 좋을듯.
5. CSS 선언은 맨 위에! ( CSS at the Top ) FireFox 나 IE 는 CSS 파일이 로딩 될때까지 어떠한 내용도 랜더링 하지 않는다. 심지어 인쇄를 할때도 말이다. 그러니 가급적이면 Style 선언부를 맨 위에 올려 놓는게 좋다.
기억하라 일단 눈에 뭔가를 보여줘야 사용자들은 인내심을 가질 수 있다.
6. Javascript 는 맨 나중에. ( Move Script to the Bottom ) Javascript 도 CSS 와 유사한 문제를 발생시키는데 이를 해결방안은 다르다. CSS 에서는 해당 StyleSheet 가 로딩될 때 까지 페이지 랜더링을 하지 않는데. Javascript 는 해당 스크립트를 로딩하는 동안 그 아래에 있는 모든 컨텐츠 요소들을 랜더링하지 않게 된다. 만약 해당 스크립트 처리에 시간이 오래 걸린다면 결국 전체 페이지 출력 시간에 문제가 될 수 있는 부분이다.
또 하나의 문제는 바로 HTTP 의 다중 연결(parallel downloads) 에 관한 문제다. HTTP 1.1 규약에서는 호스트당 최대 2개 이상의 연결은 할 수 없다. 다만 다수의 호스트에 연결하여 2*n 개의 요소들(Components) 을 받아 올 수 있다( IE 에서는 100개 이상의 이미지를 한번에 로딩이 가능하다 한다..) 하지만 스크립트 파일을 로딩중엔 이것이 불가능하게 된다는 것이다. 결국 페이지 출력시간은 그만큼 딜레이가 된다는 것.!
7. CSS Expression() 표현식은 가급적 자제해라. ( Avoid CSS Expressions ) 마땅히 해석할만한 말이 없어서 '표현식' 이라고 했는데. 내용은 간단하다. CSS 내에서 IE 5 부터 시작된 강력한 기능이라는데. 이건 IE 에서만 되는 기능에다가 또한 많은 연산을 발생시킨다.
한마디로 -_- 써봤자 좋을것 없는 짓이라는 결론..!
8. CSS 와 JS 를 HTML 에서 분리해라! ( Make Javascript And CSS externel ) 분리 해 놓으면 캐쉬정책에 따라 따로 로딩할 필요가 없게 되고 다른 페이지에서도 공통으로 이용 가능할 수도 있게 된다. 물론 좀더 많은 HTTP 연결을 요구하게 되겠지만 만약 이것이 HTML 페이지에 있었다면 그만큼 증가한 파일 사이즈 만큼 매번 로딩하게 될 것이다. 무엇이 유리할지는 당신이 판단하면 좋을듯~ ^^
9. DNS 에 질의를 줄여라! ( Reduce DNS lookups ) DNS 가 어떤 역할을 하는지는 다 알것이다. 도메인 명 을 IP 주소로 변환해주는 역활을 하게 되는데 이것 또한 바로바로 되는것은 아니다. 보통 20~120ms (밀리세컨드) 정도 소요되는데 그동안 브라우져는 주소를 받을때 까진 조용히 손가락만 빨고 기다려야 한다는 것이다.
만약 DNS 정보를 Cache 해 놓는다면 좀 났겠지만 그 역시 ip 가 변동된다면 문제가 될것이다.
그렇지 못하다면 도메인 수 만큼 DNS 에 질의를 던져야 할것이고 그만큼 딜레이가 걸린다고 봐야한다. 하지만 도메인 수가 늘어나게 되면 병렬 처리수도 증가 한다는 이야기므로 적정선을 찾아본다면 좋을것이다. 슬라이드에서는 한페이지당 2~4개 정도의 도메인이 적절하다고 한다.
10. Javascript 의 다이어트! ( Minify JavaScript ) 무슨이야긴고 하니! Javascript 내에 불필요한 부분들은 모두 삭제를 한다는 것이다. 예를 들자면 주석나 긴 변수명을 짧게 한다던지 중간에 공백 문자열을 지운다던지 해서 말 그대로 전체 코드 사이즈를 줄인다는 것이다.
사실 주석 같은거야 인간이 보기 위해서 존재할뿐 컴퓨터에겐 전혀 의미가 없다. 장문의 주석은 컴퓨터에겐 쓸데없는 부분일 뿐이다. 하지만 하지만 코딩하는것은 어디까지나 인간 ! 그렇게 변환된 코드는 디버깅하는데 난해함을 줄 수 있다 .
그렇게 된 소스를 디버깅하는것은 인간의 인내심과 엄청난 시력! 그리고 손가락이 고생하게 된다...ㅡㅡ;;;
슬라이드에는 Dojo Compressor (ShrinkSafe), YUI compressor 나 JSMin 을 소개하고 있는데 Dojo Compressor 같은 경우엔 어느정도 가독성을 보장한 형태로 코드를 다이어트 시켜주므로 한번쯤 써볼만 툴인거 같다.
11. 페이지 리다이렉트 연결은 가급적 자제 .( Avoid Redirect ) HTTP 연결을 시도하지만 바로 다른곳으로 보내게 된다. 결국 그것은 무의미한 HTTP 연결에 시간을 낭비했다고 봐야겠다. 게다가 History Back 버튼에 대한 연결 처리도 신경서야 할것이다.
자주 겪게 되는 리다이렉트 문제가 URL 끝에 디렉토리 PATH 의 슬래쉬(/) 를 빼먹어서 리다이렉트 되는 경우다 Apache 의 mod_rewrite 나 Alias 를 이용해서 처리 할 수도 있다는데.
리다이렉트 를 너무 많이 쓰다보면 개발자들이 혼란을 겪을 우려도 있고 가급적이면 지양해야하는 편이 좋다!
12. 중복된 스크립트 제거 ( Remove Duplicate Scripts ) 굳이 말이 필요할까 ㅡㅡ? 말 그대로 같은 파일을 또다시 로딩할 필요는 없는데 또다시 로딩하지 않도록 제거해야 한다는 이야기다.
13. ETags 설정! ( Configure ETags ) 이것은 브라우져에서 캐쉬된 파일의 갱신 여부를 확인하기 위해서 참조하는 정보이다. 이것을 참조하여 해당 파일이 변동 되었는지 체크하기 때문에 캐쉬 관리에 좀더 도움이 될것이다.
14. Ajax 도 예외는 아니다! ( Make Ajax Cacheable ) 수많은 Web 2.0 Ajax 로 구현한 어플리케이션도 위에서 말했던 요소들처럼 Gzip등을 이용해서 압축전송을 하고 캐쉬가능한 데이터는 캐쉬하도록 구현하는 것이다.!
Ajax 자체가 비동기식 처리 방식 덕분에 페이지 표시에 그다지 문제가 되지 않을것 같지만. 어디까지나 처리하는데 실제 보여지는 페이지에 들어나지 않지만 그것이 페이지 표시에 신속함을 말하는것은 아니다.
게다가 비동기 처리로 인하여 서버로 요청횟수가 기하 급수적으로 늘어날 수 있다. 만약 이것이 비용이 적은 연산이라면 모르겠지만 다수의 메세지를 로딩한다거나. 큰 파일을 불러온다든지의 비용이 큰 연산이라면 그것은 서버의 전체적인 속도에 영향을 미치게 된다.
이를 막기 위해서 가급적 한번 로딩된 데이터들은 캐쉬 해뒀다가 다시 서버로 요청이 가지 않도록 프로그래밍 하는것이 적절할 것이다. 이것은 빠른 응답속도와 서버 리소스의 효율적인 사용을 위해서도 꼭!! 필요한것이다.
여기 까지가 이전에 언급하였던 웹 14가지의 요건들이다.
여기까지 모두 클리어 하였다면 !! ( YSlow 에서 위 조건을 모두 충족 시켜주면 "A" 라는 평가를 받게 된다 ㅡㅡ;;..)
그 다음엔 무엇을 해야 하는가!!!
1. 서버에서는!! ( Tag : Server ) - 버퍼를 빨리 빨리 비워라!! ( Flush the Buffer early ) * Server-Side Script 언어들 에서는 출력을 위해서 버퍼에 처리 결과를 담게 된다. 하지만 중간에 처리되는 부분이 오래 걸리게 되면 출력 시간에 지연이 오게 된다. 이때 미리 처리 된 부분만큼 먼저 버퍼에 쌓인 처리 결과를 출력하는 것이다.! 그럼 보다 빠르게 출력됨을 알게 될것이고 이것은 체감상 빠르게 로딩됨을 느끼게 된다.
- Ajax 요청은 GET 방식으로! * GET 방식이 데이터의 재사용하는데 좋고 POST 방식보다 전송 단계가 짧고 ( POST 방식은 헤더를 전송한뒤 데이터를 전송하게 된다 ) GET 방식의 경우엔 한 패킷에 전송이 가능하기 때문이다. GET 방식으로 전송되는 데이터 양에는 한계가 있지만 ( IE 경우 2KB 가 한계 ..) 굳이 POST 방식으로 보낼만큼의 데이터가 많은 경우가 흔한가??
2. 컨텐츠!! ( Tag : Content ) - 무엇을 나중에 불러 올것인가.? ( Post-load Components ) * 페이지의 어떤 부분이 가장 먼저 보여야 할 것인가 를 결정하는 것이다. 처음 부터 보여지지 않을 부분을 출력하기 위해서 로딩속도를 저하 시켜야 하는가? 페이지가 보여진뒤 로딩하면 그만 아닌가!! 그것을 위해서 Javascript 를 이용하는것이다! ( Rule .6 JS to the Bottom 참고 )
- 어떤것을 먼저 불러야 하는가! ( Preload components ) * 위와 같은 맥락으로 사용자가 이후에 어떤 데이터를 불러 오게 될것인가. 미리 준비한다는 것이다. 분명 사용자들이 보편적으로 이용하는 패턴을 통해서 미리 사용자가 다음엔 이런것들을 찾게 되겠지 하고 미리 그 결과를 만들어 놓고 대기 하는 것이다. 그럼으로 하여 신속한 페이지 이동을 하게 될 수 있게 되는것이다.
구글의 스프라이트 이미지 랄지 , 야후 검색에서 사용자가 다음 어떤 단어를 타이핑할 데이터를 검색 박스에 불러 온다던지 말이다.
- DOM 객체의 숫자를 줄여라! ( Reduce Number of DOM elements ) * 이건 아주 근본적인 이야기다! 가장 빠른 로딩 시간을 보장하는것은 아무것도 띄우지 않은 화면이다. 페이지 에 객체가 늘어나고 복잡해지는 것은 그만큼 전송해야할 양이 늘어난다는 것이고 Javascript 에서 DOM 으로의 접근 속도도 느려지게 된다. 또한 잘못된 Markup 문법 또한 문제가 된다.
- 쪼개라! ( Split components accross domain ) * 최대한 동시 전송 가능한 갯수를 늘려라 . 하지만 너무 많이 늘려서 너무 많은 DNS질의를 보내지 않도록 한다 ( 2~4개 정도 ) IE 8 에서는 호스트당 동시 처리 연결 갯수를 6개 로 늘린다는데..
- Iframe 을 남발하지 말라! ( Minimize the Number of IFRAMEs ) * 광고나 다른 외부 컨텐츠를 불러오는데 유용하게 쓰일 수 있지만. 아무것도 안 부를땐 그냥 낭비일 뿐이고 페이지 로딩에 방해가 된다.
- 못찾는 파일은 없애라! ( No 404s ) * 404 는 Not Found 의 HTTP 오류 코드 다 ㅡㅡ;;; 보통 이렇게 못 찾는 파일이 있을 경우 해당 파일을 호출하기 위해서 계속 로드를 하게 된다 이는 전체적인 로딩 시간을 잡아 먹게 되는데 외부 Javasript 파일을 못찾게 되면 브라우저에서는 계속 로딩할려고 시도하게 되고 결국 다른 요소(Components ) 의 로딩을 방해하게 된다.
3. Cookie ( Tag : Cookie ) - 불 필요한 쿠키는 삭제하고, 쿠키의 크기는 가능한 최소화 한다! * 쿠키로 인하여 브라우져의 응답시간의 지연 현상이 일어나게 된다. 가급적이면 쿠키의 만료시간은 짧게 잡고 사용하지 않는 쿠키에 대해선 바로바로 삭제 해주는 것이 좋다.
4. 자바스크립트! ( Tag: Javascript ) - DOM 접근을 최소화 하라! * DOM Access 는 상당히 느린 편이다. 가급적이면 캐쉬할 수 있도록 하며 Javascript 에 의한 수정은 삼가한다.
- 보다 똑똑한 이벤트 관리방식을 이용하라! ( Develop Smart event handler ) * onload 보단 DOMContentLoaded 를 써라! * 늘어나는 이벤트 들을 위해 attach listener 를 이용하라. ( ...이부분은 좀 -_-..) * IE 메모리 누수 현상이 있을만한 곳은 정리! * onresize 시엔 조심하라!
객체 혹은 다른 요소들(Components) 에 어떠한 이벤트가 호출이 되면 다양한 메소드 들을 호출하게 된다. 또한 요소들의 숫자가 기하 급수적으로 늘게 되면 일일히 각 요소들(Components)에게 등록된 이벤트 들을 수정하기도 힘겨워 지고 코드도 보기 힘들어진다.
그러기 위해 AttachEvent 나 eventListener 같은걸 이용하는것이 적절할 수도.
5. CSS !! ( Tag : CSS ) - StyleSheet 파일은 위에서 언급했던 것 처럼 페이지 로딩 최 상단에 배치한다. - 필터 사용은 자제 ( Avoid filter ) * IE 전용에 메모리도 많이 먹고 랜더링 중에 브라우져를 멎게 할 수도 있다. 게다가 이미지에 먹히는게 아니라 블럭 에 먹히는 거라 원하게 나오지 않을 수도 있다. ** IE6 에서는 문제가 되었지만 IE7+ 에서는 수정 되었다 함
6. 이미지 ( Tag : Image ) - 필요이상의 큰 팔레트 사이즈를 갖는 GIF 이미지를 만들지 말라! 너무 많은 색을 쓰는것도 파일 사이즈를 증가 시킨다. - GIF 를 PNG 로 대체 하라 ..대세는 이제 PNG 다!! - 이미지 헤더의 주석을 삭제하라! 이로써 이미지 용량을 많이 줄일 수 있다.(생각외로 큰듯..) - CSS sprite 최적화! * 가로든 세로든 이동할 방향을 결정하고 비슷한 색을 이용하여 만든다. 단, 너무 크게는 만들지 말라 크게 만들면 파일 사이즈의 크기도 기하 급수적으로 늘어나기 때문이다~ 그러니 되도록이면 바싹바싹 빈공간을 최소화 하는것이 좋다! - 이미지를 HTML 에서 크기를 조절하지 말라! * 원본 크기에 안 맞게 나올 수 있다. - favicon. 는 최대한 작고 저장 가능하게.
7. 이동기기 ( Tag: Mobile ) - 모든 요소들은 가급적 25K 보다 작게 만든다! * iPhone 에선 그보다 크면 캐쉬 불가. * 압축이 풀린 상태에서 25K 가 넘어가지 않게 한다. * HTML 코드도 최소화!
이렇게 최적화를 하는 이유는 앞서 슬라이드에서도 밝혔지만. Server-Side 에서 최적화를 하는데는 분명 한계가 존재하고 들인 공에 비하면 크게 티가 나지 않는 경우가 많다.
실질적으로 사용자들이 체감하는 로딩속도는 최초 클릭후 무엇인가가 출력이 되고 있구나 라는것을 봤을때 부터다.
그 단 몇초만 잡는다면 이용자들에게 보다 쾌적함을 줄 수 있는 서비스가 될것이다.
누구라도 한번 로딩에 수십초씩 걸리는 서비스를 이용하길 바라는 사람은 없을것이다.
당신이 바꾼 한줄의 코드에 사용자들이 얼마나더 쾌적함을 느낄수 있느냐 없느냐는 분명 큰 차이다. 앞으로 웹 서비스는 계속 늘어날 것이다.
물론 모두가 Google 이나 Amazon 같은 서비스가 되진 않겠지만. 많이 티가 나진 않아도 보다 적은 비용으로 당신이 만든 서비스를 더 많은 사람들이 이용 할 수 있게 된다면 그것이 중요한것 아닐까?
0