'캐시'에 해당되는 글 3건

  1. 2015.07.31 쿠팡 신규가입 5천원 캐시 + 1만5천원 쿠폰
  2. 2009.07.03 opcode 캐시 소프트웨어로 성능과 처리량 높이기
  3. 2009.05.13 넥슨 사이트의 성능 튜닝
2015. 7. 31. 18:08

쿠팡 신규가입 5천원 캐시 + 1만5천원 쿠폰


쿠팡 신규 가입 혜택 받을 수 있는 기회...


현재 캐시 5천원과 장바구니쿠폰 15,000원 지급 이벤트 진행중이며 조기종료 될 수 있습니다.



신규 가입하고 캐시 5천원 받기!

회원가입 바로가기



회원가입을 완료하셨다면 쿠팡 어플에 접속하여 로그인하게되면 상단에 15000원 쿠폰 지급배너가 보입니다.

이곳에서 간단하게 휴대폰 인증을 통해 쿠폰을 발급받으실 수 있습니다.



15,000원 장바구니쿠폰 받기!

쿠폰받기 바로가기(PC화면)

쿠폰받기 바로가기(모바일화면)



Trackback 0 Comment 0
2009. 7. 3. 19:51

opcode 캐시 소프트웨어로 성능과 처리량 높이기

PHP는 스크립팅 언어로써 웹 애플리케이션을 구현하는데 종종 사용되고 있습니다. 배우기 쉽고 시각적인 결과를 빠르게 만들어 냅니다. 하지만, PHP는 인터프리팅 되기 때문에 PHP 코드는 실행될 때마다 opcode로 파싱 및 변환됩니다. opcode 캐시는 재작업을 줄이면서, PHP 애플리케이션을 더욱 빠르게 만들어줍니다.

짧은 시간 동안 PHP는 웹 애플리케이션을 위한 대중적인 프로그래밍 언어가 되었다. 초보자도 PHP를 쉽게 설치할 수 있고 배우기도 쉽다. 보다 숙련된 개발자에게는, (V5부터) 강력한 객체 지향 기능을 제공한다. PHP 개발자 커뮤니티는 방대하고, 상당히 많은 오픈 소스와 상용 라이브러리와 툴들이 언어 기능을 확장하고 있다. PHP는 시각적인 결과를 빠르게 나타내기 때문에 많은 사람들로부터 사랑을 받는다.

Perl, Python, Ruby 등 웹 애플리케이션에 사용되는 다른 스크립팅 언어와 마찬가지로 PHP 코드는 파싱되어, opcodes (PHP 엔진을 직접 실행하는 기본 명령)로 변환되고 HTTP가 이를 요청할 때마다 실행된다. 하찮거나 작은 요구에도, 서버는 복잡한 렌더링 프로세스를 즉각적으로 완벽하게 실행한다. 제공되는 페이지의 수가 증가하면서, 해석(interpretation)-즉 재작업(rework)-은 시스템에 부담을 주게 된다. 어떤 경우, PHP 코드의 "컴파일(compilation)"은 코드를 실행하는데 드는 시간보다 더 많이 든다. 따라서, 요구가 많아질수록, 보다 동적으로 해석되고, 생성된 페이지들은 더 많은 리소스들을 요구하게 된다.

여러분의 사이트에서 프로세서와 메모리에 대해 무제한 예산이 할당되었다면, 사이트의 응답시간을 보장하기 위하여 애플리케이션 스택(하드웨어, OS, 데이터베이스, 웹 서버, PHP 코드)를 최적화할 필요가 없을 것이다. 하지만, 돈은 늘 모자라기 마련이므로, 성능 튜닝은 불가피하다. 튜닝은 부족한 시스템에 메모리를 추가하고, OS 매개변수들을 수정하고, 웹 또는 데이터베이스 서버 속도를 앞당기고, 효율성 있는 코드를 재작성 하거나 결합하는 것을 의미한다. 이러한 모든 것들이 효과를 발휘한다.

리사이클(Recycle)

