본문 바로가기

.Net Technology/UI Performance

넥슨 사이트의 성능튜닝 - #4 Gzip 압축의 적용


안녕하세요~ 암암리에 넥슨의 뒤를 캐고 있는 튜닝요원 HOONS입니다. 이전 포스트에서는(넥슨 사이트의 성능 튜닝 – #3 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"/>
</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 리소스만 낭비된다는 것을 알아둡시다. 

지금까지 HOONS였습니다. 읽어주셔서 감사합니다.