본문 바로가기
서버구축 (WEB,DB)

넥슨 사이트의 성능 튜닝

by 날으는물고기 2009. 5. 13.

넥슨 사이트의 성능 튜닝

출처 : http://cafe.naver.com/headstudy


안녕하세요~ 이번에 새롭게 튜닝요원으로 활동하게 된 HOONS입니다. 성능에 관한 내용들을 어떤 방식으로 진행해야 보다 재미있고 흥미롭게 다룰 수 있을지 많은 고민을 했습니다. 고민 중에 선택한 방법이 국내의 사이트를 실례로 사이트를 직접 분석해보면 보다 재밌게 내용을 다룰 수 있다는 생각이 들어서 제목을 굉장히 건방지게 잡아 보았습니다.(-_-;) 여기서
국내 사이트를 하나 선정하고 그 사이트에서 성능을 개선할 수 있는 요소들을 찾아볼 것입니다. 그리고 왜 적용해야 하고 어떻게 적용해야 하는지에 대한 내용을 살펴볼 것입니다. 


이번에 다루는 내용이 서버단이 아닌 UI 단의 튜닝이기 때문에 서버 단의 언어는 무관합니다. 하지만 필자가 닷넷에 각별한 애정을 가지고 있기 때문에 넥슨(
http://www.nexon.com/)을 선정하게 되었습니다. 이 웹 사이트는 당연히 IIS 기반의 웹 서버를 사용하고 있을 것입니다. 넥슨은 랭키닷컴의 순위 산출결과 국내 16위에 달하는 꽤 높은 페이지 뷰를 가지고 있는 사이트이고 당연히 성능에 민감할 사이트이기 때문에 분석 사이트로 선정하였습니다. UI 성능 튜닝에 대한 포스트에서는 간혹 C# 코딩과 ASP.NET 예제들이 첨부될 수 있습니다. 닷넷을 알고 있는 개발자라면 더 쉽게 이해할 수 있을 것입니다.

그럼 넥슨의 사이트를 분석해보기 전에 먼저 기본적으로 알고 있어야 하는 브라우저의 기본 동작에 대해서 살펴보도록 하겠습니다.


 
브라우저의 동작
 
이전 포스트에서도 설명했듯이 브라우저의 동작을 모니터링 하기 위해서 IE Watch(http://www.iewatch.com/)라는 툴을 이용할 것입니다. 그리고 보다 자세한 정보가 필요하다고 생각될 경우 IBM Page Detailer(http://alphaworks.ibm.com/tech/pagedetailer)라는 툴을 이용할 것입니다.
 
분석에 앞서서 아주 기본적인 브라우저 동작 개념을 설명하도록 하겠습니다. (혹시나 "아주 기본적인"에 상처 받는 개발자가 없기를 바라며-_-;) 넥슨의 홈페이지를 처음 방문할 경우 HTML뿐만 아니라 이미지, 스타일시트, 스크립트 등 모든 구성요소들을 자신의 PC로 다운 받게 됩니다. 다음 그림은 넥슨 홈페이지를 처음 방문했을 때 순차적으로 다운되는 모습을 보여주고 있습니다.


[넥슨 페이지를 처음 방문할 경우]
 
넥슨은 총 157번의 HTTP 요청을 만들고 있고 페이지 전체 크기는 약 1.1메가 정도 되는 것을 볼 수 있습니다. 가운데 상태코드(Status)를 보면 처음 302를 빼고 모두 200을 반환하고 있는 것을 볼 수 있을 것입니다. 동작 순서를 요약하자면, 브라우저는 처음에 HTML을 다운 받습니다. 위의 분석 결과를 보면 HTML을 다운 받는 시간은 약 0.234초가 소비된 것을 볼 수 있습니다. 이 시간은 바로 서버 단의 동작으로 봐도 무방합니다.(실제로 전달되는 시간을 빼야 하겠지만..) 그 뒤에 브라우저는 HTML을 분석하여 문서 위에서부터 차례대로 구성요소(플래쉬, 스크립트, 이미지등)들을 다운로드 하게 됩니다. 그렇게 모든 구성요소를 다운 받는 시간은 약 2.3초가 걸린 것입니다. 그리고 다운로드 된 구성요소들은 인터넷 임시파일 폴더에 저장됩니다. 여기서 중요한 것은 브라우저는 다운로드를 진행하면서 로딩되는 과정을 눈으로 볼 수 있게 지원하고 있다는 것입니다. 즉, 다운받는 즉시 브라우저에 표현해 주고 있다는 것입니다.(너무 당연한 거였나-_-;) 만약 그렇지 않으면 우리는 사이트가 굉장히 느리다고 생각할 것이다. 하지만 이런 로딩 화면을 지원하지 않게 될 경우가 있습니다. 그건 뒤에 포스트에서 다루도록 하겠습니다.
 
그럼 넥슨 페이지를 다시 한번 호출해 보면 어떻게 될까요? 이건 2번째 요청입니다. 즉, 처음 다운 받았던 여러 구성요소들은 이미 인터넷임시 폴더에 저장되어 있는 상태인 것입니다. 그럼 브라우저는 어떻게 동작되는지 살펴보도록 하겠습니다.
 


[넥슨 페이지를 두 번째 방문할 경우]
 
두 번째 방문할 경우 전체 응답시간이 1.5초로 줄어든 것을 볼 수 있습니다. 여기서 페이지 사이즈는 전달된 양과는 무관한 전체 구성요소의 합계를 나타내고 있습니다.(고물-_-;) 때문에 IBM 패킷 탐지기로 전체적으로 얼마나 다운받게 되는지 관찰해 보았습니다. 관찰해 본 결과 전체 사이즈는 0.16M 정도 되는 것을 확인할 수 있었습니다. 왜냐하면 이전에 이미 다운 받아 놓은 구성요소들이 인터넷 임시파일 폴더에 저장되어 있기 때문에 변경되지 않았다면 다시 다운로드 되지 않는 것이죠. 그런데! 왜 요청 수는 155로 줄지 않을 것일지 의문이 들 수 있을 것입니다. 그 이유는 브라우저는 구성요소가 디스크에 존재할 경우 이 요소가 변경이 되었는지 확인하는 요청을 보내게 되는 것입니다. 전문적인 용어로 Conditional Get Request라고 합니다.(역시 전문 용어를 써야 가오가 사는군요-_-;) 서버에게 수정되었는지 물어보고 만약 수정되지 않았다면 304 응답을 보내게 되고 수정되었다면 그 파일을 다시 보내주게 됩니다. 그렇기 때문에 HTTP요청은 반드시 일어나게 됩니다.
위의 화면을 보면 페이지의 구성요소들의 상태코드들이 304라는 응답을 받는 것을 볼 수 있을 겁니다.

브라우저는 요청–응답 방식으로 서버에 요청하고 응답을 받아 처리하게 됩니다. 브라우저는 다음과 같은 요청을 보내게 되고, 넥슨에서는 아래와 같은 요청들이 155번 일어났다고 보면 됩니다. 다음 요청은 넥슨의 로고가 수정되었는지 물어보는 요청입니다.
 
요청(Request)

브라우저->서버
GET /S2/Portal/Nexon/Nexon2007/image/global/logo_nx.gif HTTP/1.1
Accept: */*
Referer: http://www.nexon.com/main/page/nx.aspx
Accept-Language: ko
UA-CPU: x86
Accept-Encoding: gzip, deflate
If-Modified-Since: Fri, 25 Jan 2008 08:39:11 GMT
If-None-Match: "5f655c22d5fc81:6f"
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; ..
Host: s.nx.com
Connection: Keep-Alive
응답(Reponse)

서버->브라우저
HTTP/1.1 304 Not Modified
Date: Mon, 03 Mar 2008 12:12:44 GMT
Etag: "5f655c22d5fc81:6f"

 
필자는 이 요청-응답의 로그만 보아도 어떤 부분을 튜닝해야 해야 할지 대충 구상이 잡히고 있습니다. 튜닝을 위해서는 이 응답과 요청의 속성들을 이해하고 있어야 합니다. W3G 홈페이지(영문)를 방문해서 HTTP에 대한 스펙을 찾아보면 이 속성들에 대한 스펙을 정의하고 있는 것을 볼 수 있을 것입니다. 그럼 서버에서는 이 이미지가 최신 버전인지 어떻게 확인할까요? 이것은 HTTP 버전과 IIS의 설정에 따라 다릅니다. HTTP 버전은 1.0과 1.1이 존재하지만 대부분의 브라우저들은 1.1을 사용하고 있습니다. HTTP 1.1을 이용할 경우 서버는 Etag라는 것을 브라우저에게 발행하게 됩니다. Etag는 파일의 정보와 IIS 서버의 정보를 요약하여 만들어지는 파일의 고유한 아이디와 같은 값이라고 보면 됩니다. HTTP1.1을 이용할 경우 IIS의 서버에서 별다른 작업이 없을 경우 Etag값을 이용해서 파일이 서버의 내용과 일치하는지 확인하게 됩니다. 위의 요청(Request)을 보면 If-None-Mach를 보내게 되는 것을 볼 수 있습니다. (If-None-Match: "5f655c22d5fc81:6f") 그리고 만약 HTTP1.0을 이용하고 있을 경우 최근 수정 일을 통하여 파일을 비교하게 됩니다.( If-Modified-Since: Fri, 25 Jan 2008 08:39:11 GMT) 여기서 넥슨은 Etag를 사용하고 있는것을 볼 수 있는데 ETag는 성능을 떨어뜨리는 결정적인 문제를 가지고 있습니다. 이 부분의 내용은 다음 포스트에서 자세하게 살펴보도록 하겠습니다.
 

요약

넥슨 사이트의 성능을 올리기 위한 방법들을 간략하게 요약해 보겠습니다. 먼저 캐시의 만료기한을 지정하여 수정되었는지에 대한 확인 요청조차 만들지 않게 할 수 있습니다. 그리고 여러 가지 방법들을 적용하여 HTTP 요청을 최소화해야 합니다. 그리고 텍스트 파일들은 압축을 적용할 수 있지만 넥슨은 적용하지 않고 있습니다. 또한 굉장히 많은 스크립트들을 사용하고 있지만 최소화 하지 않았고 스크립트의 위치 또한 좋지 않습니다. 마지막으로 동시 다운로드를 이용하여 보다 빠르게 다운 받게 할 수도 있습니다. 등등 지금까지 필자가 언급한 내용들은 다음 포스트에서 천천히 살펴보면서 왜 적용해야 하고 또한 어떻게 적용해야 할지 살펴보도록 할 것입니다. 이 내용들을 적용할 경우 분명 2초에서 1초혹은 그 이상으로 웹 사이트의 성능을 끌어 올릴 수 있을 것입니다. (확신합니다_-_;)


이제 하나 둘씩 넥슨 사이트를 튜닝할 수 있는 기술의 실마리를 풀어 보도록 하겠습니다. 단, 넥슨을 통해서 살펴볼 부분은 UI단(Frontend)의 기술을 튜닝하는 것입니다. 때문에 메모리 부하로 웹 서버가 죽거나 하는 문제는 UI단의 기술과는 상관 없는 부분인 것입니다. (해깔리지 말라는 의미로 ^^)

서버 단의 튜닝기술은 넥슨에 대한 포스트를 끝낸 후에 이어서 진행하도록 하겠습니다. 맨 처음 포스트에서도 다루었듯이 사이트의 성능에 문제가 있다고 판단되는 경우 그 문제가 UI단의 동작인지 서버 단의 동작인지 판단해서 튜닝을 진행해야 합니다. 그럼 먼저 캐시에 대한 이야기를 수사를 진행해 보도록 하겠습니다.



 
캐시(Cache)의 활용
 
넥슨은 캐시를 활용하지 않고 있습니다. 보다 정확하게 말하자면 캐시에 저장되는 구성요소(이미지, 스크립트, 스타일)들에 만료기간을 지정하지 않고 있는 것입니다. 그렇다면 먼저 캐시는 무엇을까요? 캐시는 검색된 웹 페이지 정보가 저장된 메모리 또는 디스크의 임시 기억 장소를 말합니다. 즉, 넥슨 페이지를 방문한 후에 넥슨 페이지에 있던 모든 파일들은 바로 캐시에 저장하게 되는 것입니다. 때문에 사이트를 재방문 해도 그 파일이 변하지 않았다면 캐시에서 파일을 불러와 로드하기 때문에 훨씬 속도를 높일 수 있습니다.
 
그렇다면 만료기간은 무엇이고 넥슨(
www.nexon.com)에서 그걸 어쨌다는 겁니까? 

다음 화면은 넥슨 사이트에 재방문 하게 되었을 때 일어나는 동작을 요약한 화면입니다.


[넥슨 사이트를 재방문 했을 때의 통계]
 
넥슨을 재방문 해보면 이미 다운받은 파일은 받지 않고 않지만 155번의 HTTP Request가 발생하고 있다는 부분이 바로 오늘 풀어야 할 과제입니다. 이 요청은 파일은 이미 다운 받았지만 최신 파일이 맞는지 확인하기 위해서 서버에 물어보는 요청인 것입니다. 하지만 헤더의 Expires나 Cache-Control을 통하여 만료기간을 지정하게 되면 이 요청은 그 기간 동안 일어나지 않게 됩니다. 예를 들어 만료기간을 하루로 지정하였다면 하루 동안은 무조건 최신 파일이라고 가정하고 확인 요청을 서버로 보내지 않게 됩니다.
 
다음 포스트에 다룰 내용의 주제이지만 이런 HTTP 요청을 줄이는 것이 성능을 개선하기 위해서 가장 핵심적인 부분입니다. 넥슨을 재방문 했을 때의 상태코드를 보면 대부분 304로 나오는 것을 볼 수 있습니다. 304 요청은 서버의 버전과 클라이언트가 가지고 있는 파일과 일치한다는 코드입니다. 이런 요청 자체를 주고 받지 않는다면 사이트의 속도는 엄청나게 향상됩니다.
 
단, 여기서의 속도는 처음 방문한 사용자가 아닌 재방문일 경우일 때의 속도입니다. 넥슨 사이트는 한번 방문했던 사람이 꾸준히 방문하는 충성도 높은 유저들이 굉장히 많을 것입니다. 게임을 하는 사람들 정말 꾸준히 방문하죠. 즉, 어쩌다가 정보가 필요할 때 방문하게 되는 사용자는 그렇게 많지 않을 거라는 것입니다. 이점으로 미루어 볼 때 분명 이 작업은 상당한 가치가 있다고 생각하고 못해도 200% 이상의 성능을 끌어 올릴 수 있을 것입니다.
 
“그럼 만약 홈페이지를 수정하게 될 경우 어떻게 되나요? 이미지가 변경되었습니다. 그래서 사용자에게 그 이미지를 내려 주어야 하는데 사용자가 가지고 있는 파일은 아직 만료기간이 지나지 않았습니다.” 이런 생각을 먼저 머리 속에 그리는 분들이 있을 것 같은데요 이 부분은 맨 뒷부분에서 언급하도록 하겠습니다.
 


Expires/Cache-Control
 
컨텐츠의 만료기간을 지정하는 방법은 Expires를 지정하는 방법과 Cache-Conrol을 이용하는 두 가지 방법이 존재합니다. Expires는 HTTP 1.0에서부터 지금까지 사용하고 있는 방법이고 Cache-Control은 HTTP 1.1에서 새롭게 탄생한 녀석입니다. 99%는 HTTP 1.1을 사용하고 있다고 보면 되지만 그래도 HTTP 1.0 사용자도 무시할 수 없기 때문에 둘 다 지정해 주는 것이 좋습니다. 여기서 먼저 만료기간을 지정하여 사용하고 있는 다음(
www.daum.net)의 로고의 응답을 받아 보도록 하겠습니다.
 

요청(Request)
GET /top/2008/logo_daum2008.gif HTTP/1.1
 
… 생략 …
응답(Reponse)
HTTP/1.1 200 OK
Cache-Control: max-age=7776000
Expires: Mon, 02 Jun 2008 12:09:53 GMT

 
… 생략 …
 
이 파일은 Cache-Control과 Expires 둘 다 지정하여 전달하는 것을 볼 수 있습니다. Expires에 지정된 날짜까지는 확인 요청(Conditional Get Request)을 보내지 않고 무조건 캐시에 저장된 파일을 이용하게 됩니다 이 날짜가 지나게 되면 서버로 확인 요청을 보내게 되겠죠. 그럼 Cache-Control: max-age=7776000은 무엇을 의미 할까요? 이것은 7776000초가 지나지 않을 때까지 요청을 보내지 않겠다는 것입니다. 이렇게 둘 다 지정되어 있을 경우 Cache-Control이 우선권을 가지게 됩니다. 
 


성능 테스트
 
그럼 만약 넥슨 사이트의 파일에 만료기간을 지정할 경우 성능이 얼마나 향상될지 궁금하지 않습니까? 그래서 HOONS요원이 테스트를 해보도록 하겠습니다. 상당히 귀찮은 작업이지만 처음 맡겨진 임무인 만큼 최선을 다해보겠습니다.(^_^;) 먼저 HOONS의 컴퓨터를 넥슨과 비슷한 환경의 IIS서버를 구성해야 합니다. 하지만 비밀요원인 HOONS가 넥슨 사이트의 소스를 가지고 있는 것도 아닌데 어떻게 구성해야 할까요? 여기서 HOONS가 테스트하고자 하는 것은 단순히 만료기간을 지정했을 때의 그 속도를 보는 것입니다. 때문에 넥슨에서 인터넷 임시폴더로 다운받은 파일만 고대로 옮기면 되는 것입니다. 그럼 넥슨 사이트에서 다음 그림과 같이 다른 이름으로 파일을 내보내겠습니다.
 

[넥슨 페이지를 다른 이름으로 저장]
 
이제 내보낸 HTML파일과 파일 폴더를 IIS서버를 생성한 후에 옮겨 보도록 하겠습니다. 
 

[넥슨 웹 서버의 폴더]

 
150개 정도의 파일이 와야 하지만 HOONS가 입수 할 수 있었던 파일은 99개 였습니다. 나머지는 절대 경로를 이용하여 직접 요청하거나 IFrame이 있을 가능성도 있습니다. 아무튼 99개로도 충분히 성능 테스트가 가능합니다. 그럼 HOONS가 둔갑시킨 가짜 넥슨 사이트를 호출해 보도록 하겠습니다.
 

[로컬에서 최초의 요청]
 
처음 요청의 경우 1.1초가 나오는 것을 볼 수 있습니다. 로컬에서 작업하기 때문에 실제 넥슨 사이트 보다 응답속도가 빠른 것을 볼 수 있습니다. 우리가 테스트하고자 하는 것은 재방문시의 속도이기 때문에 다시 한번 호출해 보겠습니다. 두 번째 요청에서는 파일을 직접 다운로드 하지 않고 최신 파일 여부만 확인하게 됩니다.


[평범한 2번째 호출]
 
그럼 이제 캐시의 만료기간을 지정해 보도록 하겠습니다. 만료기간을 설정하기 위한 방법이 2가지가 존재합니다. 하나는 IIS에서 지정하는 방법이고 다른 하나는 닷넷의 http핸들러를 이용해서 요청을 가로챈 후에 헤더를 추가해주어 내려 보내는 방법입니다. 전자의 경우 폴더 별로 지정하거나 파일 하나하나 수동으로 지정해 줄 수 있습니다. 일단은 쉽게 IIS에서 직접 설정하는 방법으로 진행해 보도록 하겠습니다. 
 
 


[IIS에서 제공하는 기능을 이용할 경우]
 

[Cache-Control헤더에 max-age를 직접 추가할 경우]

파일 개개로 열어서 만료기간을 지정해도 되고 폴더 전체를 열어도 됩니다. HOONS는 폴더 전체에 만료기간을 설정해 보도록 하겠습니다. 그럼 이렇게 만료기간의 지정은 끝났습니다. 이제 전체 요청 수와 전체 시간을 측정해보도록 하겠습니다. (과연 결과는 두구두구..)

[만료기간을 지정했을 때의 재방문]

 
위의 화면을 보면 대략 90개의 요청이 줄어든 것을 볼 수 있습니다. 그리고 시간도 0.73초에서 0.34초로 거의 50%로 줄어든 것 볼 수 있습니다. (테스트한 보람이 있군요^^;) 나머지 60개의 요청에서 줄일 수 있는 요소들을 더 줄인다면 그만큼 성능은 향상될 수 있을 것입니다. 좀 더 정교하게 테스트를 하려면 저 60개의 요청을 빼고 했어야 했겠지만 그냥 처음 수사이니 양해 부탁 드리겠습니다.(_ _;) 테스트할 때 주의 할 점이 있습니다. [F5]를 누를 경우, 즉 페이지를 새로고침 할 경우 만료기간에 상관없이 서버로 파일이 최신 파일인지 확인하는 요청(Conditional Get Request)을 보내게 됩니다. 때문에 개발자는 만료기간이 지정되었다 하더라도 수정한 내용이 잘 반영되었는지 쉽게 확인할 수 있습니다. 
 



   
 

쉬어가는 문제

 
   


만료기간 설정 in Code
 
이제 어느 정도 감이 오십니까? 아직 머리가 알쏭달쏭 하다면 위에서부터 다시 천천히 읽어보셔야 다음 수사를 쉽게 따라올 수 있을 것입니다. 위에서 살펴본 방식은 IIS에서 설정한 부분이고 이제 코드를 이용해서 여러 파일에 캐시 만료기간을 설정해 보도록 하겠습니다. 이 작업을 위해서 닷넷의 aspnet_wp.exe에서 동작되는 httpHandler를 이용할 것입니다. 뭐; 이 개념을 알고 있다면 상관없습니다만, 닷넷에 입문한지 얼마 안되었고, ASP.NET 동작 구조가 머리 속에 잘 들어와 있지 않다면 안재우님의 강좌(
http://blog.naver.com/saltynut?Redirect=Log&logNo=120027090312)를 한번 읽어 보는 것을 추천해드립니다. (^^) HOONS요원은 성능 튜닝 임무에 충실할 예정이기 때문에 링크하나로 넘어감을 이해해 주시길 부탁 드리겠습니다 (_ _;)
 
aspnet_isapi.dll라는 ISAPI 처리기는 일반적으로 닷넷과 관련된 요청만 받아서 처리합니다.(aspx, asmx 등의 요청들) 그렇기 때문에 이미지나 자바스크립트와 같은 파일은 ISAPI 처리가 처리하지 않도록 설정되어 있기 때문에 이 부분을 수동으로 추가해주어야 합니다. IIS 등록정보에서 구성에 확장자와 ISAPI 처리기와 매핑하는 처리가 가능합니다. 한방에 따라 할 수 있는 그림 하나 첨부합니다.
 

[이미지 확장자와 aspnet_isapi.dll과의 매핑]
 
보통 닷넷에서 이미지 작업을 할 때는 ashx라는 확장자를 이용합니다. 그러면 이렇게 추가해주지 않아도 작업이 가능합니다. 예를 들어 <img src=”hoons.jpg”>를 서버에서 처리하고 싶다면 <img src=”hoons.jpg.ashx”> 이렇게 주소를 설정하고 httphandler에서 받아 처리할 수 있습니다. 그럼 간단하게 코드를 살펴보겠습니다. 먼저 web.config에 httpHandler와 연결해주는 설정을 추가하였습니다.

<httpHandlers><?xml:namespace prefix = o /><?xml:namespace prefix = o />
  <!--*이미지포맷 설정*-->
  <add verb="*" path="*.gif" type="CacheControl"/>
  <add verb="*" path="*.jpg" type="CacheControl"/>
</httpHandlers>

[Web.Config 설정]
 

public class CacheControl : IHttpHandler
{
    public CacheControl()
    {    }

    #region IHttpHandler Members

   
public bool IsReusable

    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        string file = context.Server.MapPath(context.Request.FilePath);//파일 경로
        string filename = file.Substring(file.LastIndexOf('\\') + 1);//파일 이름
        string extension = file.Substring(file.LastIndexOf('.') + 1);//파일 확장자

        //Cache-Control:max-age=31536000으로 설정하기 -> 365
        context.Response.Cache.SetMaxAge(new TimeSpan(365, 0, 0, 0, 0));

        //ContentType설정
        if (extension == "gif")
            context.Response.ContentType = "image/gif";
        if (extension == "jpg")
            context.Response.ContentType = "image/jpeg";

       
//
화면 출력
        context.Response.AddHeader("content-disposition", "inline; filename=" + filename);
        context.Response.WriteFile(file);
    }
    #endregion
}


친절한 주석으로 코드는 충분히 이해할 수 있을 것입니다. 이 프로젝트는 파일로 첨부할 것이기 때문에 다운받아서 확인해 보시기 바랍니다. 실행하면 gif와 jpg 이미지는 모두 365일의 만료기간을 설정하게 됩니다. 그럼 헤더를 살펴보도록 하겠습니다. 
 


[이미지에 동적으로 추가된 만료기간]
 
헤더를 확인해보면 Cache-Control의 max-age값이 설정되는 것을 볼 수 있습니다. 후후; 그런데 IEWatch툴의 버그가 발견되었습니다. 이상하게 코드에서 설정할 경우 Cache를 이용하고 있음이 분명함에도 전체 Request 수는 그대로 나오더군요. 전 포스트에서도 파일사이즈 버그를 포스팅 했던 것 같았는데요. 이거 장비가 이래서 수사나 제대로 할 수 있겠습니까? 참고로 HOONS는 예전부터 이 툴을 사용해왔기 때문에 30일 동안 쓸 수 있는 무료기간을 이미 다 소진했습니다. 하지만 날짜를 뒤로 돌리면 불편하지만 쓸 수 있더군요. 때문에 제 컴퓨터의 달력은 항상 뒤로 갑니다. (ㅠ_ㅠ;) 누가 가난한 HOONS요원에게 후원을.. 응? (-_-;)
 
아무튼 이 예제에서는 이미지만 처리하게 설정되었지만 이미지뿐만 아니라 자바스크립트, 스타일시트, 플래쉬 등등의 여러 파일에 몽땅 만료기간을 추가하면 성능은 그만큼 좋아지게 될 것입니다.
 


서버의 파일이 변경되었을 경우는?
 
앞에서도 언급했듯이 만료기간을 지정하면 그 기간이 지정되기 전까지 캐시에 있는 파일들을 사용하게 됩니다. 하지만 서버에서 업데이트가 있었고 더 이상 그 구성요소를 사용하지 않을 경우에 어떻게 해야 할까요? 참고로 네이버나 다음과 같은 사이트 또한 1달 이상의 만료기간을 설정하여 사용하고 있습니다. 그럼 큰 사이트는 업데이트가 거의 안되기 때문에 이렇게 지정하여 사용하고 있는 것일까요?
 
이 부분을 해결하는 방법은 간단합니다. 업데이트 될 때마다 파일 이름을 변경해주면 되는 것입니다. 예를 들면 현재날짜_시간(hoon_20080305_0123.js)과 같은 파일 포맷을 지정하여 사용하는 것입니다. 이미지나 스크립트나 모든 파일에 말입니다. 이 부분도 배포과정에서 자동화하여 처리할 수 있을 것입니다. 또 큰 서비스의 회사일수록 업데이트 빈도는 적을 것이고, 업데이트 되면 보통 새로운 파일명을 사용할 경우가 많습니다. 때문에 충분히 가치 있는 일이 될 것입니다.
 


정리
 
회사에서 저녁 먹고 7시부터 쓰기 시작했는데 예제 만들고 캡쳐하고 하다 보니 새벽 2시가 되어 가는군요. 후다닥 정리하고 찜질방 가야겠습니다. (-_-;)
 
헤더의 만료기간을 지정하면 넥슨의 경우 사이트의 속도를 절반 이상 줄일 수 있습니다. 이번 수사에서는 150개의 요청 중에 100개 정도의 요청을 줄였는데도 사이트의 성능은 2배 정도 빨라지는 것을 눈으로 확인하였습니다. 하지만 HOONS의 수사는 이게 전부가 아닌 이제부터가 시작인 것입니다. 뒤에 다루는 내용까지 모두 적용한다면 성능은 더 끌어 올릴 수 있을 것입니다. 단, UI단의 성능입니다.(^_^)

일반적으로 서버 단에서 성능을 튜닝하기 위해서 어떻게 합니까? DB 구조 바꾸고, 코드 다시 짜고 프레임구조 바꾸고 하지 않습니까? 이런 서버 단의 작업은 쉽지 않기 때문에 굉장히 오래 걸리는 작업이고 유지보수 하는 개발자로서는 당연히 하기 싫은 일이 될 것입니다. 또 그렇게 크게 빨라진다는 효과를 눈으로 확인하기 힘듭니다. 하지만 지금 HOONS 요원이 설명한 이 방법을 맘먹고 진행하면 일주일 안에 충분히 사이트에 적용할 수 있을 것입니다. 
 

오늘은 재방문이 아닌 처음 방문할 때의 성능을 올리기 위한 방법을 살펴보고자 합니다. 다시 한번 강조하는 것은 이번 넥슨 사이트 관련 포스트는 UI 단의 성능 올리기 위한 수사를 진행하고 있는 것입니다. 넥슨 사이트의 경우 HTTP 요청이 150번 이상 일어나고 있는 것을 볼 수 있습니다. 이 요청을 최대한 줄이면 줄인 만큼 성능은 향상 됩니다. 그럼 넥슨은 150번인데 다른 게임 사이트들은 어떨까요? 넥슨은 많은 편에 속하는 걸까요? HOONS요원이 다른 게임 사이트들의 요청(Request)수를 한번 조사해 보았습니다. 또한 캐시를 어떻게 활용하고 있는지도 한번 조사해 보았습니다. 

 
사이트 이름
요청 수
비고
한게임(http://www.hangame.com/)
212개
- 캐시 만료기간 사용
- Active-X 설치 2번 물어봄
넷마블 (http://www.netmarble.net/)
153개
- 캐시 만료기간 사용하지 않음
- Active-X 설치 1번 물어봄
피망 (http://pmang.sayclub.com/)
351개
- 캐시 만료기간 사용
- Active-X 설치 없음
[3개 게임 사이트의 요청 수]

다른 게임 사이트를 비교해봤을때 넥슨이 거의 가장 가벼운 사이트로 볼 수 있습니다. 한게임 경우 로딩 중에 즉, 화면이 하얀 상태에서 Active-X 설치 여부를 묻게 됩니다. 이 부분이 좀 아쉬웠지만 대신 여러 파일들이 만료기간을 대부분 지정하고 있었습니다. 넷마블은 요청 수는 비교적 적었으나 캐시 만료기간을 지정하지 않고 있었고 피망 사이트는 만료기간은 사용하고 있지만 요청되는 파일이 상대적으로 많았습니다. 캐시 만료기간을 지정하지 않은 사이트는 넷마블과 넥슨이었고 피망과 한게임은 지정해서 사용하고 있었습니다. 여기서 HOONS의 짐작으로 네오위즈(피망)는 세이클럽, NHN(한게임)은 네이버와 같은 더 큰 규모의 사이트들을 운영해오고 있기 때문에 사이트 성능에 노하우가 반영된 거라 생각됩니다. 하지만 넷마블이나 넥슨은 게임 사이트 요청 수가 비교적 적다는 부분에서 좋은 점수를 주고 싶습니다. 
 
그렇다면 HTTP 요청을 줄이면 왜 성능이 좋아질지 먼저 간단하게 언급해 보겠습니다. 사이트의 성능은 파일이 무겁고 안 무겁고 보다는 얼마나 많은 요청들을 주고 받는가에 따라서 많이 좌우됩니다. 예를 들면 200K 짜리 5개를 다운 받는 것 보다는 1M 짜리 1개가 훨씬 빠르다는 것입니다. 그렇다면 왜 그럴까요? HTML 페이지 안에 있는 하나하나의 파일들은 브라우저에서 TCP 요청에 의해서 전달됩니다. 물론 Keep-Alive라는 헤더 속성을 이용해서 연결을 유지해서 다음 요청으로 전달되지만 그 요청을 동시적으로 보낼 수 없기 때문에 줄줄이 앞의 요청이 끝나기 까지 기다리게 됩니다. 현재 인터넷 익스플로러 그 각각의 연결자체가 시간이 소비되는 것이고 또한 그 파일을 요청하기 위해서 파일마다 생성되어 주고 받는 Header값 또한 무시 못합니다. 때문에 하나로 합치는 작업이 중요한 것입니다. 자, 그렇다면 이제 HTTP 요청을 줄이기 위해서 어떤 작업들을 진행해야 할지 살펴보도록 하겠습니다. 
 

 

이미지맵의 활용
 
언제부터인가 디자이너들 사이에서 “통 이미지는 최대한 잘라라”가 일반적인 공식이 되어 버렸습니다. 그 이유는 뭐 로딩이 느리기 때문에 조금씩 빨리 보여주기 위해서였습니다. 하지만 그렇게 자른 이미지가 전체적으로는 더 느리게 전달됩니다. 왜냐하면 이미지를 자른 만큼 HTTP 요청이 늘어나기 때문입니다. 여기서 우리는 성능을 위해서 이미지맵으로 돌아가야 합니다.
 
무조건 이미지 맵으로 만들라는 뜻은 아닙니다. 이미지가 연결되어 있고 단순한 링크의 개념으로 사용하고 있다면 이미지 맵을 이용하라는 것입니다. 그럼 넥슨 홈페이지에서는 이미지맵을 적용할 수 있는 부분은 어디일까요? 다음 그림을 살펴봅시다.

[넥슨 홈페이지]

 
다음과 같은 부분을 이미지 맵으로 처리하면 성능을 더 좋게 만들 수 있을 것입니다. 여기서 HOONS는 아래의 게임을 설명하고 있는 부분을 이미지 맵으로 처리해 보도록 하겠습니다. 먼저 공정한 테스트를 위해서 중간 부분만 때서 새로운 HTML파일에 저장하였습니다. 그리고 속도와 요청 수를 측정해 보았습니다. 
 
[일반적인 요청]
 
약 0.09초 정도 걸리는 것을 볼 수 있습니다. 물론 로컬이기 때문에 무지 빠릅니다. 실제로는 2배정도 걸린다고 생각하면 될 것입니다. 요청은 34번이 일어났습니다. 이제 저 이미지를 통으로 만들도록 하겠습니다. HOONS는 하나의 이미지로 만들 것이지만 실제로 4~5개 정도로 쪼개서 만드는 것이 기능성 면에서 더 유용할 것 같기도 하네요. 그럼 하나의 통 이미지로 만들었을 때의 요청 수는 얼마나 나올까요? 또 시간은 어떻게 나올까요? 혹시나 이미지 사이사이의 공백들이 이미지로 들어갔기 때문에 더 용량이 커져서 느려지지는 않을까요? 그럼 결과를 살펴봅시다. 
 
[이미지맵으로 변환했을 때]
 
요청은 단순하게 2번 일어났고 요청 시간은 0.016초가 걸리게 되었습니다. 즉, 6배정도 빨라지게 되는 것입니다. “여기서 잠깐! HOONS요원, 그런데 고작 0.07초 빠르게 하려고 이미지 맵으로 처리하라는 건가요?” 아닙니다. 앞에서도 말했지만 이 테스트 환경은 저의 로컬이기 때문에 그렇습니다. 별로 신비성이 없다고 생각할 수 있기 때문에 HOONS요원의 개인 서버로 올려서 테스트 해보았습니다. URL은 다음과 같습니다.
 
일반 이미지: http://blog.hoons.kr/NexonExpires/이미지맵적용전.htm
이미지맵 적용: http://blog.hoons.kr/NexonExpires/이미지맵적용후.htm
 
그럼 속도는 어떻게 나올지 조사해 보도록 하겠습니다.

[HOONS 서버에서의 테스트]
 

0.5초 정도 절감할 수 있는 것을 볼 수 있습니다. 이번 테스트에서는 일부로 HTML을 무겁게 하였습니다. 이전 포스트에서 조사했을 때 넥슨 메인화면의 속도는 2초였습니다. 이렇게 0.5초를 줄이면 25%나 되는 성능이 향상되는 것입니다. 화면에 보이는 것과 같이 용량은 더 많습니다. 하지만 그 속도는 6배나 빠른 것을 볼 수 있습니다. 예전부터 이미지를 잘게 쪼개는 습관이 사이트의 성능을 더 망가뜨리고 있는 것입니다. 반드시 기능 구분이 필요하고 개체의 개념으로 그 개체가 독립되어야 할 경우에만 이미지를 쪼개서 사용하고 나머지는 이미지맵을 활용하는 것이 성능에 훨씬 좋습니다.
 

 
자바스크립트 & 스타일시트 합치기
 
넥슨은 총 18개의 자바스크립트와 1개의 스타일시트를 사용하고 있습니다. 스타일시트 1개를 사용하는 것은 굉장히 좋지만 스크립트가 18개라는 점은 조금 아쉽습니다. 그 중에 9개의 스크립트는 페이지 상위에 위치하고 있습니다. 이 스크립트를 하나로 합친다면 성능은 향상됩니다. 스크립트를 기능별로 쪼개서 사용하고 있는 경우가 일반적일 것입니다. 하지만 18개는 상대적으로 좀 많이 사용하고 있는 것입니다. (-_-;)실제로 개발은 그렇게 한다 하더라도 배포 전에 하나로 합쳐서 내보내는 작업을 배포 프로세스로 포함시킬 수 있을 것입니다. “아니~ HOONS요원, 성능이 얼마나 올라간다고 이런 귀찮은 작업을 하자는 것입니까?” 성능은 귀찮음과 비례합니다. 성능이 빠를수록 사용자의 경험은 높아지게 되고, 서버의 부하도 줄일 수 있습니다. 요청 수가 줄기 때문에 당연히 서버의 성능도 올라갈 수 있는 것이고 서버의 부하가 적으면 서버 비용이 절약 되므로 결국 회사와 사용자 둘 다 윈윈 할 수 있는 작업입니다.


 
모든 스크립트를 하나로 합치는 것은 불가능할 것입니다. 스크립트가 모여있지 않고 다른 곳에 위치하고 있다는 것은 document.write와 같은 뭔가 UI와 관련된 작업을 하기 위해서일 가능성이 높습니다. 때문에 여기서 합쳐서 테스트 해 볼 스크립트는 모여있는 스크립트 입니다. 조사해본 결과 9개의 스크립트가 HEAD 영역 안에 모여있었습니다. 그 중에 제일 처음 스크립트는 5개의 스크립트를 다운로드 하는 스크립트였습니다. 즉, 처음 페이지에서 14개나 다운 받게 된다는 것입니다. 그럼 14개의 요청을 하나로 줄였을 때 어떤 성능 향상을 가져올 수 있는지 확인해 보도록 하겠습니다.
 
HOONS가 Copy&Paste작업으로(T_T) 다음과 같이 테스트 파일을 만들었습니다.
 
14개의 스크립트: http://blog.hoons.kr/NexonExpires/9개의스크립트.htm
1개로 모은 스크립트: http://blog.hoons.kr/NexonExpires/1개의스크립트.htm
 
“9개의스크립트.htm” 이 파일명은 처음에 스크립트가 9개인 줄 알고 테스트하려고 만들게 되었습니다. 여기서 HOONS는 14개의 스크립트를 모두 1개의 스크립트로 모으는 작업을 하였습니다.(ㅠㅠ) 그럼 결과를 확인해보도록 하겠습니다.

[14개의 스크립트와 1개의 스크립트 차이]
 

대략 50% 정도 성능 향상이 되는 걸 볼 수 있습니다. HOONS요원의 서버가 그렇게 빠르지 않기 때문에 시간은 좀 많이 나오는군요. 앞에서 언급했듯이 귀찮음과 성능과 비례합니다. 개발 생산성이나 기능성 면에서 효율이 좀 떨어지다 하더라도 스크립트의 수를 줄이면 성능은 향상됩니다. 더욱이나 이 스크립트들은 Head에 위치하고 있습니다. Head에 두면 안 되는 이유에 대해서 다음 포스트에서 설명할 것이지만 그 중에 하나를 언급하면 페이지가 로딩되는 과정이 느려지는 것입니다. 위 화면을 보면 1초가 걸리는걸 볼 수 있는데 사용자는 1초 동안 흰색 화면을 계속 보고 있게 되는 것입니다. 때문에 사용자는 느리다고 느낄 수 있습니다. 넥슨의 스크립트 파일을 하나로 모으는 작업 중에 파일 안에 아무 내용도 없는 파일을 하나 볼 수 있었습니다. 개발자가 실수로 빼지 않은 것 같았습니다. 이 경우 괜히 Request만 하나 잡아 먹게 되는 것이죠 (^_^) 여튼 스타일시트나 자바스크립트나 마찬가지 입니다. 반드시 필요한 경우가 아니면 파일을 분할하는 것은 좋지 않다는 것을 기억해 두도록 합시다.
 
 


CSS Sprite라고 들어봤습니까?
 

마지막으로 소개할 방법은 CSS Sprite라는 기술입니다. 쉽게 이해하기 위해서 구글 사이트를 예를 들어 설명하도록 하겠습니다. 구글의 다운로드 기록을 보면 이미지는 총 3개만 사용하고 있는 것을 볼 수 있습니다. 하지만 구글 페이지를 보면 3개 이상의 이미지를 사용하고 있습니다. 그럼 구글은 이미지 맵을 이용하고 있는 걸까요? 그렇지 않습니다. 구글은 실제로 60개 이상의 이미지가 사용되지만 CSS Sprite라는 기술을 이용해서 2개로 줄여서 사용하고 있는 것입니다. 앞의 예제를 통해서 봤듯이 60개의 Request를 2개로 줄일 수 있다면 성능은 얼마나 좋아질지 상상이 갈 것입니다. 그럼 구글 페이지를 살펴보겠습니다. 
 



[실제로 사용되고 있는 구글의 이미지]
 
이 수수께끼는 다운 받은 3개의 이미지들을 열어보면 풀 수 있습니다. 
 
[이미지의 수수께끼]

 
실제로 구글이 다운 받은 이미지는 이렇게 하나의 통 이미지를 다운받게 됩니다. 이유는 간단합니다. 요청(Request) 수를 줄이기 위해서 입니다. 그럼 어떻게 이 이미지들을 잘라서 보여주는 겁니까? 그것은 바로 CSS Sprite라는 기술을 이용해서 원하는 이미지를 지정하고 잘라서 표현할 수 있는 것입니다. 우리가 CSS Sprite를 많이 사용하지 않는 것은 유지보수뿐만 아니라 개발하는 것 자체가 굉장히 귀찮기 때문입니다. 계속 반복해서 강조하는 내용이 바로 귀찮아져야 성능을 올릴 수 있다는 것입니다.
 
그럼 CSS Sprite를 어떻게 사용하는지 간단한 예제를 살펴보도록 하겠습니다. HOONS는 다음과 같은 이미지를 준비했습니다. 



[sprite.png파일]

그리고 이 파일을 잘라서 이용하는 문법은 다음과 같습니다.
 
<style>
    .orangeBG
    {
        background: url( 'sprites.png' ) repeat-x 0 0;
        height: 20px;
    }
    .greenBG
    {
        background: url( 'sprites.png' ) repeat-x 0 -20px;
        height: 20px;
    }
    .blueBG
    {
        background: url( 'sprites.png' ) repeat-x 0 -40px;
        height: 20px;
    }
</style>
 
이렇게 스타일을 작성하면 단 한번의 요청으로 여러 개의 이미지를 사용할 수 있는 것입니다. 
 

[CSS Sprite 테스트]


CSS Sprite를 활용하면 이미지맵 그 이상의 성능을 올릴 수 있습니다. 
 


요약

앞에서 캐시의 만료기간을 지정하는 것은 재방문 한 사람을 위한 튜닝 방법이었습니다. 이번 포스트에서 설명한 방법은 사이트에 처음 방문할 경우 Request 수를 최소화 하면서 보다 가볍게 만드는 팁들을 설명했습니다. CSS Sprite, 자바스크립트 파일합치기, 이미지 맵과 같은 기법들을 사용하면 150개의 요청도 절반으로 줄일 수 있을 것이고 또한 그만큼 성능도 빨라지게 될 것입니다. 성능만 빨라집니까? 요청수가 줄면 서버의 부하도 어느 정도 줄게 됩니다. 이미지를 무조건 쪼개서 가볍게 하는 것이 페이지를 가볍게 하는 방법이 아닙니다. 또한 파일의 크기를 작게 하는 것이 페이지를 가볍게 만드는 것이 아닙니다. 페이지를 가볍게 한다는 것은 HTTP 요청을 최소화 해서 빠른 시간 내에 페이지를 로드 하는 것입니다. 
 

이전 포스트에서는 HTTP 요청 수를 최소화하여 처음 사이트에 방문 했을 때 속도를 올리는 방법들에 대해서 살펴 보았습니다. 지난 번 포스트에서 강조한 부분은 바로 HTTP 요청을 줄이는 것이었습니다. 페이지의 UI를 조금만 수정해서 HTTP 요청을 줄인다면 분명 엄청난 성능 향상을 가져올 수 있을 것입니다. 몰라서 안 했다면 이제는 귀찮음과 싸워야 할 때입니다.(-_-;) 보안 또한 귀찮음과 비례합니다. 보안을 강화할수록 사용자는 불편함을 느끼게 됩니다. 비스타의 UAC가 그 대표적인 예가 되지요. 하지만 성능은 웹 사이트 개발자가 귀찮아질수록 사용자는 편해집니다. 때문에 그만큼의 가치 있는 작업이 된다는 것이지요.
 

이전 포스트가 약간의 UI 수정 작업이 필요했다 라면 이번에 다루는 Gzip 적용에 대한 내용은 서버 단의 코드로 쉽게 적용할 수 있습니다. 그럼 Gzip이 무엇인지 넥슨에 적용할 때 얼만큼의 성능을 가져올 수 있을지 살펴 보도록 하겠습니다. 




Gzip이란 무엇인가?
 
Gzip은 국제 표준으로 등록된 무료 압축 포맷입니다. (RFC1952) 이 기술은 현재 가장 많이 사용하고 있고 그만큼 성능도 좋습니다. 이 압축은 HTTP 통신에 있어서 이용할 수 있습니다. 브라우저는 자신이 압축을 해제할 수 있는 압축 포맷을 HTTP 헤더의 Accept-Encoding의 속성을 이용해서 전달합니다. 이 때 서버는 이 속성을 확인하여 본문을 압축할 수 있습니다. 하지만 기본적으로 웹 서버는(IIS) gzip이나 deflate 압축을 적용하지 않기 때문에 개발자가 직접 설정해주어야 합니다. 그럼 압축이 어떻게 이루어 지는지 살펴보도록 하겠습니다. 다음(Daum)의 본문을 예를 들어 설명해 보겠습니다. 먼저 다음으로 요청해 보도록 하겠습니다.
 

Accept: */*
Accept-Language: ko
UA-CPU: x86
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
…생략…
[Request]

요청을 보내는 브라우저가 gzip과 deflate 압축의 해제가 가능하다면 위와 같이 Accept-Encoding에 gzip과 deflate를 지정하여 서버로 보내게 됩니다. 웹 서버에서는 이 속성을 판단하여 gzip 압축을 적용하여 본문을 내려줄 수 있는 것입니다.


HTTP/1.1 200 OK
Server: Apache
Vary: Accept-Encoding
Content-Encoding: gzip
…생략…
[Reponse]

만약 Accept-Encoding에 gzip이 포함되어 있지 않는데도 압축하여 내려주게 되면 그 브라우저에서 압축된 본문을 풀지 못하고 결국 깨져서 보이게 될 것입니다. 다음 화면은 압축된 본문을 보여주고 있습니다. 엇 처음 보는 툴이라구요? 이것은 httpWatch라는 툴입니다. 기존에 사용하던 IeWatch가 Gzip과 관련된 버그가 발견되어서 이번 포스트에서는 다른 툴들을 이용할 예정입니다.
 

[압축된 본문]


Gzip은 바이너리 파일이 아닌 텍스트 파일에 적용할 수 있습니다. 예를 들어 이미지나 압축 파일에 압축을 적용해봤자 그 크기는 줄어들지 않습니다. 텍스트를 가지고 있는 HTML, CSS, JS 파일들이 압축할 수 있는 대상들인 것입니다. 그런데 일반적으로 자바스크립트 파일과 CSS 파일에서는 압축을 적용하고 있지 않습니다. 그럼 국내의 게임 사이트들을 분석해 보도록 하겠습니다.
 
사이트 이름
Gzip 적용 여부
한게임(http://www.hangame.com/) HTML, JS, CSS 모두 압축 이용
넷마블 (http://www.netmarble.net/) 모두 압축되지 않음
피망 (http://pmang.sayclub.com/) 모두 압축되지 않음
넥슨 (http://www.nexon.com) 모두 압축되지 않음
 
Gzip을 적용한 게임 사이트는 역시 예상했던 대로 NHN의 한게임 하나였습니다. 나머지 사이트들은 아쉽게도 Gzip을 적용하지 않고 있습니다. 하지만 한게임에서도 약간의 아쉬운 점이 있습니다. 한게임은 모든 텍스트 파일들을 압축하고 있습니다. 용량이 작은 파일은(1K 미만 정도) 일반적으로 압축을 적용하지 않는 것이 더 빠릅니다. 왜냐하면 전달되는 속도보다 압축을 적용하고 푸는 시간이 더 소비될 수 있기 때문이고 또한 압축을 적용할 때 서버의 CPU 리소스를 소비하게 됩니다.
 
자, 그럼 넥슨에 압축을 어떻게 적용해야 할지 또 그 성능은 어떻게 향상되는지 살펴보도록 하겠습니다.





넥슨 사이트의 압축 적용
 

그럼 압축을 적용해 보도록 하겠습니다. 압축을 적용하는 방법은 앞에서 다룬 캐시를 적용하는 방법과 거의 비슷하게 설정하기 때문에 터프하게 설명하도록 하겠습니다. HOONS요원은 넥슨 파일을 모두 다운 받아서 HOONS IIS서버를 하나 설정하였습니다. 압축을 적용할 파일은 js, css, html 이렇게 3개 입니다. 그리고 1K 이하의 파일에는 압축을 설정하지 않도록 설정할 것입니다. 자! 이 작업을 수동으로 처리하는 것은 생략하고 동적코드로 만들어 보도록 하겠습니다. js파일과 css 파일을 aspnet_isapi.dll과 매핑시켜 처리할 수 있지만 이번에는 이미 매핑되어 있는 .ashx라는 확장자를 파일 뒤에 붙여 처리하도록 하겠습니다. 모든 파일 주소뒤에 .ashx라는 확장자를 붙이고 Web.Config 파일에 다음과 같이 Gzip이라는 httpHandler와 연결시켜 줍니다.

 

<httpHandlers>

<!--*압축할 파일 설정*-->

  <add verb="*" path="*.aspx" type="Gzip"/>

  <add verb="*" path="*.ashx" type="Gzip"/><?XML:NAMESPACE PREFIX = O />
</httpHandlers>

[Web.Config 설정]
 

이제 Gzip 클래스의 코드를 살펴보도록 합시다.

using System.IO.Compression;
using System.IO;

public class Gzip : IHttpHandler
{
    public Gzip()
    {    }

    #region IHttpHandler Members

    public bool IsReusable

    {

        get { return true; }

    }

    public void ProcessRequest(HttpContext context)

    {

        string file = context.Server.MapPath(context.Request.FilePath.Replace(".ashx",""));//파일 경로

        string filename = file.Substring(file.LastIndexOf('\\') + 1);//파일 이름
        string extension = file.Substring(file.LastIndexOf('.') + 1);//파일 확장자
 
        //1순위 - Gzip
        if (context.Request.Headers["Accept-encoding"] != null && context.Request.Headers["Accept-encoding"].Contains("gzip"))
        {
            FileInfo fileInfo = new FileInfo(file);

            if (fileInfo.Length > 1024)//1K 용량 설정
            {
                context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
                context.Response.AddHeader("Content-Encoding", "gzip");
            }
 
        }//2순위 - Deflate
        else if (context.Request.Headers["Accept-encoding"] != null && context.Request.Headers["Accept-encoding"].Contains("deflate"))
        {
            FileInfo fileInfo = new FileInfo(file);
 
            if (fileInfo.Length > 1024)//1K 용량 설정
            {
                context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress, true);
                context.Response.AddHeader("Content-encoding", "deflate");
            }
 
        }
        //ContentType설정
        if (extension == "aspx")
            context.Response.ContentType = "text/html";
        if (extension == "js")
            context.Response.ContentType = "application/x-javascript";
        if (extension == "css")
            context.Response.ContentType = "text/css";
 
        //화면 출력
        context.Response.WriteFile(file);
    }
    #endregion
}

[Gzip 클래스의 코드]


코드를 보면 Accept-encoding 속성을 통하여 압축여부를 확인하게 됩니다. 그리고 지원이 가능하다면 해당 파일의 용량이 1K 이상일 경우에만 압축을 적용하고 있습니다. 그럼 이제 성능을 비교해 보도록 하겠습니다. 성능 테스트는 IBM Page Detailer를 이용하여 분석해 보도록 하겠습니다.
 
먼저 압축을 하기 전의 크기와 시간은 다음과 같습니다.

[압축이 적용되지 않은 시간]


압축하기 전의 시간은 약 0.33초이고 크기는 82KB 정도됩니다. 이제 압축했을 때의 크기와 시간을 살펴보도록 합시다.

[압축 적용된 페이지의 시간]


압축이 적용되면 용량은 23K로 줄게 됩니다. 거의 1/4로 줄어들게 되는 것이죠. 속도는 0.16초로 거의 반정도로 줄어드는 겁니다. 그런데 용량은 1/4로 줄었는데 왜 속도는 1/4로 줄지 않는 것일까요? 이것은 컨텐츠의 크기가 속도의 전부가 아니라는 것입니다. HTTP 요청 자체가 시간을 가지고 있는 것입니다. 그래서 성능을 튜닝할때 작업해야 하는 1순위가 HTTP 요청을 작게 만드는 것 입니다. 


   
 

 
 
   

정리
 
압축은 네트워크 비용을 줄일 수 있고 또한 사용자에게 보다 빠른 속도로 적용할 수 있는 것은 사실입니다. 하지만 한가지 고려해 보아야 할 점이 있습니다. 압축은 약간의 CPU 비용이 들어간다는 점입니다. 여기서 “%프로세서 시간”의 통계가 80% 이상을 넘어가게 된다면 압축 적용을 고려해 볼 필요가 있습니다. 대부분의 사이트에서는 압축을 적용했다고 CPU 비용이 그렇게 많이 소비되지 않습니다. 그리고 중요한 부분이 반드시 바이너리 파일에 압축을 적용하면 안 된다는 것입니다. 압축을 적용하게 되면 용량이 줄어들 확률이 거의 없고 CPU 리소스만 낭비된다는 것을 알아둡시다.


원래 ETag에 대한 포스트를 쓰려고 했지만 한 초등학교 선생님의 제보가 도착해서 그 사건을 한번 분석해 보려합니다.



제보받은 내용

어느 초등학교 선생님으로부터 전달된 제보입니다.




사건 분석은 넥슨 사이트가 빨라졌다는 담긴 한 초딩의 일기로부터 시작합니다. 아니 넥슨이 어떻게 변했길래 사이트가 빨라졌다고 생각하는 걸까요?! 그래서 넥슨 사이트를 조사해 보았습니다.



[MAX-AGE를 적용한 이미지]


넥슨의 성능을 높이는 방법중에 캐시를 활용해야 한다는 내용을 먼저 포스트 했었습니다. (넥슨 사이트의 성능튜닝 - #2 캐시의 활용) 3주전만 하더라도 넥슨은 캐시를 활용하지 않고 있었지만 언제부터인가 넥슨은 Cache-Control의 max-age속성을 이용하여 캐시를 적용하고 있었던 것입니다.





그럼 얼마나 빨라졌을까?


그럼 얼마나 빨라졌을지 어떻게 관찰해볼 수 있을까요? HOONS요원은 지난번 넥슨을 조사하면서 기록한 자료들을 체계적으로 정리해 놓았습니다. (HOONS요원, 정말 꼼꼼하시군요!) 이 자료를 가지고 지금의 넥슨과의 속도를 비교해 보도록 하겠습니다. 지난 포스트(넥슨 사이트의 성능튜닝 - #2 캐시의 활용)에서 설명했듯이 캐시를 활용하면 넥슨 사이트를 재방문 했을 때의 속도를 빠르게 할 수 있는 작업입니다. 그럼 3월 3일에 조사했던 자료를 첨부해 보도록 하겠습니다.



재 방문 했을 때의 응답속도는 1.5초정도 되는 것을 볼 수 있고 요청 또한 155번 일어나는 것을 볼 수 있습니다. 캐시를 활용하지 않기 때문에 서버로 이미지 확인 요청을 계속 보내게 되는 것입니다. 자! 그럼 캐시를 활용하고 있는 넥슨사이트의 지금 속도를 확인해 보도록 하겠습니다.



요청시간은 0.8초로 반정도 줄어들게 되고 또한 100개 이상의 HTTP 요청이 줄어든 것을 볼 수 있습니다. 다시 말하자면 100개 이상의 HTTP 요청이 줄었기 때문에 시간이 줄어들게 되는 것이죠.

하지만 조금 아쉬운 부분은 캐시 만료기간을 1시간으로 지정하고 있다는 것입니다.(max-age=3600) 네이버와 다음의 경우 최소 1달 이상의 만료기간을 지정하고 있습니다. 즉, 파일을 업데이트 될 경우 아에 파일이름을 바꾸겠다는 것입니다. 파일명을 무조건 새롭게 해서 업데이트 하는 방식은 아마 새로운 관리 프로세스를 필요로 하기 때문에 큰 시스템일수록 단기간에 적용하기 어려울 것입니다. 때문에 넥슨은 지금 가장 간단하게 적용할 수 있는 방법을 활용한 것이 아닐까 생각이 듭니다.

어쨌든 넥슨은 1시간 범위에서 움직이는 사용자들이라면 0.7초나 빨라진 넥슨 사이트를 볼 수 있게 될 것입니다.
728x90

댓글