CPU 사이클을 유지하는 방법은 PHP 애플리케이션을 실행하는데 필요한 재작업 양을 줄이는 것이다. 확실히, 매번 같은 PHP 코드를 변환할 필요 없이, PHP 코드가 opcode로 변환된 후에, 원래 코드가 수정될 때까지 계속해서 공급 및 재사용 된다. 사실, 캐싱(caching)-중간 PHP 코드(opcode)를 저장 및 재사용하는 것-은 오픈 소스 Alternative PHP Cache (APC), Turck MMCache for PHP, XCache, eAccelerator, 그리고 상용 Zend Platform 등 여러 PHP 가속기들의 핵심 개념이다. 뒤에 세 가지는 바이트 코드를 캐싱 및 최적화 하면서, 더욱 빠른 속도를 제공한다.

이번 달에는 XCache를 설치, 전개, 설정하는 방법을 연구해 보자. XCache는 비교적 새로운 것으로서, 많은 사이트들은 이를 사용하여 좋은 결과를 얻고 있다. 게다가, PHP 확장으로 구현되기 때문에 구현, 설치, 설정이 쉽다. Apache와 PHP의 재컴파일이 필요하지 않다.

이 글은 XCache V1.2.0에 기반하고 있다. PHP V4.3.11에서 V4.4.4, PHP V5.1.x 에서 V5.2.x, 그리고 PHP V6의 초기 버전들을 지원하고 있다. (XCache는 PHP V5.0.x는 지원하지 않는다.) XCache는 Common Gateway Interface (CGI) 또는 명령행 PHP 인터프리터가 아닌, mod_php와 FastCGI를 사용하여 실행된다. XCache 소스 코드는 FreeBSD, Sun Solaris, Linux®, Mac OS X 등 다양한 시스템에서 구현된다. XCache는 Microsoft® Windows®에서도 구현될 수 있으며, Cygwin UNIX® 에뮬레이션 환경이나 Visual C를 사용한다. Cygwin 또는 원래의 Win32용 XCache를 구현할 수 있다. 마지막의 목표는 PHP의 공식 Win32 릴리스와의 호환성이다.

이 글에서 설명하는 데모는 Apache V2.2.3, PHP V5.2.0, XCache V1.2.0 (2006년 12월 10일 릴리스), Xcode V2.4.1 on Mac OS X V10.4.8 Tiger를 기반으로 한다. 하드웨어 플랫폼은 2-GHz Intel® Core Duo와 2 GB RAM을 갖춘 Apple MacBook이다.






XCache 구현 단계

진행하기 전에, PHP는 그 자체로 실행되고, 여러분 쉘(Shell)의 PATHphpize가 있는 것을 확인한다. GNU Compiler Collection (GCC) 같은 C 컴파일러가 있어야 하고, makem4를 포함하고 있는 개발 툴 수트가 있어야 한다. Mac OS X에서, 무료 Xcode 소프트웨어 개발 환경은 필요한 빌드 툴을 제공한다.

아래 명령어를 완성하여 Mac OS X에서 XCache를 구현, 전개, 벤치마크 한다. 다른 플랫폼에서의 구현도 비슷하다. 리눅스를 사용하고 있다면, 그 배포판에는 XCache가 이미 포함되어 있고, 이를 사전 패키징 된 형태로 사용할 수 있다.

Mac OS X의 공유 메모리 늘리기

Mac OS X의 공유 메모리의 양을 늘리는 것부터 시작한다. /etc/sysctl.conf 파일을 생성(또는 편집)하고 다음 엔트리들을 만든다.


Listing 1. Mac OS X의 공유 메모리 양 늘리기
                
kern.sysv.shmmax=33554432
kern.sysv.shmmin=1
kern.sysv.shmmni=32
kern.sysv.shmseg=8
kern.sysv.shmall=8192

이러한 설정으로 공유 메모리의 양이 32MB로 늘어났다. 공유 메모리를 더욱 확장해야 한다면, 하드웨어 페이지 크기로 나뉜 kern.sysv.shmmax 값에 kern.sysv.shmall을 설정한다. 이렇게 하면 sysctl hw.pagesize를 사용할 수 있을 것이다. 예를 들어, 128 MB의 공유 메모리가 필요하다면, kern.sysv.shmmax=134217728kern.sysv.shmall=32768을 설정하라.

수정 사항을 적용하려면 Mac OS X를 재시작 한다. 재부팅 되면 다음을 타이핑 하여 새로운 설정이 적용되었는지를 확인한다.

