'Cache-Control'에 해당되는 글 4건

  1. 2009.09.07 HTTP GET & DDoS CC Attack (2)
  2. 2009.08.05 서블릿 응답 헤더(Response Header)
  3. 2009.05.13 넥슨 사이트의 성능 튜닝
2009.09.07 14:46

HTTP GET & DDoS CC Attack



RIOREY DDOS Solution Http Get & CC attack 에 대한 차단

RIOREY는 많은 다른 Application level공격들을 방어 할 수 있는 매우 효과적인 TCP SYN rate를 소개했습니다.

이러한 공격들은 다양한 HTTP attacks, CC attacks, P2P attacks 그리고 많은 것들은 포함합니다.

이 문서에서는 SYN RATE 엔진이 어떠한 방식으로 운영되는지, 그리고 최근에 HTTP 공격들을 대신해서

막아주는데 사용되는지를 설명합니다.

TCP SYN Rate 엔진은 매우 유연한 동작을 합니다, 이 엔진은 서버의 행동을 대신해서 클라이언트의 동작을 시험하기 때문에 서버의 운영 시간 동안에 거짓 경보를 울리는 일이 없게 됩니다.

우리는 HTTP GET attack CC attack의 예와 함께 TCP SYN Rate가 어떤 방식으로 돌아가는지 설명을

할 것입니다.

HTTP Attack들은 보통 TCP session들을 열게 되어 있습니다. 그리고 그때 HTTP GET을 요청 합니다.

HTTP GET이 빨리 반복 되었을 때 그것은 HTTP Server의 과도한 응답을 요구하게 되는 요인이 되며 결

국 서버는 장애를 일으키게 됩니다.

 

HTTP GET 공격의 종류

HTTP 요청은 두 가지 주요 사항이 있습니다:

a) 첫 번째 HTTP 요청은 단일 TCP connection입니다.

이것은 HTTP 1.0 1.1 버전에 의해 제공되어 집니다.

b) 다중 요청들은 단일 TCP connection 안에 있습니다.

이것은 HTTP 1.1에 의해 제공 됩니다.

대부분의 HTTP공격은 다음의 기술을 사용합니다.

 

단일 HTTP Get 공격

a) 단일 TCP Connection 에서 한번의 HTTP 요청을 처리하는 경우.

이것은 또한 큰 규모의 랩실에서 소프트웨어 Generate 를 이용해서 HTTP 공격을 유발하는

경우입니다. 어쨌든 더욱 정교한 공격은 이 기술을 사용합니다.

b) 더 적은 공격 대역폭과 함께 Victim 에게 더욱 큰 손해를 입히는 원인입니다.

 

HTTP 공격 기술을 사용하여 실행하기 위한 조건.

a) 공격자들은 TCP SYN packet Generating 에 의해 TCP connection 을 시작합니다.

b) 그때 ACK 를 보내고, 그리고 다음과 같이 GET 명령을 보냅니다:

GET /Gallerys/java/45/stat.php?id=1020880659&v=1.0 HTTP/1.1

c) 공격자들은 Victim 서버들을 끊임없이 응답하게 하기 위해서 반복적으로 GET request를 반복적으

로 보낼 것입니다.

공격 발생 일 때

공격의 한 형태로서 공격자들은 높은 비율의 공격 Packet Generate할 필요가 있습니다.

매번 HTTP GET Generate되기 전에 TCP SYN이 먼저 Generate 된 다는 것은 반듯이 명심해야 됩니다,

그렇기 때문에 종종 HTTP 공격이 이 기술을 사용하여 또한 같은 HTTP GET 요청비율의 TCP SYN RATE

flood의 원인이 됩니다.

RIOREY의 공격에 대한 대응

HTTP 요청이 매번 오기 전에 TCP SYN이 먼저 오기 때문에, RIOREY는 빠르게 SYN Rate 요청을 초과하는것을 탐지 합니다, 그리고 TCP connection을 거절 할 것입니다.