sysctl -a | grep kern.sysv

소스에서 XCache 구현하기

다음에는, 소스 코드에서 XCache를 구현한다. http://xcache.lighttpd.net에서 소스를 다운로드 한다. 코드가 있다면 이것의 압축을 풀어서 .tar 파일이 만든 새로운 디렉토리로 옮긴다.


Listing 2. 소스에서 XCache 구현하기
                
$ cd /tmp
$ wget http://210.51.190.228/pub/XCache/Releases/xcache-1.2.0.tar.gz
$ tar xzf xcache-1.2.0.tar.gz
$ cd xcache

phpize를 실행하여 XCache가 컴파일을 할 수 있도록 한다.


Listing 3. phpize 실행하기
                
$ phpize
Configuring for:
PHP Api Version:         20020918
Zend Module Api No:      20020429
Zend Extension Api No:   20050606

configure를 실행하여 원래의 OS에 맞는 makefile을 만든다.


Listing 4. configure를 실행하여 makefile 만들기
                
$ ./configure --enable-xcache --enable-xcache-coverager 
checking build system type... i686-apple-darwin8.8.1
checking host system type... i686-apple-darwin8.8.1
...
creating libtool
configure: creating ./config.status
config.status: creating config.h

여기에서, --enable-xcache 옵션에는 XCache 지원이 포함되고, --enable-xcache-coverager에는 가속기의 효율성을 측정할 수 있는 부가 기능이 포함되어 있다. opcode 옵티마이저를 실행하려면 --enable-xcache-optimizer를 추가한다.

물론, 다음 단계는 make 명령어를 사용하여 코드를 구현 및 설치하는 단계이다. make를 실행한 다음, 루트(root)로서 make install을 실행한다.


Listing 5. make를 사용하여 코드를 구현 및 설치하기
                
$ make
...
cp ./xcache.so /Users/strike/tmp/xcache/modules/xcache.so
Build complete.

$ sudo make install
Installing shared extensions: /usr/lib/php/extensions/no-debug-non-zts-20020429/

앞서 두 가지 작업들이 무리 없이 수행되었다면 /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so에 XCache가 있어야 한다.( /usr/lib/php/extensions/no-debug-non-zts-20020429 경로는 사용 중인 API의 버전과 PHP 구현에 사용되는 컴파일 옵션을 반영한다. Zend Thread Safety 기능을 실행했다면 "no-debug"가 "debug"가 되고 "non-zts"가 "zts"가 될 수 있다.)

php.ini 파일 바꾸기

확장본이 설치되었기 때문에, php.ini 파일을 변경하여 XCache 확장을 추가하고 설정하도록 해야 한다. /private/etc/php.ini를 열고 다음 라인을 파일에 추가한다.


Listing 6. XCache 확장용으로 php.ini 변경하기
                
[xcache-common]
zend_extension      = /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so

[xcache.admin]
; Change xcache.admin.user to your preferred login name 
xcache.admin.user   = "admin"

; Change xcache.admin.pass to the MD5 fingerprint of your password
; Use md5 -s "your_secret_password" to find the fingerprint
xcache.admin.pass   = "0ad72f3f352fcd8acdf266bafd0ac48d"

[xcache]
; Change xcache.size to tune the size of the opcode cache
xcache.size         = 24M
xcache.shm_scheme   = "mmap"
xcache.count        = 2
xcache.slots        = 8K
xcache.ttl          = 0
xcache.gc_interval  = 0

; Change xcache.var_size to adjust the size of variable cache
xcache.var_size     = 8M
xcache.var_count    = 1
xcache.var_slots    = 8K
xcache.var_ttl      = 0
xcache.var_maxttl   = 0
xcache.var_gc_interval =     300
xcache.test         = Off
xcache.readonly_protection = On
xcache.mmap_path    = "/tmp/xcache"
xcache.coredump_directory =   ""
xcache.cacher       = On
xcache.stat         = On
xcache.optimizer    = Off

[xcache.coverager]
xcache.coverager    = On
xcache.coveragedump_directory = ""

주: 간단히 하기 위해 일부 주석들은 생략했다. 각각의 매개변수가 무엇을 제어하는지를 알고 싶다면 XCache 소스 코드에 포함된 xcache.ini 파일의 샘플 설정을 참조하라.

opcode와 변수 캐시의 크기는 32MB이고, 이것은 /etc/rc에 설정된 최대 사이즈이다. Mac OS X의 경우, xcache.mmap_path는 파일 이름이며, PHP 코드는 MacBook에서 실행되기 때문에, xcache.count 2로 설정된다. 이 숫자는 MacBook에 있는 CPU의 수이다. XCache 통계 페이지로 접근 하려면, xcache.admin.pass 설정을 변경해야 한다. 다음을 실행한다:

$ md5 -s "password"

여기에서 password 는 여러분의 패스워드이다. 결과를 xcache.admin.pass에 복사한다. 예를 들어, 패스워드를 op3nsesam3으로 하고 싶다면, 다음과 같이 한다:

$ md5 -s "op3nsesam3"
MD5 ("op3nsesam3") = cd959ac3debe8f587546a3fa353b3268

그리고 cd959ac3debe8f587546a3fa353b3268xcache.admin.pass에 복사한다.

웹 서버 재시작

XCache를 설정했다면, Apache 웹 서버를 재시작 한다. 대부분의 시스템에서 루트로서 apachectl restart를 사용할 수 있다.

$ sudo apachectl restart
/usr/sbin/apachectl restart: httpd restarted

phpinfo()를 호출하는 프로그램 만들기

XCache가 실행되는지를 확인하려면, phpinfo()를 호출하는 PHP 프로그램을 만들고 그 파일을 웹 브라우저에서 연다. 아래 그림과 같은 XCache 섹션을 볼 수 있다.


그림 1. phpinfo() 메소드가 XCache 설정을 요약하고 있다.
 


XCache 모니터링

XCache를 모니터링 하려면, XCache 소스 코드의 admin 디렉토리에 있는 관리 페이지를 설치한다. 전체 admin 디렉토리를 Apache 문서 루트에 복사한다. (Mac OS X에서, 문서 루트는 일반적으로 /Library/WebServer/Documents이다.)

$ cp -pr admin /Library/WebServer/Documents

복사가 완료되면, sudo apachectl restart로 웹 서버를 재시작 한다. 관리 패널이 실행되는 것을 확인하려면 브라우저에서 http://localhost/admin 입력한다. 그림 2와 비슷한 대시보드를 볼 수 있다.


그림 2. XCache Administration 대시보드
 


애플리케이션 테스트하기

테스트 할 애플리케이션 한 두 가지를 선택한다. 여러분 코드를 사용하거나, 보다 복잡한 것을 원한다면 phpMyAdmin 또는 Serendipity 같은 대형 PHP 애플리케이션을 사용하라.






벤치마크 실행하기

Apache HTTP 웹 서버에는 ab라고 하는 유틸리티가 포함되어 있고, Apache HTTP server benchmarking tool의 약자이다. ab를 사용하여 많은 PHP 페이지에 대한 요청들을 자동화 한다. phpMyAdmin 애플리케이션은 시스템에 이미 설치되어 있으므로 좋은 예시가 된다.

ab 유틸리티는 사용이 간단하며 반복 카운트와 URL을 제공한다. ab 유틸리티는 그 URL을 여러 번 요청하고 통계를 리턴한다. XCache가 이미 실행 중이기 때문에, 첫 번째 벤치마크는 가속화 된 성능을 보여줄 것이다.

ab를 실행하기 전에, 브라우저에서 http://localhost/phpmyadmin/으로 간다. 이 페이지를 한번 방분하면 이 페이지를 캐시로 렌더링 하는데 사용되는 모든 PHP 코드를 로딩한 것이 된다. 아래 벤치마크를 실행하고, 100,000회 반복한다.


Listing 7. phpMyAdmin용 벤치마크
                
$ ab -n 100000 http://localhost/phpmyadmin
...
Concurrency Level:      1
Time taken for tests:   14.597 seconds
Complete requests:      100000
Failed requests:        98262
   (Connect: 49131, Length: 49131, Exceptions: 0)