이것은 매우 효과 적인 방법이며, 그것은 뿐만 아니라 HTTP 공격도 처리하고 또한 많은 다양한 TCP

connection을 기반으로 하는 Application 공격을 차단할 것입니다.


CC Attack


CC
공격

예를 들어 많은 ISP는 공격 동안에 많은 Victim IP address들의 변경을 통해서 HTTP 공격을 방어하고 있

습니다.

정상적인 컴퓨터에서는 일단 URL을 찾아 보면 그것은 악의의 탐색을 피하기 위해 IP address를 은닉하게

될 것입니다.

ISP Victim IP를 변경 했을 때, 그것은 Cache의 시간이 만료될 때까지 약간의 교체 주기를 만들어 내게

될 것입니다.

예를 들어 공격의 추적기능을 지금 사용해 보면:

GET /Gallerys/java/45/stat.php?id=1020880659&v=1.0 HTTP/1.1

User-Agent: SickleBot

Host: gernhardts.com

Cache-Control: no-cache

HTTP/1.1 200 OK

Date: Sat, 14 Jan 2006 21:43:18 GMT

Server: Apache/1.3.33 (Unix) mod_log_bytes/0.3 FrontPage/5.0.2.2635 PHP/4.3.11 mod_ssl/2.8.22

OpenSSL/0.9.7c

X-Powered-By: PHP/4.3.11

Connection: close

Transfer-Encoding: chunked

Content-Type: text/html

eG1/ZwEGf2R4eHwBBn9/dWIBBn5keHh8LDUsYWVgYGVjYmhjYGBtfmRjYWl8bWtpIm9jYQEGfn91Yiw/LGFlYG

BlY2JoY2BgbX5kY2FpfG1raSJvY2EBBm==

위의 예와 같이 변동 추적을 사용하면, 공격자들은 Victim IP 변경을 지금 추적하고 공격을 방어한 ISP

대해서 노력을 헛되이 만들어 버릴 수 있습니다.

이와 같이 위의 변동은 하나의 SYN 을 계속하고 있고 그렇기 때문에 RIOREY SYN RATE Limit Cache control의 상태를 알 필요가 없고 공격의 다양함을 대신해서 똑같이 효율적으로 공격을 막아 낼 수가 있습니다.


공격에 대한 SYN RATE를 이용한 대응

TCP SYN Rate를 적당히 사용하기 위해서는, SYN RATE Limit를 올바르게 사용해야 합니다. SYN RATE

RX Box안에서 IP address의 쌍(서버-클라이언트) 사이에서 SYN Rate로 정의 됩니다.

그렇기 때문에 그것은 서버가 아닌 클라이언트의 행동을 주의 깊게 보게 됩니다.

또한 서버에 연결된 한 명의 클라이언트이든지 또는 서버에 연결된 1000명의 클라이언트이든지, RIOREY SYN RATE는 각각의 클라이언트에 독립적으로 동작하게 될 것이며 이러한 방법을 사용하면 Application 의 형태는 SYN RATE에 의해 정의 될 수 있습니다. 그것은 서버에 연결된 수많은 클라이언트가 아닙니다.

예를 들어, 정상적인 웹 접속에서, 한 클라이언트 IP에 대한 SYN RATE 10보다는 크게 될 것이며 이것

은 분당 SYN입니다. 다른 Application은 한 클라이언트들에 다른 SYN 비율을 갖게 될 것입니다.

SYN RATE levels을 정하면, 정상 작동 상태에서 네트워크를 관찰해야 합니다. RX는 당신의 네트워크에 분 SYN RATE를 권고 해 줄 것입니다. 당신은 당신의 Application에 대한 IP들의 주위에서 정상적인 SYN RATE의 집단을 보게 될 것 입니다.

Per IP SYN Rate Limit" 는 대략 정상적인 SYN RATE의 집단의 상위 10% 또는 최저 10 ppm 에서 정하고, 그리고 그때 "Max SYN Rate" "Per IP SYN Rate Limit" 위의 대략 10% 또는 최저 10ppm 에서 정해질 것이다.

이러한 Setting은 고객의 서버들에 초과 연결 된 과도한 공격자들의 연결을 제거하게 될 것이며 그리고

전통적인 HTTP Get공격과 CC 공격에 대해서 효율적인 차단을 하게 될 것입니다.

SYN RATE 적용 예

예제1) 만일 정상적인 집단의 상위의 SYN RATE 43ppm일 때 Per IP SYN RATE Limit 53이고 MAX

SYN Rate 63입니다.

예제2) 만일 정상적인 집단의 상위의 SYN RATE 156ppm 일 때 Per IP SYN RATE Limit 172이고

MAX SYN Rate 189입니다.

일단 Setting이 되면 좋은 Application을 관찰하고 좋은 유저들이 어떤 문제가 생기는지를 확인해야 합니.

 

다중 HTTP Get 공격

HTTP 공격의 새로운 형태는: 단 하나의 TCP connection에서 다중 GET 일으키는 HTTP 1.1 일 것입니다.

이 기술을 사용한다면 탐지 엔진은 다수의 연결의 독립적인 GET을 볼 필요가 있고 공격 안에서 다양한

GET들을 구분해야 한다.

HTTP의 형태는 RX안에서 HTTP 공격으로서 보고되고 탐지 됩니다,

하나의 Connection 안에서 멀티 GET 들을 사용한다는 뜻은 다수의 연결을 만들어 낸다는 것을 뜻합니

. 이 공격은 TCP SYN RATE Detector는 방어를 못하고 RX HTTP 에 의해 제거될 것입니다.

이상의 내용에 대해서 HTTP Get & CC 공격에 대한 RIOREY의 차단 방법에 설명을 드렸습니다.


출처 : http://ddos.tistory.com/


Trackback 0 Comment 2
  1. lkjh 2010.11.08 14:49 신고 address edit & del reply

    번역기썼냐?원본어딨어

    • Favicon of https://blog.pages.kr 날으는물고기 2010.11.08 21:48 신고 address edit & del

      출처는 하단에 표기되어져 있습니다.
      보시면 아시겠지만 번역시 돌린 것입니다.
      충분히 이해 될거라 생각해서 번역기 결과를 그대로 올렸습니다.

      절 아시는지 왜 반말을 하시는지 참..; 쩝!

2009.08.05 13:54

서블릿 응답 헤더(Response Header)

서블릿에서 응답 헤더들을 설정하는 방법 - 응답헤더는 클라이언트에게 문서 내용을 한글자라도 보내기 전에 설정해야 한다.
 - 일반적으로 setHeader("","") 로 사용한다.
 - 서블릿 버전2.1에서는 헤더를 설정하면 바꿀수 없었다 같은 이름으로 헤더를 설정하더라도 이전것은 유지되면서 새로운 해더가 추가되는 형식이 였다. 하지만 서블릿 버전 2.2 에서는 setHeader를 같은 이름으로 하면 덮어쓰기 형식으로 지원하며, 같은 이름으로 추가는 addHeader("","")로 하면 된다.

 Accept-Range

 - HTTP1.1 에서 추가
 - 클라이언트가 보낸 Range요청헤더를 받아들 일 수 있는지의 여부를 뜻한다.
 - 받아들 일 수 있다면 byte 단위 지정 아니면 none을 지정한다.

 Age

 - HTTP1.1에서 추가
 - 프록시서버에서 사용하는 것으로 원래의서버가 문서를 만든 후 얼마만큼의 시간이 지났는지를 ...
 - Servlet에선 거의 사용하지 않는다.

 Allow

 - 서버가 지원하는 요청방법(GET,POST등)을 지정한다.
 - 상태코드가 405일 때에는 반드시 이 헤더를 설정해야 한다.
 - 서블릿의 기본 service메소드는 OPTIONS요청에 대해 이 헤더를 자동적으로 만든다.

 Cache-Control

 이것은 문서를 받은 클라이언트가 그 문서를 개시에 어떻게 저장해야 하는것인가를 지시하기 위한 헤더