Broken pipe errors:     0
Non-2xx responses:      50869
Total transferred:      25739714 bytes
HTML transferred:       12005084 bytes
Requests per second:    6850.72 [#/sec] (mean)
Time per request:       0.15 [ms] (mean)
Time per request:       0.15 [ms] (mean, across all concurrent requests)
Transfer rate:          1763.36 [Kbytes/sec] received

위에서 재미있는 통계라 한다면, 초당 요청과 모든 테스트를 완료하는데 드는 총 시간이다. 전자의 경우, 값이 클수록 더 나은 것이다. 후자는 값이 낮을수록 더 나은 것이다.

이제 Listing 8과 같이 php.ini 파일에서 XCache 실행을 멈추고 벤치마크를 다시 실행한다. XCache 확장에 대한 레퍼런스에 주석을 달거나, 모든 XCache 기능들을 꺼둘 수 있다. 벤치마크를 다시 실행하기 전에 Apache를 재시작한다.


Listing 8. XCache를 실행시키지 않은 phpMyAdmin용 벤치마크
                
$ sudo apachectl restart
$ ab -n 100000 http://localhost/phpmyadmin
Concurrency Level:      1
Time taken for tests:   17.771 seconds
Complete requests:      100000
Failed requests:        98256
   (Connect: 49128, Length: 49128, Exceptions: 0)
Broken pipe errors:     0
Non-2xx responses:      50872
Total transferred:      25741232 bytes
HTML transferred:       12005792 bytes
Requests per second:    5627.15 [#/sec] (mean)
Time per request:       0.18 [ms] (mean)
Time per request:       0.18 [ms] (mean, across all concurrent requests)
Transfer rate:          1448.50 [Kbytes/sec] received

여기에서는 XCache를 실행시키지 않았는데, 초당 요청의 수가 줄어들고, Apache 서버가 각 요청에 더 오랜 시간이 걸린다는 것을 반영하고 있다. 테스트의 전체 수트를 실행하는데 드는 시간도 올라갔다.

비록 이것은 단순한 벤치마크이고, 데이터베이스로 phpMyAdmin을 연결하지 않아 PHP를 해석하는데 프로세싱 시간에 제한이 있었고, 과학적이지도 않았지만, XCache을 이용하여 무엇을 얻을 수 있는지는 확실히 알 수 있다. PHP나 Apache를 재컴파일 할 필요가 없는 적은 투자로 XCache는 비교적 좋은 결과를 만들어 낸다. 이것은 코드가 복잡할수록 그의 효용도 더욱 크다.

XCache가 얼마나 일을 잘하는지 궁금하다면, http://localhost/xadmin으로 가서 List PHP를 클릭하라. 캐시에 PHP 파일 리스트, 캐시 히트, opcode에서 측정된 코드의 크기, 바이트로 측정된 소스 파일의 크기 등을 볼 수 있다. 그림 3은 XCache가 XAMPP 스택 패키지용으로 구현되었을 때 나타난 결과이다.


그림 3. XCache 관리 페이지는 캐시의 상태와 내용을 보여준다.
 


XCache는 많은 가속기 옵션들 중 하나이다. 기타 다른 무료 오픈 소스 대안책들도 있고 강력한 Zend 소프트웨어에 상용 옵션들도 있다. 각각의 PHP 가속기 마다 고유의 시스템 요구 사항들이 있기 때문에, 애플리케이션의 설정과 특성에 맞게 선택해야 한다. 이들 중 어떤 것이 더 낫다고 말하기 어렵지만, 컴파일러 캐시를 설치할 것을 권하고 싶다.






수 많은 튜닝 옵션들

캐싱 외에도, 애플리케이션 속도를 높일 수 있는 다른 대안들도 있다. 많은 난해한 기능들을 제거하여 PHP 엔진에서 지방질을 빼낼 수 있다. 예를 들어, TCP/IP V6 (IPv6) 네트워킹을 사용하지 않는다면, PHP를 구현할 때 이 기능을 실행하지 않으면 된다. PHP 소스 코드 트리의 맨 위에서 ./configure --help를 타이핑 하면 PHP 설정 옵션들의 전체 리스트를 볼 수 있다. 어떤 설정 옵션들을 선택하든지 간에,

--enable-inline-optimization --disable-debug

위 라인을 최종 설정 명령어에 추가한다. 전자 옵션은 가장 빠른 PHP 실행 파일을 만들어 낸다. (Zend Engine 같은 소프트웨어로 추가적인 opcode 최적화가 필요 없다.) 후자 옵션은 디버그 모드에서 PHP를 활용하는데, PHP 애플리케이션 서버의 문제를 해결할 때에만 필요하다.

물론, 다른 C 애플리케이션처럼, C 컴파일러를 활용하여 더 나은 실행 파일을 구현할 수 있다. Apache Dynamic Shared Object (DSO)(Linux) 또는 FreeBSD(x86 프로세서)로서 PHP를 실행한다면 -prefer-non-pic 옵션을 CFLAGS (C 컴파일러 옵션을 저장하고 있는 환경 변수)에 추가하는 것도 고려해 볼만하다. Non-PIC는 위치 독립 코드를 사용하여 PHP를 구현하고, 10%정도의 성능 향상을 보인다. CFLAGS-march를 사용하여 프로세서 유형을 규명할 수 있다. AMD Opteron 프로세서에는 -march=opteron을 사용한다.

또 다른 속도 향상 옵션으로는 opcode optimization이 있다. Zend Engine 같은 소프트웨어는 컴파일 동안 생성된 opcode를 최적화 하기 때문에 코드가 수행하는 작업량이 줄어든다.

캐싱과 최적화는 투명할 뿐만아니라 부가적인 프로그래밍 요소들이 필요 없다. 보다 힘든 작업에 적용하려면 프로파일링(profiling) 또는 코드가 어디에서 시간을 소비하는지를 검사하는 것도 좋다. 낭비가 많고 느린 알고리즘만큼, 병목 현상도 분명한 재작업의 원인이 된다. 사이클을 보강하기 위해 코드를 조정하는 것도 좋지만, 프로파일링을 선행하지 않고 어떤 최적화도 시도해서는 안된다.






예고

다음 회에는 최적화를 주제로 여러분을 찾아오겠다. 디버깅, 빠른 텍스트 검색, 대안 웹 서버 등에 대해서도 설명하겠다. 그 동안 여러분은 PHP 가속기와 opcode 최적화에 대해 복습하기 바란다. 조금만 시간을 들여 공부하면 많은 깨달음을 얻을 수 있을 것이다. 여러분의 머신이 여유 사이클로 무엇을 할 수 있는지를 상상해 보라.

기사의 원문보기



참고자료

교육
  • PHP.net - PHP 개발자를 위한 관련 자료
  • 추천 PHP 문서 리스트
  • PHP 관련 기술자료 리스트 (developerWorks)
  • PHP 관련 기술자료 리스트 (한국 developerWorks)
  • PHP 스킬을 넓히고 싶다면 IBM developerWorks의 PHP project resources를 참고하세요.
  • developerWorks podcasts에서 소프트웨어 개발자들의 재미있는 인터뷰나 토론을 볼 수 있습니다.
  • developerWorks technical events and webcasts.
  • IBM 오픈 소스 개발자들의 최신 컨퍼런스, trade show, webcast와 기타 이벤트
  • 한국 developerWorks 오픈 소스 존 - 오픈 소스 기술을 사용하고, IBM의 제품들과 함께 사용하는데 있어서 도움이 될 만한 HOT-TO 정보, 툴, 프로젝트 업데이트를 만나보십시오. Eclipse, Globus/Grid, Apache, Derby/Cloudscape, Linux, PHP, Perl, Python을 비롯하여 라이센싱과 오픈 소스 개발과 관련한 주제들을 다루고 있습니다.
  • Safari Books Online 방문하면 오픈 소스 기술에 대한 좋은 자료를 찾아 볼 수 있습니다.


제품 및 기술 얻기

토론


필자소개

Martin Streicher는 Linux Magazine의 편집장이며, Hesketh.com에서는 웹 개발자로, developerWorks의 기고자로 활동하고 있다. 퍼듀대학교에서 컴퓨터 공학 석사 학위를 받았으며, 1986년부터 유닉스 계열 시스템을 프로그래밍 하고 있다.



출처 : http://www.ibm.com/developerworks

Trackback 0 Comment 0
2009. 5. 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