1. public 
 - 다른 일반적인 규칙들을 위배하더라도문서를 캐시해도 좋다는 뜻.
2. private 
 - 문서를 한 명의 사용자에 대해서만 그리고 비공개(비공유)캐시에만 저장할 수 있다는 뜻.
3. no-cache
 - 문서를 캐시에 저장하지 말라는 뜻.
 - 브라우저는 일반적으로 양식데이터를 포함하는 요청에 대해서는 캐싱하지 않는다.
4. no-store
 - 문서를 캐시뿐만 아니라 다른 어떠한 임시적인 저장장소에도 저장해서는 안된다는 뜻.
 - 보안상 중요한 정보들을 이렇게 처리하자.!
5. must-revalidate
 - 클라이언트는 문서를 사용할 때마다 원래의 서버에 대해 문서의 유효성을 재확인해야 한다.
6. proxy-revalidate 
 - 공유 캐시에만 적용된다는 점만 제외한다면 must-revalidate와 동일하다.
7. max-age=xxx 
 - HTTP1.1 을 지원하는 클라이언트데서만 동작.
 - xxx초 이후에는 문서가 유효하지 않을 수 있다는 뜻.
 - 응답에 max-age왕 Expires모두 있다면 max-age가 우선
8.s-max-age=xxx
 - max-age와 동일하나 공유캐시에 적용되는 것이다.

 Connection

 - 연결방식에 대한 지시를 의미하는 헤더이다.
 - Connection: keep-alive 이면 영속적 연결이 쓰인다.(기본)
   단 Content-Length 응답헤더를 반드시 포함시켜야 한다.
 - 영속적 HTTP연결을 사용하지 않으려면 Connection을 close로 하기보단 Content-Length를 지정하지 않으면 된다.
 - 영속적 연결 자세히 보기

 Content-Encoding

 - 전송 과정 도중에 페이지가 어떻게 인코딩되어야 하는지를 지정한다.
 - 여기서 MIME 정보가 쓰인다.

 Content-Language

 - 문서가 어떤 언어로 쓰여져 있는지를 지정한다.

 Content-Length

 - 응답의 크기(바이트 개수)를 의미한다.
 - 영속적 연결 자세히 보기 에서 ByteArrayOutStream으로 바이트 개수를 알아낸 후 response.setContentLength를 이용해서 헤더에 설정한 후 바이트 스트림으로 writeTo메소드를 호출하는 것이다.
 - 바이트 개수를 얻어보자!

 Content-Location

 - HTTP1.1 에서 추가
 - 요청된 문서의 또 다른 주소를 제공하는데 쓰인다.

 Content-MD5

 Content-MD5 응답 헤더는 응답에 포함된 문서의 MD5 다이제스트를 제공한다.
 다이제스트(Digest)란? 문서전체가 제대로 전송되었다는걸 확인하기 위한 용도로 쓰인다.

 Content-Range

 - HTTP1.1 에서 추가
 - 문서의 일부분만을 전송할 때 어디서 어디까지의 문서가 전송되는 것인지를 알려주는 역할을 한다. 

 Content-Type

 - 헤더의 응답 문서의 MIME형식을 의미한다.

 Date

 - 현재 일시를 GMT형식으로 지정한 것이다.
 - response.setHeader("Date"."xxx") 로 설정한다
 - 대부분의 서버들은 이 헤더를 자동적으로 설정해 준다.!

 ETag

 - HTTP1.1 에서 추가 
 - 클라이언트가 나중에 참조할 때 사용할 이름을 문서에 부여하는 역할을 한다.

 Expires

 - 이 헤더는 문서가 캐시에 남아 있을 수 있는 유효 시간을 설정한다.
 - 10분이상 남아 있지 않도록 하는 예제
 long currentTime = System.currentTimeMillis();
 long termTime = 1000*60*10 // 10분 밀리초 단위로
 response.setDateHeader("Expires",currentTime+termTime);
 - Cache-Control 헤더의 max-age 와 동일한 기능

 Last-Modified

 - 문서가 마지막으로 수정된 일시를 의미한다.
 - getLastModified 를 이용해서 처리하자.!

 Location

 - 문서의 주소를 의미한다.
 - 상태코드가 300번 대이면 반드시 이 헤더를 포함시켜야 한다.

 Pragma

 - HTTP 1.0에서 no-cache로 설정하면, 브라우저는 문서를 캐시에 저장하지 않는다. 그러나 HTTP 1.0 브라우저에 따라서 이 헤더에 대한 반응 방식이 다를 수도 있다는 점을 주의해야 한다.
 - HTTP 1.1 에서는 Cache-Control:no-cache 쪽이 좀 더 신뢰할 수 있다.

 Refresh

 - 브라우저가 지정된 시간 이후에 문서를 자동적으로 다시 로딩하게 만드는데 쓰인다. 
 - 반복적인 재요청이 아니다.
 - response.setHeader("Refresh","5;URL=http://host/path");
 - 이 헤더는 HTTP 1.1의 공식 헤더가 아니지만, 넷스케이프와 익스플로러가 모두 지원하고 있어서 거의 공식적인 헤더라고 해도 무방하다. 

 Retry-After

 - 응답 상태 코드 503과 함께 쓰이는 것으로 얼마 후에 문서 요청을 다시 시도해야 하는지 알려주는 역할을 한다.

 Server

 - 웹서버 종류를 알려주는 헤더로 서블릿이 직접 설정하는일은 거의없다.

 Set-Cookie

 - 응답에 담긴 페이지와 연관된 하나의 쿠키를 설정한다.
 - 쿠키가 여러개면 그 개수 만큼의 Set-Cookie들을 설정한다.
 - response.setHeader("Set-Cookie",...) 보다는 response.addCookie를 사용하자!

Trailer

 - HTTP1.1 에서 추가 
 - "chunked" 전송 인코딩 방식으로 전송되는 메시지의 꼬리에 존재하는 헤더 필드들을 식별하기 위한 것이다.
 - 별로 사용되진 않는다.

Transfer-Encoding

 - "chunked" 전송 코딩을 사용할 때에는 이 헤더의 값을 chunked로 설정

Upgrade

 - 서버에게 몇 가지 새로운 프로토콜들 중 하나로 전환하자고 요청했을 때 사용한다.
 - 서블릿에서는 이 헤더를 사용하는 일이 거의 없다.

Vary

 - HTTP1.1 에서 추가 
 - 클라이언트가 응답 문서의 캐시 여부를 결정할 때 사용할 헤더 이름을 알려주는 역할을 한다.
 - 거의 쓰이진 않는다.

Via

 - HTTP1.1 에서 추가 
 - 게이트웨이나 프록시가 사용하는 것으로 요청이 통과한 중간 사이트들의 목록이 담긴다.

Waning

 - 캐시나 내용전송 과정의 오류등을 클라이언트에게 경고하는 역할
 - 거의 쓰이진 않는다.

WWW-Authenticate

 - 상태코드가 401이면 반드시 포함되는 헤더

@ 참고 및 주의 사항

1.            은 아직도 HTTP1.0 만을 지원하는 브자우저를 고려해서request.getRequestProtocol 로 HTTP1.1을 지원하는지를 체크해야 한다.

2. 공유캐시 : 프록시 서버에 있는 캐시 등을 의미


출처 : http://blog.naver.com/kazki7074


Trackback 0 Comment 0
2009.05.13 19:09

넥슨 사이트의 성능 튜닝

출처 : 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초나 빨라진 넥슨 사이트를 볼 수 있게 될 것입니다.

Trackback 1 Comment 0