100

자바스크립트 성능 이야기 : NHN은 이렇게 한다!

  • View
    272

  • Download
    24

Embed Size (px)

DESCRIPTION

박재성, 심상민, 양정권, 황준호 지음 | TECH@NHN 시리즈 _ 005 | ISBN: 9788998139001 | 18,000원 | 2012년 09월 18일 발행 | 396쪽

Citation preview

Page 1: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!
Page 2: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!
Page 3: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!
Page 4: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

어렸을 때 집에 있던 TV 채널을 바꾸려면 TV 앞으로 가서 손으로 직접 채

널 손잡이를 돌려야만 했다. 그러다 언제부터인가 리모컨으로 바뀌기 시

작하더니 이제는 직접 TV를 조작하려면 버튼이 어디 있는지 한참을 찾아

야 한다. 아름다운 디자인을 위해 눈에 잘 보이지 않는 곳에 버튼을 숨겨

놨기 때문이다. 하지만 불편을 느끼는 사람은 별로 없다. 대부분의 사람들

은 직접 버튼을 누르기보다는 리모컨을 사용해 TV를 조작하기 때문이다.

그리고 리모컨을 사용해 백여 개가 되는 채널을 순식간에 뒤져 본다. 화면

에 나오고 있는 방송이 재미있는지 없는지, 볼만한지 아닌지 확인하는 데

오랜 시간이 걸리지 않는다. 그야말로 순식간이다. 웹 사이트를 이용하는

사용자도 TV 시청자와 비슷하다. 그들의 손에는 리모컨 대신 키보드와 마

우스가 있다. 그리고 두 가지 도구를 사용해 이제는 세는 것조차 불가능해

보일 만큼 많은 웹 사이트를 빠르게 항해한다. 그리고 수많은 웹 사이트에

서는 이런 사용자를 잡아 놓기 위해 많은 노력을 기울인다.

하지만 사용자는 냉정하다. 웹 사이트를 둘러보고 이내 빠른 속도로 다른

사이트로 떠나 버린다. 여기서 잠깐. 사용자는 왜 떠나는 것일까? 웹 사이

트에서 제공하는 내용이 마음에 안 들어서? 더 이상 웹 사이트의 다른 페

이지를 볼 필요가 없어서? 개발자로서 이런 고민은 기획자나 디자이너에

게 넘겨 주고 개발자가 담당하는 부분으로만 범위를 좁혀 고민해 보자. 왜

떠났을까? 버그? 만약 웹 사이트가 정상적으로 동작하지 않는 문제가 있

다면 그것은 그 누구에게도 책임을 미룰 수 없다. 버그는 하루라도 빨리 없

애야 한다. 어느 정도 문제를 해결해 웹 사이트를 정상적으로 이용할 수 있

는 상황임에도 사용자가 계속 떠난다면 이번에는 웹 사이트가 원활하게

동작하는지 관심을 가질 차례다. 웹 페이지가 화면에 보일 때까지 걸리는

추천사

Page 5: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

시간이 너무 긴 것은 아닌가? 웹 페이지가 정상적으로 표시됐지만 웹 페이지

구성 요소의 동작이 너무 느린 것은 아닌가? 사용자에게 인내심을 요구하고

있는 것은 아닐까? 사용자는 절대 기다려 주지 않는다.

만약 사용자에게 인내심을 요구하고 있다고 생각한다면 지금보다 웹 사이트

의 성능을 높일 수 있는 방법이 없을지 고민해야 한다. 성능을 높인다는 것은

단순히 체감 속도를 높여 만족감을 높이는 것만은 아니다. 웹 사이트의 성능

을 높이면 한정된 시간에 사용자에게 더 많은 것을 보여 줄 수 있고 사용자로

부터 행동을 이끌어 낼 가능성을 높일 수 있다. 또한 느린 사이트로 사용자가

옮겨 가는 것을 줄여 사용자를 모을 수도 있다. 그리고 네트워크와 같은 한정

된 자원을 절약할 수도 있다. 이것은 동시에 더 많은 사용자가 사이트를 이용

할 수 있다는 것을 의미하며, 더 적은 비용으로 서비스를 운영할 수 있다는

것을 의미한다.

이 책에서는 동적인 웹 사이트를 만들기 위해 주로 사용하는 언어인 자바스

크립트를 중심으로 성능을 높이는 방법을 설명한다. 몇 년간 NHN의 다양

한 서비스에 적용해 효과를 본 방법부터 최근 화제가 되고 있는 HTML5의

여러 요소를 활용한 방법까지 다양한 방법을 이 책에서 확인할 수 있을 것이

다. 또한 단순히 방법을 소개하는 데 그치지 않고 실제 서비스에 적용한 사례

도 소개하고 있으므로 실제 웹 사이트의 성능을 높이고자 하는 개발자에게

큰 도움이 되리라 생각한다.

이 책이 모쪼록 웹 사이트의 성능으로 고민하는 개발자에게 도움이 되기를

바란다.

- Ajax UI랩장 장정환

Page 6: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

오늘날의 웹은 수많은 기술로 이뤄져 있고, 과거 PC로만 이용하던 웹 서비

스는 이제 모바일 기기로 영역을 빠르게 확장하고 있다. 다양한 기기를 이

용해 다양한 장소에서 웹을 사용하기 시작하면서 웹 페이지를 제대로 그

리고 빠르게 보여주는 것, 즉 성능에 대한 관심과 중요성이 점점 증가하고

있다.

성능을 높이는 방법은 이전에도 많이 다뤄 온 주제다. 그러나 단순히 방법

만 논하는 것에서 더 나가 그 방법이 등장하게 된 배경과 기반 기술을 이해

한다면 각 상황에 맞춰 성능 최적화를 이룰 수 있을 것이다.

이 책은 성능과 관련된 기본적인 주제를 다루면서 동시에 그 방법들이 필

요한 이유를 설명하고 제시된 방법들을 실제 예제로 확인할 수 있도록 구

성했다.

1장 웹 사이트 최적화에서는 프론트엔드 최적화에 대한 기본 지식과 브라

우저가 웹 페이지를 보여 주기 위해 어떤 단계를 거치는지 설명한다.

2장 기본적인 웹 사이트 최적화 방법에서는 성능을 높이기 위해 가장 널

리 사용하는 방법들을 다룬다. HTTP 요청을 최소화하는 방법, 전송되는

파일의 크기를 줄여 네트워크 트래픽을 감소시켜 필요한 리소스를 더 빠

르게 로딩하는 방법, 그리고 간단하지만 많은 개발자들이 간과할 수 있는

파일의 로딩 순서(배치)를 변경하는 방법 등을 다룬다.

3장 성능을 높이는 코드 스타일은 특별하고 새로운 방법을 다루지는 않

지만, 코드 작성에서 쉽게 간과하는 성능 관련 내용을 다루고 있다. 일상적

인 코드 작성 스타일을 바꾸는 것만으로도 성능을 크게 향상시킬 수 있음

을 예제를 통해 직접 확인해 본다.

머리말

Page 7: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

4장 렌더링은 브라우저가 HTML 코드를 분석해 화면으로 출력하는 과정

을 설명한다. 이 과정에서 발생할 수 있는 성능 비용과 이를 최소화하기 위

한 방법을 다루며, 최근 브라우저에서 경쟁적으로 도입하고 있는 하드웨어

가속 렌더링에 대한 내용도 다룬다.

5장 UI 스레드와 타이머의 활용에서는 UI 스레드에 대한 이해와 함께 타

이머의 동작 방식에 대해 자세히 살펴본다. 타이머를 활용해 작업을 스케줄

링하는 방법과 이를 활용한 성능 향상 방법을 소개한다. 그 외 관련 신기술

도 함께 소개한다.

6장 DOM 스크립팅은 자바스크립트 코드의 대다수를 차지하는 DOM

핸들링과 관련된 내용을 다룬다. 간단하지만 성능에 큰 차이를 가져올 수

있는 방법들을 소개하며, CSS 규칙의 활용과 피해야 할 코드 패턴을 소개

한다.

7장 AJAX와 다이내믹 로딩에서는 AJAX 응답 캐싱을 사용해 더 빠르게

AJAX를 처리하는 방법과 리소스를 필요한 시점에 로딩해 초기 페이지 로

딩 속도를 높일 수 있는 방법을 다룬다. 또한 간단한 사례를 통해 실제 얼마

만큼 성능을 높일 수 있는지 확인해 본다.

8장 성능을 높이는 신기술은 아직 잘 알려지지 않은 신기술을 어떻게 성

능 향상에 활용할 수 있는지 설명한다. 최신 기술 동향과 새로운 기술의 사

용 방법을 이해할 수 있다.

9장 네이버 메일 3.0 성능 개선 사례에서는 과거 네이버 메일 서비스의 성

능에 어떠한 문제가 있었는지와 이를 개선하기 위해 어떠한 방법들을 적용

Page 8: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

했는지 확인해 본다. 이론적인 부분들이 어떻게 사용되고 적용됐는지 파악

하는 데 큰 도움이 될 것이다.

책의 내용을 처음부터 순서대로 읽을 필요는 없다. 각 장들은 흐름을 갖고

진행되긴 하지만 필요한 장의 내용만을 따로 읽어도 문제가 없도록 구성했

다. 조금은 어려울 수 있는 내용도 있어 초급 개발자보다는 중급 개발자 이

상에게 적합할 수 있다. 하지만 일부 내용은 초급 개발자가 읽기에 무리 없

을 정도의 수준으로 설명했다.

이 책을 통해 NHN UIT(User Interface Technology) 센터에서 오랫동안

고민해 왔던 자바스크립트를 활용한 프론트엔드 성능 향상 방법과 노하우

가 독자 여러분에게 잘 전달될 수 있기를 기대하며, 웹을 이용하는 모든 사

용자에게 더욱 더 빠른 경험을 줄 수 있는 수많은 결과물의 시작점이 될 수

있기를 바란다.

마지막으로 이 책은 UIT센터의 수많은 동료들의 헌신과 경험의 공유가 없

었다면 탄생하기 어려웠을 것이다. 직접 눈에 보이지는 않지만 수많은 뛰어

난 개발자들의 노력은 NHN 서비스 곳곳에 녹아 들어 여러분이 더욱 빠른

웹을 경험할 수 있도록 도움을 준다. 그들의 노력에 박수를 보낸다.

Page 9: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

몇 년 전 성능 향상 방법을 처음 접하고 개발 중이던 서비스에 적용해 본 기

억이 떠오른다. 이전까지 프론트엔드의 성능 향상은 서비스 개발 측면에서

현재와 같이 중요하게 다뤄지지는 않았고, 초창기 성능 향상과 관련된 방법

을 적용하면서도 그것이 어떻게 그러한지에 대해 깊이 있게 이해하지는 않

았던 것 같다. 이후 조금씩 관련 내용들을 이해하고 새로운 내용들을 알아 가

면서 언젠가 관련 지식을 정리해 보면 좋겠다고 생각했었는데, 마침 기회가

되어 저자로 참여할 수 있었다. 이번이 개인적으론 저자로 참여하는 두 번째

책이어서 이전 책에서 아쉬웠던 부분들을 채우고 싶었지만 언제나 그렇듯

글로 뭔가를 표현한다는 것은 정말 어려운 일임을 다시 한번 깨닫는다.

아무쪼록 부족한 부분이 많지만 누군가에게 이 책의 내용이 도움이 되었으

면 하는 바람이고, 마지막으로 책을 쓰는 동안 묵묵히 옆에서 도움을 준 사

랑하는 아내 혜진에게 고마움을 표하고 싶다.

- 박재성

원고를 쓰고 있는 중에도 신기술이 쏟아져 나와 다 담을 수 없는 것이 안타

까웠다. 욕심은 많은데 할 수 있는 것이 한정돼 있다. 그래도 평소에 성능에

관해 하고 싶었던 말을 담을 수 있는 좋은 계기가 아니었나 싶다. 책을 읽은

독자가 여기서 멈추지 않고 성능에 대해 더 고민하고 공부했으면 한다. 성

능은 아무리 공부해도 아깝지 않은 분야이기 때문이다. 그리고 이를 계기로

자바스크립트에 대해 깊게 고민하는 개발자가 많아 졌으면 한다. 그들과 생

각을 나누고 싶다.

저자의 말

Page 10: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

이 책을 만들 수 있게 도와준 Ajax UI랩원들과 팀장님, 랩장님 그리고 센터

장님께 감사의 말씀을 드리고 싶다. 그리고 여름 휴가까지 가서 책을 썼음에

도 묵묵히 옆에서 도와주고 응원해준 아내 목련에게 사랑한다는 말을 전하

고 싶다.

- 심상민

2007년경 메일 3.0 서비스의 UI 개발을 담당하면서부터 프론트엔드 최적

화의 중요성을 알게 됐다. 전통적인 페이지 단위의 개발 방식에서 한 페이지

에서 모든 것을 구현하는 방식으로 변경되면서 여러 가지 예상하지 못한 문

제가 나타났고 그러한 문제를 해결하느라 고군분투했던 때가 생각난다. 이

후 메일 3.0, 캘린더, N드라이브와 같은 대용량 자바스크립트 서비스를 개

발하면서 프론트엔드 성능에 대한 노하우를 쌓게 됐고 차츰 문서도 쌓여 갔

다. 이 책에서 소개한 최적화 방법은 대부분 UIT센터의 Ajax UI랩원들이

몸소 경험해서 얻은 값진 결과라고 할 수 있다. 이 자리를 빌어 이제껏 함께

고생했던 Ajax UI랩원들에게 감사드린다는 말을 전하고 싶다.

그리고 대부분의 최적화 책들이 해외 서적이어서 국내 저자가 집필한 책이

없는 것이 아쉬웠고 언젠가 이런 소중한 경험을 책으로 출간해 많은 UI개발

자에게 도움을 줘야겠다고 생각했다. 이 책 한 권이 어딘가에서 고군분투하

고 있을 UI 개발자 한 명에게라도 도움을 줬으면 좋겠다.

- 양정권

Page 11: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

사는 동안 어떤 제목, 어떤 내용의 책이 되든 내 이름이 쓰여 있는 책 한 권

을 내 보고 싶다는 생각을 막연하게 했던 적이 있습니다. 우연치 않은 기회

에 업무 영역에서 책을 쓸 기회를 얻게 됐고, 지금은 기대 반, 걱정 반으로

마무리 작업을 하는 단계까지 이르렀네요. 무언가를 독자에게 전달하기 위

해 책을 써 내려간 과정들은 정보의 공유라는 성과 외에도 제 자신에게 그동

안 쌓아 온 지식을 정리하고, 검증하고, 부족한 부분은 채울 수 있는 시간이

됐던 것 같습니다. 이제는 제가 지금껏 기록한 지식들이 좀 더 쾌적한 웹 서

비스 사용 환경을 만드는 데 조금이나마 도움이 되기를 바랄 뿐입니다.

책을 쓰는 과정에서 정말 많은 분들이 도움을 주셨는데요, 좋은 기회를 주신

검색Ajax팀의 양정권 팀장님과 좋은 서비스를 함께 만들어가는 검색Ajax

팀원들, 집필 과정 전반을 이끌어주신 박춘권 차장님과 책의 리뷰에 도움을

주신 많은 분들께 무한한 감사를 드리며, 항상 함께해 주는 가족과 하나님께

감사드립니다.

- 황준호

Page 12: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1

웹 사이트 최적화

웹 사이트 성능과 최적화 �������������������������������������������������������� 22

브라우저 동작 방식을 기반으로 한 최적화 �������������������������������� 23

서비스 이동 단계 ........................................................................ 24

리다이렉트 단계 ......................................................................... 25

애플리케이션 캐시 확인 단계 ....................................................... 29

네트워크 통신 단계..................................................................... 32

브라우저 처리 단계..................................................................... 37

요약 ������������������������������������������������������������������������������������ 39

목차

Page 13: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2

기본적인 웹 사이트 최적화 방법

HTTP 요청 최소화����������������������������������������������������������������� 42

CSS 스프라이트 기법 활용 ........................................................... 43

헤더에 만료 날짜 추가 ................................................................ 46

자바스크립트 파일 통합 .............................................................. 49

파일 크기 최소화 ������������������������������������������������������������������ 50

Gzip 압축을 이용한 파일 크기 최소화 ........................................... 50

쿠키 크기 최소화 ........................................................................ 52

렌더링 성능 향상 ������������������������������������������������������������������ 53

스타일시트와 자바스크립트 배치를 이용한 성능 향상 ...................... 54

초기 렌더링 시 AJAX 요청 최소화 ................................................ 54

마크업 최적화 ............................................................................ 55

요약 ������������������������������������������������������������������������������������ 58

Page 14: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3

성능을 높이는 코드 스타일

객체의 생성, 초기화 성능 ������������������������������������������������������� 60

배열의 생성, 초기화 성능 비교 ..................................................... 60

오브젝트(Object) 객체의 생성, 초기화 성능 비교 ........................... 62

스코프 체인 탐색과 성능 �������������������������������������������������������� 64

스코프 체인이란? ....................................................................... 65

지역변수를 활용한 스코프 체인 탐색 성능 개선 .............................. 69

프로토타입 체인 ......................................................................... 71

그 외 스코프 체인 탐색 성능에 영향을 미치는 요소 .......................... 72

반복문과 성능 ���������������������������������������������������������������������� 73

반복문의 성능 비교..................................................................... 73

for, while, do-while 구문의 최적화 ............................................. 76

효율적인 알고리즘 구현을 통한 성능 개선 ..................................... 81

조건문과 성능 ���������������������������������������������������������������������� 82

조건문의 성능 비교..................................................................... 82

조건문 최적화 ............................................................................ 82

코드별 장단점 비교..................................................................... 91

문자열 연산과 성능 ��������������������������������������������������������������� 92

문자열 생성 성능 비교 ................................................................ 92

문자열 연산 성능 비교 ................................................................ 93

Page 15: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

정규 표현식과 성능 ��������������������������������������������������������������� 95

탐색 대상 축소를 통한 성능 향상 .................................................. 95

컴파일 횟수 축소를 통한 성능 향상 ............................................... 96

요약 ������������������������������������������������������������������������������������ 97

4

렌더링

렌더링 과정 ����������������������������������������������������������������������� 102

렌더링이 진행되는 과정 ............................................................ 102

리플로와 리페인트 ................................................................... 106

렌더링 과정 확인하기 ............................................................... 108

리플로 최소화 방법 ������������������������������������������������������������� 112

작업 그루핑 ............................................................................. 112

실행 사이클 ............................................................................. 113

노출 제어를 통한 리플로 최소화 방법 .......................................... 115

캐싱 ....................................................................................... 118

CSS 규칙 ................................................................................ 119

하드웨어 가속 렌더링 ���������������������������������������������������������� 119

브라우저에서의 활용 ................................................................ 120

웹킷의 GPU 렌더링 .................................................................. 124

인터넷 익스플로러의 GPU 렌더링 .............................................. 125

요약 ���������������������������������������������������������������������������������� 126

Page 16: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

5

UI 스레드와 타이머의 활용

UI 스레드의 정의와 특징 ������������������������������������������������������ 128

타이머의 정의와 특징 ���������������������������������������������������������� 130

setTimeout() 함수와 setInterval() 함수의 차이 ........................... 131

타이머는 얼마나 정확한가? ....................................................... 135

타이머를 활용한 작업 스케줄링 ��������������������������������������������� 137

페이지 로딩 시 발생할 수 있는 문제 ............................................ 137

점진적 처리를 위한 작업 스케줄링 .............................................. 139

점진적 렌더링(Progressive Rendering) ...................................... 141

대기 시간 줄이기 ...................................................................... 142

고려할 수 있는 새로운 방법들 ����������������������������������������������� 144

효율적 스크립트 실행 - setImmediate() 함수 .............................. 144

단일 스레드 극복 - 웹워커(Web Worker) .................................... 145

효율적인 스크립트 애니메이션 실행

- requestAnimationFrame() 함수 ............................................. 150

요약 ���������������������������������������������������������������������������������� 151

Page 17: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

6

DOM 스크립팅

자바스크립트 코어와 DOM 스크립팅 ������������������������������������ 154

자바스크립트 코어의 성능 발전 .................................................. 155

자바스크립트 벤치마크와 DOM 스크립팅 .................................... 156

DOM 접근 최소화 �������������������������������������������������������������� 160

DOM 캐싱 .............................................................................. 160

HTML 컬렉션 캐싱 ................................................................... 162

자바스크립트 객체 캐싱 ............................................................ 166

CSS 속성 변경 �������������������������������������������������������������������� 170

CSS 클래스나 규칙 사용 ............................................................ 170

CSS 규칙 사용 ......................................................................... 174

인터넷 익스플로러에서 성능 저하를 가져오는

:hover 선택자 ......................................................................... 176

CSS 선택자 사용 ...................................................................... 177

이벤트 ������������������������������������������������������������������������������ 180

이벤트 위임 ............................................................................. 181

빠르게 반복되는 이벤트의 단순화 ............................................... 185

요약 ���������������������������������������������������������������������������������� 187

Page 18: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

7

AJAX와 다이내믹 로딩

효율적인 AJAX 통신 ����������������������������������������������������������� 190

변하지 않는 데이터................................................................... 190

일정한 주기로 변하는 데이터 ..................................................... 193

로컬 저장소를 이용한 캐싱 ........................................................ 195

다이내믹 로딩과 최적화 ������������������������������������������������������� 198

이미지 로딩 속도 최적화 ........................................................... 198

다이내믹 로딩을 활용한 최적화 .................................................. 200

지연 로딩 ................................................................................ 201

최적화와 브라우저 점유율 ......................................................... 204

동적인 자바스크립트 실행 ......................................................... 206

의존성 있는 자바스크립트의 다이내믹 로딩 .................................. 214

최적화 사례 - 상세 검색 ������������������������������������������������������� 230

요약 ���������������������������������������������������������������������������������� 233

Page 19: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

8

성능을 높이는 신기술

폴리필 ������������������������������������������������������������������������������ 236

내비게이션 타이밍 API �������������������������������������������������������� 238

웹 저장소 �������������������������������������������������������������������������� 243

웹 저장소의 활용 ...................................................................... 244

IndexedDB와 웹 SQL 데이터베이스와의 비교 .............................. 248

애플리케이션 캐시 �������������������������������������������������������������� 251

웹소켓 ������������������������������������������������������������������������������ 255

양방향 통신과 코멧................................................................... 255

지속적으로 서버와 연결할 수 있는 웹소켓 .................................... 258

웹소켓 사용 방법 ...................................................................... 261

웹소켓 서버 ............................................................................. 262

CSS3 3D Transforms ��������������������������������������������������������� 264

Prefetch와 Pre-Rendering ������������������������������������������������� 267

요약 ���������������������������������������������������������������������������������� 268

Page 20: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

9

네이버 메일 3.0 성능 개선 사례

메일 3.0 서비스 개발과 성능 문제 ��������������������������������������� 272

네트워크 요청 줄이기 �������������������������������������������������������� 274

헤더에 만료 날짜 설정 ............................................................ 275

버퍼 비우기 ........................................................................... 275

AJAX 요청 횟수 줄이기 .......................................................... 278

CSS 스프라이트 사용 .............................................................. 278

외부에 스타일시트 파일과 자바스크립트 파일 작성 ..................... 279

‘404 Not Found’ 발생 원인 제거 ............................................. 279

파일 크기 줄이기 �������������������������������������������������������������� 280

Gzip 컴포넌트 사용 ................................................................ 280

CSS 파일과 자바스크립트 파일 통합 ......................................... 280

사용하지 않는 CSS 규칙 제거 ................................................... 281

쿠키 크기 줄이기 .................................................................... 282

페이지 크기를 증가시키는 디자인 수정 ...................................... 283

초기 페이지 로딩 속도 높이기 ��������������������������������������������� 284

렌더링 방식 변경 .................................................................... 285

AJAX 캐시 사용 ..................................................................... 286

IFrame 사용 줄이기 ................................................................ 288

CSS Expression( ) 표현식 사용하지 않기 .................................. 289

요약 �������������������������������������������������������������������������������� 291

Page 21: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

사용자 경험(UX, User Experience) 전문가 제이콥 닐슨(Jakob Nielsen)은 웹 페이지의 반응 시

간(Response Time)이 되도록 1초 이내여야 사용자 경험에 긍정적인 영향을 미친다고 분석했

다. 그 이상 시간이 걸리면 컴퓨터의 동작에 이상이 생겼다고 생각해 결국 웹 페이지를 떠나고

사용자 경험에 부정적인 영향을 미치게 된다. 이처럼 웹 페이지의 반응 시간, 즉 응답 속도는 웹

서비스의 경쟁력 가운데 하나다.

DHTML과 AJAX(Asynchronous JavaScript and XML) 기술로 기존의 정적이었던 웹 서비스가

한 화면에서 여러 기능을 실행하는 동적인 웹 서비스로 바뀌면서 웹 페이지의 응답 속도도 이

전에 비해 느려졌다. 아무리 좋은 콘텐츠를 보유하고 화려한 UI(User Interface, 사용자 인터페

이스)로 무장해도 웹 페이지의 응답 속도가 느리면 사용자는 외면한다. 느려진 웹 서비스의 속

도를 개선하기 위해 2009년경부터 국내에서는 클라이언트(브라우저)에서의 성능 개선에 집중

하게 됐고, 웹 사이트를 최적화하려고 수많은 개발자들이 연구와 개발을 병행하고 있다.

웹 서비스는 모두 브라우저에서 표현된다. 그렇기 때문에 브라우저가 어떻게 동작하는지 이해

하는 것은 웹 사이트 최적화에 꼭 필요한 일이다. 이 장에서는 브라우저가 웹 페이지를 처리하

는 방식을 서비스 이동 단계, 리다이렉트 단계, 애플리케이션 캐시 확인 단계, 네트워크 통신 단

계, 브라우저 처리 단계로 나눠 살펴보고, 각 단계마다 최적화에 고려해야 할 사항들을 알아보

겠다.

웹 사이트 최적화

1

Page 22: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

22 자바스크립트 성능 이야기

웹 사이트 성능과 최적화

제이콥 닐슨의 웹 사이트인 “useit.com(http://www.useit.com/)”은 웹 사이트의 반응

시간을 다음과 같이 평가한다1.

• 0�1초: 사용자의 동작에 해당 기능이 바로 반응한다고 느끼는 시간

• 1초: 불필요하게 오래 기다리지 않았다고 느끼는 시간. 0.2~1초의 시간은 컴퓨터가 동작하는 시

간으로 이해할 수 있는 시간이다. 시간이 1초 이상 걸리면 컴퓨터의 동작에 이상이 생겼다고 생각

하게 된다.

• 10초: 사용자가 집중력을 잃지 않는 최대 시간

이 평가에 따르면 웹 페이지의 반응 시간이 1초 이내여야 사용자 경험에 긍정적인 영향

을 미친다. 그렇다면 현실은 어떤가? 웹 사이트의 기능이 점점 복잡해지고 많아지면서

느린 반응 속도 때문에 이용 중인 서비스를 떠나 다른 유사한 서비스를 이용하게 만드

는 사이트가 있는 반면, 빠른 반응 속도로 웹 사이트를 탐색하는 데 큰 불편함이 없는 서

비스를 제공하는 사이트도 있다.

웹 서비스 분야에서는 몇 년 전부터 데스크톱에서 사용하는 오피스 프로그램이나 메일,

사진 관리 프로그램 등과 같은 설치형 애플리케이션을 웹에서도 사용할 수 있는 웹 애

플리케이션으로 제공하기 시작했다. 웹 애플리케이션은 설치형 애플리케이션과 같은

디자인과 사용자 경험을 갖고 있어, 인터넷 접속만 가능하다면 언제 어디서든 동일한

UI의 애플리케이션을 사용할 수 있다. 하지만 설치형 애플리케이션의 빠른 응답 속도와

성능, UX에 익숙한 사용자들은 웹 애플리케이션에서도 설치형 애플리케이션과 동일한

성능과 UX를 기대했고, 웹 애플리케이션의 느린 응답 속도와 로딩 시간은 사용자에게

적지 않은 실망감을 안겨주었다.

많은 포털과 서비스 업체에서는 이러한 사용자의 기대에 부응하기 위해 여러 가지 방

법을 시도했다. 그러던 중에 웹 페이지 성능 최적화라는 개념이 생겨났다. 웹 서비스

의 반응 속도를 개선할 수 있는 가장 효과적인 방법은 네트워크 속도의 개선이지만

이는 단기간에 이루기 힘든 과제이며 인터넷 서비스를 제공하는 업체에서는 불가능

1 더 자세한 내용은 “Response Times: The 3 Important Limits(http://www.useit.com/papers/responsetime.html)” 문서를 참조한다.

Page 23: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 23

한 부분이다. 네트워크의 속도를 개선하는 대신 반대로 네트워크 사용량을 줄이고 브

라우저에서 웹 페이지의 실행 속도를 높여 웹 페이지의 반응 속도를 개선하고 있다.

웹 페이지의 성능 최적화가 주목받으면서 다양한 최적화 기법이 등장했고, 지금도 브라

우저 개발사와 수많은 개발자들이 좀 더 나은 최적화 방법을 찾고 있다.

브라우저 동작 방식을 기반으로 한 최적화

웹 페이지 최적화는 브라우저가 네트워크와 통신하는 과정은 물론 브라우저에서 페이

지를 표시하는 과정에서의 응답 속도를 개선하는 것이다. 그래서 무턱대고 성능을 개선

하기보다는 브라우저가 어떻게 동작하는지 이해해야 올바른 개선 방향을 잡을 수 있다.

사용자는 원하는 웹 사이트로 이동하기 위해 브라우저의 주소창에 주소를 입력하

고 Enter 키를 누르거나 링크를 클릭한다. 그런 다음에 브라우저는 서버에 요청을 보

내 페이지를 구성하는 자원을 다운로드하고 이 자원을 이용해 요청 결과를 화면에 보

여 준다. 이때 브라우저가 어떤 단계로 동작하는지, 단계별로 얼마나 시간이 걸리는

지 정의한 연구 활동이 W3C(World Wide Web Consortium)의 내비게이션 타이밍

(Navigation Timing) 명세2다. 내비게이션 타이밍 명세의 가장 큰 목적은 정확한 단계

별 소요 시간을 측정하는 것이지만 브라우저의 처리 단계를 정확하게 정의했다는 데도

큰 의미가 있다.

다음 그림은 내비게이션 타이밍 명세에서 브라우저가 사용자의 요청을 처리하는 순서

를 정리한 프로세싱 모델3에서 웹 페이지 최적화와 관련 있는 단계를 정리한 것이다.

서비스 이동

단계

(Prompt for unload)

리다이렉트

단계

(redirect)

애플리케이션 캐시 확인

단계

(App Cache)

네트워크 통신단계

(DNS, TCP, Request,

Response)

브라우저 처리단계

(Processing, onload)

그림 1-1 브라우저가 사용자 요청을 처리하는 순서

2 내비게이션 타이밍 명세에 대한 자세한 내용은 “Navigation Timing(http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/

Overview.html)” 문서를 참조한다.

3 프로세싱 모델에 대한 자세한 내용은 http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#processing-

model을 참조한다.

Page 24: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

24 자바스크립트 성능 이야기

• 서비스 이동 단계: 사용자가 웹 서비스를 이용하다 다른 주소로 이동할 때 브라우저가 제일 먼저 실

행하는 단계다. 프로세싱 모델의 Prompt for unload에 해당한다.

• 리다이렉트 단계: 사용자가 요청한 URL에서 다른 URL로 다시 보내는 단계다. 프로세싱 모델의

redirect에 해당한다.

• 애플리케이션 캐시 확인 단계: 브라우저의 캐시에 데이터가 있는지 확인하는 단계다. 프로세싱 모

델의 App Cache에 해당한다.

• 네트워크 통신 단계: 브라우저가 네트워크와 통신해서 웹 페이지와 구성 요소를 다운로드하는 단계

다. 네트워크 통신 단계에 해당하는 프로세싱 모델의 단계는 DNS, TCP, Request, Response

등이다.

• 브라우저 처리 단계: 다운로드한 웹 페이지와 구성 요소로 웹 페이지를 화면에 그리는 단계다. 프로

세싱 모델의 Processing과 onLoad에 해당한다.

프로세싱 모델의 각 단계를 기준으로 성능 개선을 위해 고려할 내용은 무엇인지 살펴보

겠다.

서비스 이동 단계

프로세싱 모델의 Prompt for unload4에 해당하는 서비스 이동 단계는 사용자가 웹 서

비스를 이용하다가 다른 주소로 이동할 때 브라우저가 제일 먼저 실행하는 단계다. 이

단계의 작업은 다른 주소로 이동하기 전에 보고 있던 페이지에서 실행하는데, 모두 브

라우저 내부에서 처리하기 때문에 브라우저 성능과 직결된다.

웹 서비스를 이용하면 페이지가 표시될 때 우리도 모르게 이벤트가 할당되고 이때 메모

리를 조금씩 사용한다. 그런데 이 메모리를 더 이상 사용하지 않을 때, 즉 페이지를 떠날

때는 메모리를 해제해야 한다. 이 메모리 해제 작업이 서비스 이동 단계에서 실행하는

작업 가운데 하나다.

필요 없는 메모리 해제를 담당하는 가비지 컬렉션 기능이 제대로 동작하지 않으면 브라

우저가 응답 없음 상태가 되거나 실행 속도가 급격히 느려진다. 2001년에 출시된 인터

넷 익스플로러 6을 오래 사용하다 보면 전체적인 동작이 느려지고 갑자기 응답 없음 상

태가 된다. 이런 현상은 브라우저에서 기본적으로 동작하는 가비지 컬렉션이 제대로 동

작하지 못해서 발생하는 현상이다.

4 Prompt for unload 단계에서 실행하는 절차에 대한 더 자세한 내용은 http://www.whatwg.org/specs/web-apps/current-work/multipage/

history.html#prompt-to-unload-a-document 문서를 참조한다.

Page 25: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 25

웹 페이지에서 동시에 많은 변수가 생성되고 처리되는 동안 브라우저에서 허용한 임계

치를 넘었을 때 가비지 컬렉션이 동작하는데, 가비지 컬렉션이 동작하면 스크립트 실행

이 중단된다. 가비지 컬렉션이 완료되기 전까지는 스크립트가 동작하지 못해 페이지가

느려지는 것이다. 이를 해결하는 여러 가지 방법이 있지만 성능이 개선된 최신 브라우

저로 업데이트하는 방법이 가장 좋다. 만약 웹 서비스가 인터넷 익스플로러 6을 지원해

야 한다면 가비지 컬렉션 문제에 주의해서 개발해야 한다5.

특히 DOM(Document Object Model)의 생성과 삭제가 빈번한 페이지, 한 페이지에

다수의 AJAX 통신이 필요한 페이지, 이벤트 바인딩 수가 많은 페이지, 사용자 체류 시

간이 긴(2분 이상) 페이지를 개발할 때는 필요 없는 변수나 오브젝트 삭제, 이벤트 해제

등을 활용해 메모리를 관리해야 한다. 자바스크립트의 변수와 객체, DOM 객체, AJAX

통신 등을 사용할 때 성능을 높이는 방법은 “3. 성능을 높이는 코드 스타일(59페이지)”

과 “4. 렌더링(101페이지)”, “5. UI 스레드와 타이머의 활용(127페이지)”, “6. DOM 스

크립팅(153페이지)”, “7. AJAX와 다이내믹 로딩(189페이지)”에서 설명할 것이다.

서비스 이동 단계의 작업은 브라우저 내부에서 자동으로 실행한다. 만약 별도로 처리하

려면 beforeunload 이벤트를 활용할 수 있다. 어떤 사이트를 방문한 다음 다른 사이트

로 이동할 때 “이용해 주셔서 감사합니다.”라는 알림 문구가 나타나는 것을 본 적이 있

을 것이다. 이 알림 문구를 나타낼 때 beforeunload 이벤트를 활용한다. 2008년에 네

이버 메일 3.0 서비스와 네이버 캘린더 서비스를 개발할 때 인터넷 익스플로러 6의 가

비지 컬렉션 문제로 페이지의 동작이 느려지거나 응답 없음 상태가 되는 현상이 있었는

데, 이때도 beforeunload 이벤트를 활용하고 메모리 해제를 담당하는 모듈을 만들어서

대응했다.

리다이렉트 단계

프로세싱 모델의 redirect에 해당하는 리다이렉트 단계는 사용자가 요청한 URL에서

다른 URL로 다시 보내는 단계다. 쉽게 볼 수 있는 리다이렉트는 소셜 네트워크 서비스

(SNS, Social Networking Service)에서 사용하는 단축 URL이다. SNS의 특성상 입

5 인터넷 익스플로러 6에서 가비지 컬렉션의 동작 문제와 해결책에 대한 자세한 내용은 마이크로소프트의 지원 사이트(http://support.

microsoft.com/kb/942840)를 참조한다.

Page 26: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

26 자바스크립트 성능 이야기

력하는 글자 수에 제한이 있다. ‘http://search.naver.com/search.naver?sm=tab_

hty.top&where=nexearch&ie=utf8&query=%EC%9B%B9%EC%82%AC%E

C%9D%B4%ED%8A%B8%EC%B5%9C%EC%A0%81%ED%99%94&x=0

&y=0’와 같은 URL을 그대로 SNS에 입력하면 글자 수 제한 때문에 원하는 내용을

다 입력하지 못한다. 단축 URL을 사용하면 SNS의 글자 수 제한을 피할 수 있다. 단

축 URL인 ‘http://me2.do/FDEftv’는 실제로는 ‘http://search.naver.com/search.

naver?sm=tab_hty.top&where=nexearch&ie=utf8&query=%EC%9B%B9%EC%

82%AC%EC%9D%B4%ED%8A%B8%EC%B5%9C%EC%A0%81%ED%99%94

&x=0&y=0’로 리다이렉트되는 주소다.

리다이렉트가 발생하면 상태 코드로 ‘301’이나 ‘302’를 반환하는데, 이는 HTTP 요청을

완수하기 위해서 추가로 뭔가를 해야 한다는 의미다. 이런 이유로 리다이렉트가 발생하

면 어떤 자원도 다운로드하지 않으며 브라우저에 일시적으로 빈 페이지가 보인다. 그렇

기 때문에 특정한 목적으로 리다이렉트를 사용하는 것은 어쩔 수 없지만 의도치 않게 또

는 실수로 리다이렉트가 발생한다면 웹 페이지의 성능 향상을 위해 바로잡아야 한다.

리다이렉트와 관련해서 개발자가 흔히 저지르는 실수 가운데 하나는 URL 뒤에 슬래시

(/)를 넣지 않는 것이다. 인터넷 익스플로러의 주소창에 ‘naver.com’을 입력하고 Enter

키를 누르면, 주소창에 입력한 주소가 ‘http://www.naver.com/’으로 바뀌면서 리다

이렉트된다. 이 과정을 웹 사이트 모니터링 도구인 HttpWatch6로 확인하면 아래와 같

이 302 redirect가 발생하는 것을 볼 수 있다.

그림 1-2 HttpWatch로 확인한 리다이렉트 발생

6 웹 브라우저의 HTTP 요청을 모니터링하고 송수신 데이터를 확인할 수 있는 도구다. HttpWatch에 대한 자세한 내용은

http://www.httpwatch.com/moreinfo.htm을 참조한다.

Page 27: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 27

NHN에서는 HttpWatch와 같은 도구를 이용해 서비스 페이지에서 불필요하게 리다

이렉트되는 요소를 확인해서 개선하고 있다. 실제로 네이버 메인 페이지에 있는 링크를

확인하면 다음 그림과 같이 주소 뒤에 슬래시(/)가 있는 것을 볼 수 있다.

그림 1-3 네이버 메인 페이지의 서비스 링크

두 번째로 실수하는 부분은 웹 페이지를 이용한 리다이렉트다. 흔히 사용하는 방법은

메타태그를 이용하는 방법이다. 다음 예제는 1초 후에 http://www.naver.com/으로

페이지가 이동하는 코드다.

<meta http-equiv="refresh" content="1; url=http://www.naver.com/">

이 코드에는 두 가지 성능 문제가 있다. 첫 번째는 이 코드를 실행시킬 별도의 페이지를

거쳐야 리다이렉트된다는 점이다. 두 번째는 최종 페이지에 도착했을 때 캐시가 설정된

리소스임에도 불구하고 조건부 GET 요청이 이뤄진다는 점이다.

Page 28: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

28 자바스크립트 성능 이야기

조건부 GET 요청은 브라우저에 캐싱된 리소스를 사용하기 전에 해당 리소스를 사용해

도 되는지 서버에 물어 보는 것이다. 이때 헤더의 If-Modified-Since 정보7를 이용해

서버에 요청을 보낸다. 리소스가 수정되지 않았다면 ‘304 Not Modified’ 코드를 받아

캐시에 있는 리소스를 사용한다. 자원이 수정됐다면 서버로부터 리소스를 다운로드

한다.

캐시의 만료 날짜를 설정했다면 서버의 확인을 거치지 않고 바로 캐싱된 리소스를 사용

할 수 있는데, 메타태그로 리다이렉트하면 조건부 GET 요청으로 불필요한 서버 통신

이 발생해 응답 속도가 느려진다.

다음은 HttpWatch로 네이버 메인 페이지(http://www.naver.com/)에 접속하는 과

정을 테스트한 결과다. 그림 1-4는 브라우저 주소창에 직접 http://www.naver.com/

을 입력해 접속한 결과다. 주소창에 직접 URL을 입력해서 접속하면 캐시에서 만료 날

짜가 설정된 파일을 읽어 온다.

그림 1-4 주소창에 직접 입력해서 네이버에 접속한 경우

다음 그림은 앞에서 보여 준 메타태그를 이용해 접속한 결과다. 결과를 비교하면 메타

태그로 리다이렉트했을 때 불필요한 통신이 있음을 확인할 수 있다.

그림 1-5 메타태그를 이용해서 리다이렉트한 경우

만약 개발 기간이 부족해서 기존 페이지에 메타태그를 넣어 리다이렉트 용도로만 사용

하고 해당 페이지에 있던 이미지와 스타일시트, 스크립트 등의 링크를 제거하지 않았다

7 브라우저에 저장된 최종 수정 날짜다. 캐시와 관련된 용어는 "애플리케이션 캐시 확인 단계(29페이지)” 절에서 다시 설명하겠다.

Page 29: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 29

면 이 파일까지 다운로드한다는 문제도 있다. 리다이렉트 용도로만 사용하는 페이지인

데 불필요한 파일을 받는 것이다. 이렇게 되면 사용자는 리다이렉트 페이지를 거쳐 최종

페이지의 렌더링이 완료되기 전까지 영문도 모른 채 빈 페이지만 보고 있어야 한다. 이

빈 페이지가 보이는 시간이 길어질수록 사용자는 답답함을 느낀다.

리다이렉트가 필요하다면 메타태그가 아니라 서버사이드 설정으로 처리하는 편이 더

좋다. 서버사이드 설정으로 리다이렉트하는 대표적인 방법에는 아파치 웹 서버에서 제

공하는 mod_alias8 모듈을 사용하는 방법이 있다. mod_rewrite 모듈도 있지만 패턴

이 복잡하지 않다면 mod_alias 모듈로 충분히 처리할 수 있다.

웹 페이지의 주소뿐 아니라 이미지와 스타일시트, 자바스크립트와 같이 페이지를 구성

하는 요소도 리다이렉트될 수 있다. HttpWatch로 301이나 302 상태 코드가 발생하는

요소들을 찾아보기 바란다. 의도치 않게 리다이렉트가 발생하고 있다면 성능 향상을 위

해서 바로잡아야 한다.

애플리케이션 캐시 확인 단계

리다이렉트 작업을 마치고 HTTP 요청을 처리하기 위한 준비를 마쳤다면 브라우저는

맨 먼저 서버로 요청을 보낸다. 서버에서 응답이 오면 개별 요소(이미지, 스타일시트,

자바스크립트 등)가 사용자 PC에 있는지 캐시 데이터를 찾는다. 별도로 설정하지 않았

다면 윈도우 운영체제에서는 C:\Documents and Settings\{User}\Local Settings\

Temporary Internet Files 디렉터리에 캐시 데이터가 있다. 캐시 데이터의 종류에는

다음과 같이 쿠키(cookie), 이미지, 스크립트, 스타일시트 등이 있다.

그림 1-6 사용자 PC에 있는 캐시 데이터 정보

8 이 책은 자바스크립트로 개발하는 사이트의 성능 최적화에 대해 다루기 때문에 웹 서버 설정에 대한 설명은 하지 않겠다. mod_alias 모듈에 대

한 자세한 내용은 http://httpd.apache.org/docs/2.0/mod/mod_alias.html을 참조한다.

Page 30: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

30 자바스크립트 성능 이야기

캐시 데이터가 있는 이유는 무엇보다도 사용자가 동일한 페이지를 다시 방문했을 때 브

라우저와 서버 사이에 통신을 하지 않고 캐시에 있는 자원을 사용하겠다는 것이다. 성

능 향상을 위해 가장 효과적인 방법은 브라우저와 서버 사이의 통신을 최대한으로 줄이

는 것이다. 그러므로 다시 방문한 사용자에게 좀 더 빠른 응답 속도를 제공하려면 애플

리케이션 캐시를 잘 활용해야 한다.

캐시를 다룰 때는 개별 요소들이 ‘정말 유효한지’, ‘사용해도 되는 것인지’ 검증하는 것

이 중요하기 때문에 몇 가지 용어와 규칙이 필요하다. 표 1-1은 애플리케이션 캐시를

활용할 때 이해하고 있어야 할 몇 가지 중요한 용어를 정리한 것이다. 더 자세한 정보를

알고 싶다면 W3C에서 제공하는 “Header Field Definitions(http://www.w3.org/

Protocols/rfc2616/rfc2616-sec14.html)”를 참고하기 바란다.9

표 1-1 캐시 설정에 필요한 용어

용어 설명

Last-Modified 서버에서 저장하고 있는 마지막 수정 날짜.예: Last-Modified Thu, 06 Aug 2009 00:42:24 GMT

If-Modified-Since

브라우저에서 저장하고 있는 마지막 수정 날짜.예: If-Modified-Since Thu, 14 Jun 2007 07:02:00 GMT

Expires 만료 날짜. 언제까지 유효한지 미리 지정해 놓은 시간 정보로, 특정 날짜까지는 사용할 수 있다는 정보다.예: Expires 08:37:06 Tuesday, December 11, 2012 GMT

Cache-Control Expires 정보의 한계를 극복하려고 HTTP/1.1에서 소개된 캐시 설정 추가 정보

max-age 캐싱할 시간. Cache-Control 정보 가운데 하나로 캐싱할 시간을 초 단위까지 정할 수 있다. Expires 정보는 특정 날짜 및 시간을 정하지만, max-age 정보는 캐싱할 시간을 초 단위로 지정할 수 있다. Expires 정보가 ‘언제까지’를 설정하는 정보라면 max-age 정보는 클라이언트 시간과 관계없이 현재부터 며칠, 몇 년 등으로 지정할 수 있다. 그러므로 만료 날짜를 계속 확인하는 과정이 필요 없기 때문에 더 효율적이다. 다만 이 속성은 HTTP/1.1을 지원하지 않는 브라우저에서는 작동하지 않는다. 그래서 일반적으로 max-age 설정을 이용하려면 Expires 설정과 max-age 설정을 동시에 해야 한다. 자세한 내용은 아파치 웹 서버 설정의 mod_expires 부분

9을 참조하기 바란다.

예: Cache-Control max-age=36000(10시간을 의미한다.)

다음 예제를 보며 브라우저가 이미지 파일을 어떻게 보여 주는지 알아보자.

<img src="http://www.example.com/a.gif">

9 http://httpd.apache.org/docs/2.0/mod/mod_expires.html

Page 31: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 31

브라우저에서 URL 정보를 이용해 서버에 요청을 보내면 마크업 데이터가 브라우저에

도착한다. 이때 a.gif라는 이미지 파일을 다운로드하는 태그를 만났을 때 브라우저가 처

리하는 절차는 다음과 같다.

Expires 정보 확인

If-Modified-Since 정보 확인

If-Modified-Since 정보와 Last-Modified 정보

비교

로컬에 저장된 파일 사용(Cache Read)

로컬에 저장된 파일을 사용하라는 응답

(304 Not Modified)

서버의 파일 전송(200 OK)

서버에 이미지 요청(200 OK)

정보가 없거나 기한이 지났음

정보가 있음

날짜가 같음 날짜가 다름

정보가 없음

정보가 있고 기한이 남아 있음

그림 1-7 브라우저의 이미지 정보 처리 절차

1� Expires 정보 확인

브라우저가 a.gif 파일을 서버에 요청하기 전에 사용자 PC에 저장된 Expires 정보(만료 날짜)를

확인한다. Expires 정보가 있고 설정한 기한이 남았다면 사용자 PC의 a.gif 파일을 사용한다. 이

단계가 네트워크 통신 단계의 Cache Read 단계다.

2� If-Modified-Since 정보 확인

Expires 정보가 없거나 기한이 지났으면 If-Modified-Since 정보(마지막으로 수정한 날짜)가

있는지 확인한다. If-Modified-Since 정보가 없으면 서버에 요청해 이미지를 받는다. 이때 상태

코드는 ‘200 OK’다. If-Modified-Since 정보가 있으면 If-Modified-Since 정보를 서버에 보

낸다.

3� If-Modified-Since 정보와 Last-Modified 정보 비교

서버는 브라우저가 보낸 If-Modified-Since 정보를 Last-Modified 정보(서버에 저장된 마지

막 수정 날짜)와 비교한다. 수정한 날짜가 같다면 상태 코드 ‘304 Not Modified’를 보내 사용

자 PC에 있는 파일을 사용하게 한다. 날짜가 다르다면 서버에서 파일을 보낸다. 이때 상태 코드는

‘200 OK’다.

Page 32: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

32 자바스크립트 성능 이야기

이 과정에서 알 수 있듯이 사용자 PC에 캐시 파일이 있고 Expires 정보가 유효하다면

해당 파일을 서버에 요청하지 않고 캐시에 저장된 파일을 이용한다. 이러면 동적인 이

미지 등을 제외한 나머지 정적인 요소(이미지, 스타일시트, 자바스크립트 등)는 별도로

서버에 요청하지 않기 때문에 로딩 속도를 크게 향상시킬 수 있다. 사용자가 서비스를

처음 방문했을 때는 캐시가 없는 상태이기 때문에 큰 효과를 거둘 수 없겠지만, 캐시를

올바르게 설정했다면 그 이후에는 웹 서비스의 응답 속도를 개선하는 데 큰 도움을 줄

것이다.

네트워크 통신 단계

프로세싱 모델의 DNS, TCP, Request, Response는 모두 네트워크 통신에 관련된 단

계다. 이 네 단계를 거치는 비용은 상당히 크다. 네트워크 통신에 들어가는 비용을 줄이

면 성능 향상에 큰 효과를 볼 수 있다.

네트워크 비용을 줄이는 첫 번째 방법은 Expire 설정이나 Cache Control 속성을 이용

해 사용자 웹 페이지에 다시 왔을 때 캐시를 사용하는 방법이다. 두 번째 방법은 스타일

시트나 자바스크립트와 같이 파일을 합쳐서 서비스해도 문제가 없는 리소스를 합쳐 하

나의 링크로 제공해 요청 횟수를 줄이는 방법이다.

다음 그림은 HttpWatch의 Time Chart 기능으로 네트워크 통신의 각 단계에 걸린 시

간을 측정한 결과다. Time Chart에서는 캐시를 읽어오는 시간을 더 상세하게 구간별로

볼 수 있다.

그림 1-8 HttpWatch의 Time Chart 기능으로 확인한 단계별 네트워크 통신 정보

Page 33: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 33

Blocked

브라우저에서 리소스를 호출하기 전에 소요되는 대기 시간이다. 이 대기 시간은 브라

우저 캐시를 확인하는 시간과 네트워크에 연결하기 전에 소요되는 준비 시간으로 이

뤄진다.

네트워크에 연결하기 전에 소요되는 대기 시간은 브라우저의 동시 연결 개수와 밀접한

관련이 있다. 동시 연결 개수가 많을수록 대기 시간은 더 짧아진다. 오래된 브라우저의

동시 연결 개수는 호스트별로 2개이고 최근에 새로 나온 브라우저의 동시 연결 개수는

최대 8개까지로 늘어나기도 했다. 다음은 스티브 사우더스(Steve Souders)의 웹 사이

트에 있는 “Roundup on Parallel Connections” 문서10에서 정리한 브라우저별 동시

연결 개수다.

표 1-2 브라우저의 호스트별 최대 동시 연결 개수

브라우저 HTTP/1�1 HTTP/1�0

인터넷 익스플로러 6, 7 2 4

인터넷 익스플로러 8 6 6

파이어폭스 2 2 8

파이어폭스 3 6 6

사파리 3, 4 4 4

크롬 1, 2 6 ?

크롬 3 4 4

오페라 9.63, 10.00alpha 4 4

DNS(DNS Lookup)

호스트 이름으로 IP 주소를 조회한 시간이다. 보통 20~120ms(밀리초) 정도 소요된다.

이 작업이 완료되지 않으면 어떤 자원도 다운로드하지 않는다. 브라우저에서 IP 주소를

조회할 때 동일한 호스트로 요청하는 요소는 다시 IP 주소를 확인하지 않아 시간이 추

가로 걸리지 않는다. 그럼 하나의 호스트로 모든 리소스를 호출하면 시간을 줄일 수 있

10 http://www.stevesouders.com/blog/2008/03/20/roundup-on-parallel-connections/

Page 34: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

34 자바스크립트 성능 이야기

을 것 같지만, 대신 호스트별 동시 연결 기능은 포기해야 한다. 그러므로 이 부분을 최적

화하려면 여러 가지를 고려해야 한다. 야후!에서 제공한 “Best Practices for Speeding

Up Your Websites”의 “Reduce DNS Lookups” 문서11에 따르면 하나의 웹 페이지

를 기준으로 2~4개 정도의 호스트를 사용할 것을 제안하고 있다.

보통 이미지의 크기가 크고 개수가 많은 이미지 검색 서비스를 개발할 때 이런 부분을

많이 고려한다. 네이버 이미지 검색 서비스에서도 섬네일 이미지 관련 호스트는 2개를

사용한다(그림 1-9).

그림 1-9 네이버 이미지 검색 서비스

한 번에 보이는 이미지의 개수가 많을 때 호스트를 여러 개 만들어 동시에 다운로드하

면 성능을 높일 수 있다고 생각하지만 실제로는 고려할 사항이 많다. DNS Lookup 단

계에서 걸리는 시간도 무시할 수 없다. 또한 동시 연결 개수가 2개인 오래된 브라우저의

성능도 함께 고려해야 하기 때문에 여러 가지 가정을 세워서 테스트한 다음 최적의 호

스트 개수를 찾아야 한다.

11 http://developer.yahoo.com/performance/rules.html#dns_lookups

Page 35: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 35

TCP(Connect)

웹 서버와의 TCP 연결을 생성하는 데 필요한 시간이다. 프론트엔드에서는 이 단계에

걸리는 시간을 줄일 수 없다.

Request(Send)

웹 서버와 TCP 연결이 이뤄지면 원하는 정보를 서버에 보내는 시간이다. 주로 요청 헤

더 정보를 보내는 시간이 여기에 해당한다. Send 시간은 파일 올리기와 같이 보내야 할

정보가 많을수록 오래 걸린다. 파일을 올리면 브라우저에서는 해당 파일을 바이너리

형태로 변환해 데이터를 헤더 정보에 실어서 웹 서버로 보내는데, 파일 크기가 클수록

Send 시간이 오래 걸린다.

그렇지만 파일 업로드는 특수한 상황이라고 할 수 있다. 더 일반적인 상황에서 Send 시

간을 최적화하는 방법은 쿠키 정보를 포함하지 않는 별도 호스트를 사용해 서비스하는

것이다. 즉, 쿠키 정보가 필요 없는 파일(이미지, 스타일시트, 자바스크립트)에는 별도

호스트를 적용해 헤더 정보에서 쿠키 정보를 없애는 것이다. 이렇게 하면 보내는 헤더

의 크기가 줄어들기 때문에 Send 시간을 절약할 수 있다.

HttpWatch로 네이버 서비스에 있는 이미지의 요청 헤더를 확인하면 다음 그림과 같

이 쿠키 정보가 없는 걸 볼 수 있다. 이미지에 대해서는 www.naver.com 대신 static.

naver.net이라는 호스트를 사용하고 있기 때문이다.

그림 1-10 쿠키 정보가 없는 이미지의 헤더 정보

쿠키 정보를 줄이는 방법은 “2. 기본적인 웹 사이트 최적화 방법(41페이지)”에서 더 자

세히 설명한다.

Page 36: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

36 자바스크립트 성능 이야기

Wait

웹 서버에서 보내는 응답 메시지를 받기까지의 대기 시간이다. 서버 연산 처리 시간에

해당된다. 모든 요소에 대해서 Wait 시간이 전반적으로 느리다면 사용자가 많아 웹 서

버가 과부하 상태이거나 기타 다른 이유가 있을 수 있다. 그러나 특정 요소만 느리다

면 서버 측 연산 처리 시간을 튜닝해야 한다. AJAX로 개발할 때 응답이 느리다면 먼저

Wait 시간을 확인해 본다.

Response(Receive)

서버에서 보내는 응답 메시지를 받는 시간이다. 이 시간은 해당 요소의 데이터 크기에

비례하기 때문에 사용자의 네트워크 상태에 따라서 다를 수 있다. 이 단계에서 성능을

개선하는 방법으로는 데이터 크기를 줄이기 위해 Gzip과 같은 파일 압축 기술을 사용

하는 방법이 있다. 요즘에는 웹 페이지에 동적인 기능이 많아져 자바스크립트 파일의

크기가 늘어나고 있다. 자바스크립트 파일의 크기를 줄이는 방법은 보통 2단계를 거친

다. 첫 번째 단계에서는 주석이나 공백을 없애 코드의 양을 줄이고, 두 번째 단계에서는

코드의 양을 줄인 파일을 Gzip 등으로 압축해서 파일의 크기를 줄인다. Gzip으로 압축

하면 원본 파일의 약 30% 정도로 크기가 줄어든다. Gzip 압축으로 파일 크기를 줄이는

방법은 “2. 기본적인 웹 사이트 최적화 방법(41페이지)”에서 더 자세히 설명한다.

TTFB(Time To First Byte)

서버가 해당 요소의 첫 번째 바이트 정보를 브라우저에 보내는 데 걸린 시간이다.

Receive 시간의 첫 단계라고 말할 수 있다. 프론트엔드에서는 이 단계에 걸리는 시간을

줄일 수 없다.

Cache Read

사용자 PC에 저장돼 있는 캐시를 읽어 오는 시간이다. Blocked 단계와 Cache Read

단계는 네트워크 단계에 속하지는 않는다.

다음은 HttpWatch의 Time Chart로 캐시를 읽어오는 시간과 그렇지 않은 시간을 비

교한 결과다. 그림 1-11은 캐시 설정이 되어 브라우저에서 바로 읽어오는 경우고, 그림

1-12는 캐시 설정이 안 된 일반적인 파일을 서버에서 받는 경우다.

Page 37: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 37

그림 1-11 캐시 설정 적용된 리소스를 브라우저에서 읽어 오는 경우

그림 1-12 캐시 설정이 안 된 리소스를 서버에서 받는 경우

두 결과에서 볼 수 있듯이 캐시가 설정돼 있지 않으면 많은 단계를 거치게 된다. 이런 요

소들이 하나 둘 모여서 40~50개 정도 된다면 응답 속도는 계속 느려질 것이다.

브라우저 처리 단계

프로세싱 모델의 Processing과 onLoad에 해당하는 브라우저 처리 단계는 서버에서 받

은 HTML과 이미지, 스타일시트를 조합해 사용자가 실제로 보는 화면을 만드는 단계

다. 서버에 요청한 요소가 모두 도착하면 브라우저는 DOM을 생성하기 시작한다. 이렇

게 생성된 DOM을 조작하는 시점에 따라 응답 속도가 달라질 수 있다.

예전에 아주 간단한 기능만 구현하던 시절에는 거의 인라인(inline) 형태로 개발했다.

인라인 형태로 개발할 때는 이벤트를 HTML 요소의 속성으로 추가해서 함수를 바인

딩한다.

<html>

<head>

<title>자바스크립트 성능 이야기</title>

<script type="text/javascript">

Page 38: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

38 자바스크립트 성능 이야기

function() onloadMsg {

var sHelloString = "안녕하세요!!!";

alert(sHelloString);

}

</script>

</head>

<!-- 인라인 이벤트 등록 방식: 요소에 속성을 추가해 동작하는 함수를 바인딩함 -->

<!-- onload 이벤트가 발생하면 onloadMsg() 함수가 실행된다. -->

<body onload="onloadMsg();">

</body>

</html>

그러나 UI 개발이 복잡해지면서 인라인 형태의 이벤트 바인딩은 점점 한계를 드러냈

고 HTML과 의존성도 큰 문제였다. 그래서 복잡한 UI를 개발할 때는 인라인 형태의

이벤트 바인딩 방법보다 이벤트 핸들러로 바인딩하는 방법을 사용한다. 이벤트 핸들러

로 이벤트를 바인딩하려면 최우선 조건이 해당 DOM이 존재해야 한다는 것이다. 모든

DOM이 존재하는 바로 그 시점이 DOMContentLoaded12 이벤트나 onload 이벤트

가 발생하는 시점이다.

DOMContentLoaded 이벤트와 onload 이벤트는 모두 “이제 모든 준비가 완료됐으

니 마음대로 DOM을 조작해도 된다”라는 의미다. 그러나 다음 그림에서처럼 두 이벤

트는 발생 시점이 다르다.

onloaddomLoading domInteractive domContentLoaded

domComplete

그림 1-13 브라우저의 DOM 처리 절차에 따른 이벤트 발생 순서

12 DOMContentLoaded 이벤트에 대한 더 자세한 내용은 https://developer.mozilla.org/en-US/docs/DOM/DOM_event_reference

/DOMContentLoaded를 참조한다.

Page 39: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

1장 웹 사이트 최적화 39

onload 이벤트는 DOM에서 기본으로 제공하는 이벤트로 문서에 있는 모든 이미

지, 스타일시트, 자바스크립트 등이 모두 다운로드될 때마다 발생한다. 이와 달리

DOMContentLoaded 이벤트는 기본적으로 DOM 생성에만 관련돼 있다. 이미지나

다른 요소를 다운로드하는 것과 관계없이 DOM이 로딩되고 난 직후에 발생한다.

많은 양의 이벤트를 바인딩해야 하고 이미지나 스타일시트의 개수가 많은 페이지를 개

발한다면 onload 이벤트보다는 DOMContentLoaded 이벤트를 이용하는 게 좋다. 또

는 페이지를 로딩하고 실제 사용자가 동작할 때까지의 시간이 많이 걸려 문제가 됐다면

DOMContentLoaded 이벤트를 이용해 보기 바란다. 이전보다 체감 속도가 더 빨라질

것이다.

브라우저 처리와 DOM 조작에 관련된 최적화는 3~7장에서 더 자세히 설명하겠다.

요약

이번 장에서는 성능 최적화를 시작하기 전에 먼저 어떤 단계로 브라우저가 웹 페이지를

처리하는지 살펴봤다. 브라우저가 웹 페이지를 처리하는 단계와 각 단계마다 성능 향상

을 위해 무엇을 고려해야 하는지 정리하면 다음과 같다.

• 웹 페이지 성능을 개선하려면 먼저 브라우저가 웹 페이지를 어떻게 처리하는지 알아야 한다.

• 페이지를 떠나는 서비스 이동 단계에서 브라우저는 메모리 해제 작업을 실행한다. 그러나 브라우저

에 따라 메모리 해제를 담당하는 가비지 컬렉션 기능이 제대로 동작하지 않아 페이지가 느려질 수

있다. 서비스 이동 단계에서 일어나는 성능 문제를 개선하려면 필요 없는 변수나 객체를 삭제하고,

이벤트를 해제해 메모리를 관리해야 한다.

• 리다이렉트가 발생하면 어떤 파일도 다운로드하지 않고, 리다이렉트가 완료되기 전까지는 빈 화면

이 보인다. 되도록 리다이렉트를 사용하지 않는다. 리다이렉트를 해야 한다면 웹 페이지를 통한 리

다이렉트보다는 웹 서버 설정을 이용한 리다이렉트를 권장한다.

• 만료 날짜를 설정하는 등 애플리케이션 캐시를 올바르게 설정해 활용하면 브라우저와 서버 사이의

통신을 최대한으로 줄일 수 있다. 브라우저와 서버 사이의 통신이 줄면 웹 페이지를 방문했을 때 응

답 속도가 빨라지고 로딩 속도가 빨라져 사용자가 웹 페이지를 더 빨리 볼 수 있다.

Page 40: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

40 자바스크립트 성능 이야기

• 브라우저에는 여러 가지 처리 과정이 있지만 무엇보다 네트워크 통신 단계(DNS, TCP, Request,

Response)에 들어가는 비용이 가장 크다. 네트워크 통신 단계에 들어가는 비용을 줄여 성능을 높

이는 방법에는 여러 가지가 있다. 브라우저가 허용하는 호스트별 동시 연결 개수를 활용해 데이터

와 파일을 동시에 받는 방법이 있으며, 쿠키의 크기를 줄여 요청 헤더의 크기를 줄이는 방법도 있

다. 또한 스타일시트 파일과 자바스크립트 파일을 Gzip으로 압축해 전송받는 파일의 크기를 줄이

는 방법이 있으며, 이미 다운로드한 리소스를 캐시에 저장해 다시 사용하는 방법도 있다. 이런 방법

들은 모두 웹 사이트를 구성하는 요소 때문에 생기는 네트워크 비용을 줄여 성능을 향상시키는 방

법이다.

• 브라우저 처리 단계는 서버에서 받은 HTML과 이미지, 스타일시트를 조합해 사용자가 실제로 보

는 화면을 만드는 단계다. 이 단계에서 onload 이벤트보다 DOMContentLoaded 이벤트로

DOM을 조작하는 시점을 조절하면 웹 페이지의 응답 속도를 더 빠르게 할 수 있다.

하나의 웹 페이지가 나타날 때까지 브라우저 내부의 처리 과정은 상당히 복잡하다. 그

렇기 때문에 하나를 개선했다고 해서 속도가 갑자기 빨라지지는 않는다. 다만 브라우저

의 처리 과정을 이해함으로써 처음으로 성능 향상을 시도하는 개발자나 어디서부터 시

작해야 할지 몰랐던 개발자라면 성능을 향상시키려면 무엇을 고려해야 할지 대략적인

방향을 잡을 수 있을 것이다. 이제 다음 장에서는 기본적인 웹 사이트 최적화 방법을 알

아보겠다.

Page 41: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

이 장에서는 웹 사이트를 최적화할 수 있는 기본적인 방법을 소개한다.

특히 “어떻게 하면 사용자가 웹 사이트를 방문했을 때 웹 페이지가 빠르게 응답하게 할 것인가?”

에 초점을 맞춰 HTTP 요청 최소화, 파일 크기 최소화, 렌더링 성능 향상에 대해 알아본다.

HTTP 요청 최소화는 파일 개수를 줄여 네트워크 통신 과정에서 낭비되는 시간을 줄이는 방법

이다. 파일 크기 최소화는 서버와 주고받는 데이터의 양을 줄여 전송 속도를 향상시키는 방법

이다. 그리고 브라우저의 특성을 이용해 화면을 렌더링하는 시간을 줄여 사용자의 체감 속도를

높이는 방법도 있다.

이 장에서 소개하는 방법은 이미 많은 웹 사이트나 책을 통해 알려진 내용이다. 그렇지만 반드

시 알아 둬야 하는 기본적인 내용이라 새로운 최적화 방법을 살펴보기 전에 한 번쯤 정리한다

는 생각으로 읽어보기 바란다.

기본적인 웹 사이트최적화 방법

2

Page 42: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

42 자바스크립트 성능 이야기

HTTP 요청 최소화

HTTP 요청 최소화는 최적화에서 가장 기본이면서도 중요한 부분이다. 웹 사이트는 주

로 마크업, 이미지, 스타일시트, 자바스크립트 등으로 구성된다. 각 구성 요소는 모두 웹

서버에 있다. 이 구성 요소를 사용자의 컴퓨터로 가져오는 데는 네트워크 비용이 든다.

네트워크 비용은 곧 응답 시간으로 이어진다. 그러므로 다운로드해야 하는 구성 요소의

개수를 줄이는 것은 가장 효과가 크고 중요한 최적화 방법이다. 웹 페이지에서 다운로

드하는 구성 요소의 개수는 HttpWatch나 YSlow13로 알아볼 수 있다.

다음은 YSlow로 네이버me(http://me.naver.com) 서비스 페이지를 분석한 결과다.

왼쪽 분석 결과는 캐시가 없는 상태일 때를 분석한 결과고, 오른쪽 분석 결과는 캐시가

있는 상태일 때를 분석한 결과다. YSlow를 이용하면 이처럼 웹 페이지의 구성 요소의

개수와 크기를 쉽게 알 수 있다.

Statistics The page has a total of 63 HTTP requests and a total weight of 1347�4K bytes with empty cache

WEIGHT GRAPHS

Empty Cache Primed CacheHTTP Requests - 63

Total Weight - 1347.4K

HTTP Requests - 24

Total Weight - 139.1K

1 HTML/Text 54.8K 1 HTML/Text 54.8K

5 JavaScript File 529.9K 3 JavaScript File 0.1K

5 Style Sheet File 36.8K 1 Style Sheet File 0.0K

18 CSS Image 570.9K 18 Image 84.2K

1 Favicon 0.9K

33 Image 153.8K 1 Favicon 0.0K

그림 2-1 YSlow로 네이버me 서비스 페이지를 분석한 결과

캐시가 없는 상태에서는 총 63개의 구성 요소를 다운로드하고, 캐시가 있는 상태에서

는 24개의 구성 요소를 다운로드한다. 네이버me 서비스는 화면 구성이 미려하면서도

많은 서비스를 이어 주기 때문에 기능도 많다. 그리고 대부분의 기능이 자바스크립트를

이용해 동적으로 반응하도록 구현돼 있다. 이런 서비스를 어떻게 해서 63개의 구성 요

소만으로 구현해 HTTP 요청을 최소화할 수 있는지 살펴보겠다.

13 야후!에서 개발한 웹 페이지 성능 분석 도구다. YSlow는 야후!에서 정의한 규칙에 따라 등급을 나누어 최적화 결과와 최적화해야 하는 부분을

알려 준다. YSlow에 대한 더 자세한 내용은 http://yslow.org/를 참조한다.

Page 43: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 43

CSS 스프라이트 기법 활용

좀 더 미려한 페이지를 만들려면 어쩔 수 없이 이미지를 사용해야 한다. 순수한 HTML

마크업만으로는 표현에 한계가 있기 때문이다. 대신 이미지를 많이 사용하면 HTTP 요

청이 많아질 수밖에 없다. 이미지를 많이 사용하면서도 HTTP 요청을 최소화하는 방법

가운데 하나가 CSS 스프라이트 기법이다.

CSS 스프라이트 기법은 이미지 여러 개를 하나로 만들고 스타일시트에서 background-

position 속성을 설정해 필요한 부분의 이미지만 보여 주는 기술이다. 여러 이미지를 하나

의 이미지로 합치기 때문에 HTTP 요청 횟수를 줄일 수 있고, 이미지의 컬러 테이블과 같

은 메타데이터를 하나로 합칠 수 있어 파일 크기가 줄어든다.

하나로 합친 CSS 스프라이트 이미지를 사용할 때는 다음 예제와 같은 형식으로 스타일

시트를 작성한다.

33

/* CSS 스프라이트용 이미지를 설정한다. */

.menu_list li a{background:url(http://example.com/some.png) no-repeat}

/* background-position 속성의 왼쪽과 위쪽 기준을 설정해 보여 줄 이미지를 지정한다. */

.menu_list a.me{background-position:0 0} /* 첫 번째 아이콘 */

.menu_list a.mail{background-position:0 -52px} /* 52픽셀 아래에 있는 두 번째 아이콘 */

CSS 스프라이트 기법은 구글이나 야후! 등 많은 웹 서비스에서 활용하고 있다. 다음 이

미지는 구글에 적용된 CSS 스프라이트 이미지다.

그림 2-2 구글의 CSS 스프라이트 적용 사례

Page 44: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

44 자바스크립트 성능 이야기

네이버me에서도 CSS 스프라이트 기법을 활용하고 있다. 다음은 네이버me에서 사용

한 CSS 스프라이트 이미지다.

그림 2-3 네이버me의 CSS 스프라이트 이미지

CSS 스프라이트 기법은 이미지를 관리하기 어렵고 웹 접근성을 나쁘게 하는 요소가 있

다는 단점이 있지만 구성 요소를 다운로드하는 시간을 크게 줄여 준다. 만약, CSS 스프

라이트 기법을 사용하지 않았다면 그림 2-3의 이미지를 기준으로 60여 개의 이미지를

다운로드해야 한다. 이미지 하나를 받는 데 0.05초가 걸린다고 가정하면 60개의 이미

지를 받는 데 총 3초라는 시간이 걸린다.

인터넷 익스플로러 8 버전의 동시 커넥션 수를 활용해 6개의 병렬 호스트에서 동시에

이미지 6개를 다운로드한다고 해도 60개의 이미지를 다운로드하려면 0.5초라는 시간

이 걸린다. 그렇지만 CSS 스프라이트 기법을 활용하면 이미지를 1개만 다운로드하면

되기 때문에 단 0.05초만에 필요한 이미지를 다운로드한다.

Page 45: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 45

웹 사이트를 개발하고 최적화한 경험이 있는 개발자라면 알겠지만 클라이언트에서 단

0.1초를 줄이는 것도 매우 힘든 일이다. 그럼에도 0.2~1초 차이에 사용자의 체감 속도

가 달라지기 때문에 0.1초라도 줄여야 한다. 실제로 2008년에 네이버 메일 3.0을 개발

할 때는 디자인 및 마크업 부서와 협업해 CSS 스프라이트 기법으로 응답 속도를 2~3초

정도 빠르게 개선했다. 네이버 메일 3.0 개선에 대한 자세한 내용은 “9. 메일 3.0 성능 개

선 사례(271페이지)”를 참조하기 바란다.

CSS 스프라이트 제작 도구 N-MET

CSS 스프라이트 기법을 활용하기 위해 여러 이미지를 하나의 이미지 파일로 합치고, 각 이

미지에 해당하는 스타일시트를 작성하는 것이 쉬운 일은 아니다. NHN에서는 N-MET(NHN

Markup Enhancement Tool)이라는 도구로 비효율적이고 반복적인 마크업 개발 업무를

빠르게 처리하는데, CSS 스프라이트 이미지도 이 도구로 처리한다. N-MET의 CSS Sprites

Generator가 CSS 스프라이트 이미지를 처리하는 도구다.

CSS Sprites Generator를 활용하면 여러 개의 이미지를 하나의 CSS 스프라이트 이미지로

만들어 낼뿐더러 관련된 스타일시트 파일도 만들어 낸다. 또한 각 이미지의 좌푯값 정보를 저

장하기 때문에 CSS 스프라이트 이미지를 쉽게 수정하고 새로운 이미지를 추가할 수 있다.

Page 46: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

46 자바스크립트 성능 이야기

33

N-MET는 http://html.nhndesign.com/markup_tools/nmet에서 무료로 공개하고 있

으므로 누구나 다운로드해서 사용할 수 있다. N-MET과 CSS Sprites Generator에 대한 자

세한 내용과 사용 방법은 http://html.nhncorp.com/N-MET/guide_public/#csg 문서

를 참조한다.

헤더에 만료 날짜 추가

헤더에 만료 날짜를 추가하는 이유는 웹 페이지를 구성하는 이미지, 스타일시트 파일,

자바스크립트 파일 등을 사용자 컴퓨터의 캐시에 저장해서 재사용하기 위해서다. 사용

자가 처음 웹 페이지에 방문하면 만료 날짜가 설정된 요소를 사용자 컴퓨터에 저장한

다. 이후 같은 웹 페이지에 다시 방문하면 유효한(파일의 내용이 바뀌지 않고 사용 가능

한) 요소는 서버에 요청하지 않고 사용자 컴퓨터에서 바로 읽어 온다. 이로 인해 상당히

많은 HTTP 요청을 줄여 웹 페이지의 성능을 향상시킬 수 있다.

사용자 컴퓨터에 저장된 캐시 파일

만료 날짜 설정에 대해 좀 더

깊이 알기 위해 사용자 컴

퓨터에 저장된 파일을 살펴

보겠다. 따로 설정하지 않았

다면 C:�Documents and

Settings�{User}�Local

S e t t i n g s �Tempora r y

Internet Files 디렉터리에

캐시 데이터가 있다. 또는 인

터넷 익스플로러 8을 기준으

로 임시 인터넷 파일 및 열

어본 페이지 목록 설정1 4

14 메뉴에서 도구 > 인터넷 옵션을 선택한 다음 일반 탭의 검색 기록에서 설정 버튼을 클릭하면 대화 상자가 나타난다.

그림 2-4 사용자 컴퓨터에 캐싱된 파일 위치 확인

Page 47: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 47

대화 상자에서 파일 보기 버튼을 클릭하면 캐시에 저장된 요소를 살펴볼 수 있다.

다음 그림은 캐시에 저장된 요소의 예다.

그림 2-5 사용자 컴퓨터에 저장돼 있는 캐시 파일

캐시에 저장된 파일에는 만료 날짜가 없는 파일도 있고 만료 날짜가 있는 파일도 있다.

모든 구성 요소에 무조건 만료 날짜를 설정하는 것이 아니라 특정 기간까지 변경되지

않아도 서비스에 문제가 없는 요소에만 적용해야 한다. 주로 페이지 디자인을 위한 이

미지나 스타일시트 파일, 자바스크립트 파일 등이 여기에 해당한다.

만약, 만료 날짜 전에 수정 사항이 있어 파일을 변경해야 한다면 파일 이름을 변경하거

나 파일 이름 뒤에 쿼리스트링을 추가해 새로 추가된 파일임을 알려야 바로 반영된다.

예를 들어 some.js 파일에 수정 사항이 있고 바로 반영해야 한다면 다음과 같이 파일

이름이나 쿼리스트링을 변경한다.

3

//방법1: 파일 이름을 변경한다.

<script type="text/javascript" src="some_20120622.js"></script>

//방법2: 쿼리스트링을 추가한다.

<script type="text/javascript" src="some.js?20120622"></script>

브라우저에서 캐싱된 파일을 이용할지 서버에 요청할지 판단하는 기준은 그림 2-5의

칼럼 이름을 기준으로 이름과 인터넷 주소다. 그렇기 때문에 위와 같이 작업해서 파일

이름이나 파일의 주소를 바꾸지 않으면 계속 같은 파일로 인식하고 사용자 컴퓨터에 있

는 파일을 로딩한다.

Page 48: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

48 자바스크립트 성능 이야기

만료 날짜 설정과 확인 방법

만료 날짜 설정은 웹 서버에서 처리하는 내용이기 때문에 간략하게 설명하겠다. 아파치

웹 서버에서 만료 날짜를 설정하려면 mod_expires 모듈15이 필요하다. mod_expires

모듈을 설치한 다음 httpd.conf 파일을 수정해 만료 날짜를 설정한다. httpd.conf 파일

의 ExpiresActive 키워드로 만료 날짜 설정을 활성화하거나 비활성화한다. 특정한 파

일 형식에만 만료 날짜를 부여하려면 ExpiresByType 키워드를 활용한다.

만료 날짜가 올바르게 설정됐는지는 HttpWatch로 확인할 수 있다. HttpWatch를 사

용하면 각 요소의 헤더 정보를 보여 준다. 다음 그림은 네이버 모바일(http://m.naver.

com/) 페이지에 있는 이미지 파일의 헤더 정보를 HttpWatch로 확인한 내용이다.

그림 2-6 네이버 모바일 메인 페이지에서 사용하는 이미지의 헤더 정보

만료 날짜가 1년 후(2013년 6월 25일)로 설정돼 있다. 즉 사용자가 캐시 파일을 지우기

전까지는 1년 동안 계속 캐시 파일을 읽는다.

만료 날짜를 설정하면 어떤 효과를 볼 수 있는지 YSlow로 확인해 보자. 다음 그림은

네이버 모바일 페이지를 YSlow로 검사한 결과다. 캐시가 없는 첫 방문에서는 24개의

HTTP 요청을 처리해야 하는데, 캐시가 있는 다음 방문에서는 HTTP 요청을 3개만 처

리했다.

15 mod_expires 모듈과 사용법에 대한 자세한 내용은 http://httpd.apache.org/docs/2.2/mod/mod_expires.html을 참조한다.

Page 49: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 49

Statistics The page has a total of 24 HTTP requests and a total weight of 237.8K bytes with empty cache

WEIGHT GRAPHS

Empty Cache Primed CacheHTTP Requests - 24

Total Weight - 237.8K

HTTP Requests - 3

Total Weight - 46.0K

1 HTML/Text 45.9K 1 HTML/Text 45.9K

2 JavaScript File 5.0K 1 Image 0.04K

2 Style Sheet File 16.4K 1 Favicon 0.0K

6 CSS Image 68.1K

1 Favicon 0.2K

12 Image 101.9K

그림 2-7 YSlow로 측정한 모바일 네이버 메인 페이지의 통계 데이터

스타일시트 파일, 자바스크립트 파일, 심지어 일정 간격으로 변하는 콘텐츠에 포함된

이미지에도 만료 날짜를 설정해 얻은 성능 개선 결과다. 모바일 환경의 네트워크 상태

는 PC 환경보다 불안하기 때문에 더 중요한 성능 개선 요소라고 할 수 있다.

자바스크립트 파일 통합

시간이 지남에 따라 웹 서비스의 기능은 날로 향상되고 있다. 기능이 향상됨에 따라 자

바스크립트 파일은 개수도 많아지고 크기도 커지고 있다. 특히 네이버에서 서비스하는

메일, 캘린더, N드라이브, 포토앨범과 같은 도구형 서비스는 모든 UI를 자바스크립트

로 구현하기 때문에 성능 향상에 어려움이 많다. 이럴 때 성능을 높이는 방법은 여러 개

의 자바스크립트 파일을 하나의 파일로 합쳐 파일 개수를 최소화하는 것이다.

2008년 네이버 메일 3.0을 개발할 때 자바스크립트 파일의 개수는 무려 60개 정도였

고, 캐시를 비운 상태에서 자바스크립트 파일을 로딩하는 데만 3초 이상의 시간이 걸렸

다. 이 파일을 모두 합치고 최소화해 단 0.15초 만에 로딩하도록 성능을 개선했다.

네이버 메일 3.0의 사례에서 보듯이 웹 사이트의 성능을 개선할 때는 파일의 용량보다

파일의 개수가 더 중요하다. 아주 용량이 작은 파일라도 원격 서버에서 가져와야 한다

면 네트워크 비용이 든다. 파일이 캐시에 있더라도 해당 파일이 유효한지 판단해야 한

다. 또한 병렬로 다운로드하는 데 한계가 있기 때문에 파일의 개수가 늘어나는 것은 성

능에는 치명적이다.

Page 50: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

50 자바스크립트 성능 이야기

다시 강조하지만 웹 페이지의 성능을 높이는 제일 좋은 방법은 파일의 개수를 줄여

HTTP 요청을 최소화하는 것이다.

파일 크기 최소화

파일의 개수를 줄여 HTTP 요청을 최소화했다면 웹 페이지 구성 요소의 크기를 어떻게

줄일지 알아보자.

Gzip 압축을 이용한 파일 크기 최소화

점점 커지는 자바스크립트 파일과 스타일시트 파일의 크기를 줄이는 가장 효과적이고

쉬운 방법은 파일을 압축하는 것이다. 아파치 웹 서버에서 파일을 압축하는 대표적인

인코딩 방식에는 Gzip과 deflate의 두 가지가 있는데, deflate 방식은 지원하지 않는 브

라우저가 많고 효과도 떨어지기 때문에 대부분 Gzip 방식을 사용한다. Gzip 압축은 웹

서버에서 설정하는 방법16이라 여기서는 Gzip으로 압축한 파일이 어떻게 전달되고,

Gzip으로 압축했을 때 어떤 효과가 있는지만 살펴보겠다.

압축 전송 흐름 및 확인

압축 전송은 다음과 같은 순서로 진행된다. 클라이언트에서 some.js라는 파일을 만났을

때를 가정해 보자.

� 클라이언트에서 헤더 정보로 인코딩 여부를 물어본다.

Accept-Encoding: gzip, deflate

16 아파치 웹 서버에서 Gzip 압축 전송을 설정하는 방법은 http://httpd.apache.org/docs/2.0/mod/mod_deflate.html 문서를 참조한다. Gzip

압축 전송을 적용할 파일 형식을 지정할 수 있고 대상 브라우저를 선택할 수 있다.

Page 51: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 51

� 서버에서 헤더 정보로 인코딩 여부를 알려준다. 인코딩된 요소라면 다음과 같이 응답이

온다.

Content-Encoding: gzip

� 클라이언트에서 인코딩된 요소를 받음과 동시에 압축을 해제한다. 이때 추가적인 CPU

연산 비용이 들어간다. 여기서 발생하는 CPU 연산 비용도 무시할 수 없기 때문에 압축

할 파일과 크기를 잘 설정해야 한다. 보통 이미지 파일은 이미 압축돼 있기 때문에 압축

하지 않고, 스타일시트 파일과 자바스크립트 파일을 압축한다. 그리고 파일 크기가 작

으면 속도 개선 효과보다 CPU 연산 비용이 더 들기 때문에 파일 크기가 일정한 크기 이

상인 경우에만 압축하는 것이 좋다. 스티브 사우더스의 “웹 사이트 최적화 기법(2008,

ITC)”에 의하면 파일 크기가 1~2KB 이상일 때 압축할 것을 권장한다.

압축 전송의 효과

Gzip으로 압축해 전송하면 평균 70% 정도 파일 크기가 작아지는 효과를 볼 수 있다.

모바일 환경과 같이 네트워크 환경이 불안한 상황에서는 더욱 효과적인 기술일 것이다.

NHN의 자바스크립트 라이브러리인 Jindo 프레임워크(Jindo 2.1.0)로 간단한 테스트

를 했다17. 테스트 결과를 봤을 때 자바스크립트 파일의 최소화와 Gzip 압축을 함께 적

용하면 상당히 많은 크기를 줄일 수 있을 것이다18.

표 2-1 Jindo 프레임워크의 Gzip 압축 테스트 결과

원본 497�47KB

최소화(공백 및 주석 제거) 170.03KB(65.82% 감소)

Gzip 41.86KB(62.13% 감소)

17 테스트에 사용한 도구는 Closure Compiler(http://closure-compiler.appspot.com/home)이고, 테스트 옵션은 Whitespace Only로 설정했다.

18 압축 전송의 성능은 브라우저별로 다를 수 있다. http://www.vervestudios.co/projects/compression-tests/results에서는 브라우저별로 압축

전송을 테스트한 내용을 볼 수 있다.

Page 52: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

52 자바스크립트 성능 이야기

쿠키 크기 최소화

포털 사이트에서는 일반적인 웹 사이트에서보다 쿠키의 크기가 더 크다. 공통으로 사용

하는 로그인 정보도 있고 개별 서비스가 전체 사이트와 공유하는 정보가 많아진다. 이

때 필요한 정보를 저장하는 가장 손쉬운 방법이 최상위 도메인을 이용해 쿠키를 설정하

는 것이기 때문이다. 이렇게 되면 자바스크립트 파일이나 스타일시트 파일, 이미지 등

쿠키 정보가 필요 없는 구성 요소를 요청할 때도 헤더 정보에 쿠키가 포함된다. 즉, 헤더

를 전송할 때 데이터 크기가 커진다.

사실 쿠키의 크기는 성능 관점에서 우선순위가 낮은 편이지만 쿠키를 지속적으로 관리

하지 않으면 성능에 영향을 미칠 수 있다. 쿠키의 크기를 줄이는 기본적인 방법은 다음

과 같다.

• 지속적인 관리로, 사용하지 않는 쿠키는 삭제한다.

• 쿠키를 설정할 때 최상위 도메인은 되도록 사용하지 않는다.

• 쿠키의 만료 날짜를 최대한 짧게(사용할 만큼만) 설정한다.

• 쿠키 정보가 필요 없는 이미지, 스타일시트, 자바스크립트 파일은 별도의 도메인으로 서비스한다

(예: *.naver.com이 아니라 *.naver.net으로 서비스). 최상위 도메인이 같지 않으면 쿠키 정보

는 공유되지 않는 점을 활용한 방법이다.

다음 그림은 네이버 오픈캐스트에 있는 섬네일 이미지의 헤더 정보를 분석한 내용이다.

이미지를 요청한 사이트의 최상위 도메인(*.naver.com)과 섬네일을 제공하는 사이트

의 최상위 도메인(*.naver.net)이 서로 다르다. 따라서 쿠키 정보가 없다.

그림 2-8 쿠키 정보가 없는 헤더 정보

Page 53: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 53

최적화가 비교적 잘돼 있는 사이트에서 속도를 줄이기란 쉬운 일이 아니다. 쿠키 크기

를 줄이는 것이 사소하게 느껴지지만 요청 헤더 크기가 하나 둘 커지다 보면 전체적으

로 느려질 수 있다. 그렇기 때문에 쿠키 크기도 관심을 두고 관리해야 한다.

렌더링 성능 향상

전체적인 로딩 속도는 동일한데 빈 페이지가 계속 보이다 갑자기 콘텐츠가 나타나는 페

이지가 있고, 처음부터 콘텐츠가 조금씩 보이며 화면이 빠르게 나타나는 페이지가 있

다. 콘텐츠가 조금씩 보이는 화면이 더 빠르게 느껴지는 것은 체감 속도 때문이다. 렌더

링 성능 향상의 목표는 페이지를 요청했을 때 사용자가 대기하는 시간을 최대한 줄여서

이 체감 속도를 높이는 것이다.

자세한 내용을 알아보기 전에 브라우저가 어떤 순서로 마크업을 파싱해서 화면에 보여

주는지 기본적인 흐름을 살펴보자.

1� HTML 파싱과 DOM 트리 구성

사용자가 페이지를 요청하면 네트워크를 통해 마크업을 받아 온다. 그러고 나서 마크업 문자열을

토큰 형태로 잘라서(Tokenizer) 트리를 구축하고 파싱 작업을 시작한다. 그런 다음 DOM 트리

(DOM Tree)19를 생성한다.

2� 렌더 트리 구성(DOM+스타일 규칙)

DOM 트리를 생성한 다음 바로 화면을 그리지는 않는다. 스타일시트의 정보를 적용해야 하기 때

문이다. DOM 트리 정보와 스타일시트의 스타일 규칙을 결합해 렌더 트리(Render Tree)를 만든

다. display:none 속성처럼 DOM 트리에는 있지만 화면에 보이면 안 되는 요소를 걸러낸 결과

가 렌더 트리다.

3. 렌더 트리의 배치

최종적으로 스타일 규칙에 따라 각 요소를 화면의 어디에 배치할지 좌표를 설정한다.

4. 렌더 트리 그리기

요소의 좌표가 설정되면 브라우저에 순차적으로 화면을 그린다. 이때 사용자는 화면을 조금씩 보

게 된다.

19 DOM 트리에 대한 자세한 내용은 http://www.w3schools.com/htmldom/dom_nodetree.asp를 참조한다.

Page 54: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

54 자바스크립트 성능 이야기

기본적으로 위와 같은 단계를 거치는데 실수로 또는 잘 몰라서 이 흐름을 방해하는 방

법으로 페이지를 개발하면 성능에 영향을 주게 된다. 어떻게 하면 성능에 영향을 주지

않고 더 빠르게 화면을 보여줘 쾌적한 서비스를 제공할 수 있는지 알아보겠다20.

스타일시트와 자바스크립트 배치를 이용한 성능 향상

스타일시트 파일은 페이지 제일 위쪽에 놓고 자바스크립트 파일은 페이지 맨 아래쪽에

놓아야 한다.

브라우저 렌더링 단계에 따르면 사용자에게 화면을 보여 주기 전에 렌더 트리를 생성해

야 하는데, 이때 스타일시트 파일이 반드시 필요하다. 스타일시트 파일을 최대한 빨리

다운로드해야 하는 이유다. 그리고 파이어폭스나 인터넷 익스플로러는 스타일시트 파

일을 모두 다운로드할 때까지 화면을 렌더링하지 않고 기다린다. 그래서 스타일시트 파

일은 페이지의 제일 위쪽인 <head> 태그와 </head> 태그 사이에 놓아서 최대한 빨리

다운로드해야 한다.

자바스크립트 파일을 페이지 아래에 놓아야 하는 가장 큰 이유는 파일을 다운로드해서

실행하기 전까지 브라우저가 DOM 파싱도 중지하고 아무것도 렌더링하지 않기 때문이

다. 자바스크립트에는 document.write() 메서드가 있어 마크업을 렌더링하는 도중에도

DOM을 추가할 수 있다. 이로 인해 이미 서버 통신을 완료하고 필요한 구성 요소를 모두

브라우저에 가져왔음에도 자바스크립트를 수행하느라 렌더링이 멈추게 된다. 이때 사용

자에게는 마치 화면이 멈춘 것처럼 보여 체감 속도가 느려진다.

따라서 자바스크립트 파일은 </body> 태그 바로 위에 놓는 것이 좋다.

초기 렌더링 시 AJAX 요청 최소화

네이버 캘린더나 메일, N드라이브와 같은 도구형 서비스는 화면 전체를 자바스크립트

(AJAX 통신)로 그리고 컨트롤한다. 동적인 웹 사이트에서 화면을 그리는 단계는 일반

적으로 다음과 같다.

20 렌더링 성능을 향상시키는 방법은 “5. UI 스레드와 타이머의 활용(127페이지)”과 “6. DOM 스크립팅(153페이지)”에서 더 자세히 설명한다.

Page 55: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 55

1. 사용자가 페이지를 요청한다.

2. 서버에서 보낸 마크업을 다운로드해 렌더링을 시작한다. 이때 마크업은 화면을 구성하는 레이아

웃만 있고 실제로 보여 줄 데이터는 나중에 AJAX 요청을 통해 받은 다음 그릴 것이다.

3. 자바스크립트 다운로드와 렌더링이 끝난 후 onload 이벤트가 발생한다.

4. onload 이벤트가 발생한 다음에야 AJAX 통신을 실행하고 데이터를 화면에 그린다.

5. 화면을 완성한다.

이 과정에는 두 가지 큰 문제점이 있다. 원래 AJAX 통신을 사용하지 않는 방법으로 페

이지를 개발했다면 3번 단계에서 사용자는 화면을 보게 된다. 그런데 5번 단계까지 가

서야 사용자는 최종 화면을 볼 수 있다. 또 다른 문제는 렌더링이 반복된다는 것이다.

1~3번 단계까지 전체 화면을 한 번 그리고 4~5번 단계에서 화면을 한 번 더 그린다.

네이버 메일 3.0 같은 서비스는 사용자가 많을뿐더러 사용자가 하루에도 몇 번씩이나

방문한다. 그렇기 때문에 무엇보다 초기 응답 속도가 중요하다. 초기에 AJAX 통신 방

법을 사용했지만 AJAX 통신을 하지 않고 JSON 형태로 필요한 데이터를 받아 그려 주

는 방식으로 변경했다. 이렇게 해도 통신 비용만 덜었지 여전히 앞에서 말한 문제가 있

었다.

결국 최종적으로 초기 렌더링 시에 마크업 전체를 서버에서 보내는 방식으로 개발해 체

감 속도를 높였다. 다시 말하면, 1~3단계에서 전체 화면과 데이터가 있는 화면을 모두

그리는 것이다. 그리고 사용자의 행동이 있을 때 AJAX 요청을 실행해서 데이터를 받은

다음 화면을 그리게 했다.

초기 렌더링 시에 AJAX 통신으로 받은 데이터를 화면에 그리는 방법은 화면을 두 번

그리게 되어 체감 속도를 매우 느리게 한다. 체감 속도를 높이려면 되도록 초기 렌더링

시에는 AJAX 요청을 최소화한다.

마크업 최적화

마크업을 최적화해 사용자가 실제 느끼는 체감 속도를 빠르게 하는 방법을 소개하겠다.

마크업 최적화의 목표는 빈 페이지가 한참 있다가 전체 화면이 한꺼번에 나타나는 것이

Page 56: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

56 자바스크립트 성능 이야기

아니라 영역별로 차츰 렌더링하게 하는 것이다. 끊김 없는 처리를 위한 여러 가지 방법

이 있지만 여기서는 <table> 태그 처리에 대한 특성을 살펴보겠다.

DHTML21 기술이 발표된 이후에는 페이지 전체의 레이아웃을 <table> 태그로 구성

하는 방법을 사용하지 않게 됐다. 그러나 어쩔 수 없이 <table> 태그를 사용해야 하는

경우도 있고 기존에 만들어 둔 페이지를 개편하지 못한 경우도 있다.

인터넷 익스플로러에서는 <table> 태그를 렌더링할 때 표 안에 있는 텍스트와 이미지

등을 모두 파싱할 때까지 화면 표를 그리지 않는다. 만약 <body> 태그부터 시작해서 전

체 화면을 그리는 데 <table> 태그를 사용했다면, 한참 있다가 전체 화면이 한번에 보일

것이다. 반면에 파이어폭스에서는 <table> 태그가 모두 완료되기 전에도 표 안에 있는

각 요소가 보이기 때문에 부분적인 렌더링이 가능하다.

여기서는 특정 브라우저보다는 모든 브라우저에 대응할 수 있는 렌더링 방법을 정리하

겠다.

� HTML 코드가 올바른지 검사한다. http://validator.w3.org/ 사이트를 이용하면 손쉽

게 검사할 수 있다.

� 태그의 중첩을 최소화한다. 다음과 같은 화면을 그리려면 태그가 어느 정도나 중첩됐을

까? 외곽 테두리와 알림 문구, 이미지 태그 정도일 것이라 예상할 수 있지만 실제로는 7

개 정도가 필요하다. 각 테두리에 그라데이션 효과를 주기 위해 추가로 중첩된 마크업이

필요하다. 이런 디자인은 디자이너와 협의해 속도를 위해서 좋은 방법을 찾아야 한다.

다음에 나오는 코드를 브라우저 주소창에 입력하면 해당 페이지의 전체 태그 개수를 알

수 있다.

21 DHTML에 대한 자세한 설명은 http://ko.wikipedia.org/wiki/DHTML을 참조한다.

Page 57: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

2장 기본적인 웹 사이트 최적화 방법 57

javascript:alert(document.getElementsByTagName("*").length);void(0);

전체 태그의 개수(보통 1000개 이하를 권장)를 줄이는 것도 중요하지만 중첩된 태그를

최소로 하는 것이 더 중요하다. 전체적으로는 태그의 개수를 줄이고 부분적으로는 중첩

된 태그를 최소화해 간결하게 디자인하는 것이 렌더링 속도를 높이는 방법이다.

� 본문 전체를 <table> 태그로 감싸지 않는다.

� 되도록 <div> 태그와 스타일시트를 이용해 레이아웃을 구성한다.

� 이미지 크기가 너무 크지 않게 한다.

NHN의 마크업 복잡도 체크 도구 - Markup Complexity Checker

NHN에서는 마크업의 복잡도를 체크하는 도구인 Markup Complexity Checker를 개발

해 최적화에 활용하고 있다. Markup Complexity Checker은 http://html.nhndesign.

com/markup_tools/complexity에 공개돼 있어 누구나 이용할 수 있다. 복잡도를 체크할

웹 페이지의 URL을 입력하거나 마크업 코드를 직접 입력하면 요소의 개수, 평균 중첩도 등을

확인해서 보여 준다.

Page 58: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

58 자바스크립트 성능 이야기

요약

이 장에서 소개한 방법은 쉽게 적용할 수 있지만 효과가 크다. 아주 기본적인 방법이므

로 웹 사이트를 최적화할 때 무엇보다 먼저 검토해야 한다. 어떤 순서로 검토하면 좋은

지 간단하게 다음과 같이 정리했다.

1. HTTP 요청을 최소화할 수 있는지 분석한다.

a. HttpWatch와 같은 도구의 워터폴(Waterfall) 차트22로 웹 페이지를 구성하는 요소에 무엇

이 있는지 분석한다.

b. 이미지 요청 수를 분석하고 요청 수가 너무 많으면 CSS 스프라이트를 적용할 수 있는지 검토한

다.

c. 스타일시트 파일이나 자바스크립트 파일의 개수를 분석하고 파일의 개수가 많다면 되도록 하나

의 파일로 통합한다.

d. 불필요한 '404' 상태 코드가 나타나는 요소를 찾는다.

e. HTTP 요청에 대한 최적화 작업이 완료됐다면 재사용 방문자를 위해 캐시 설정을 진행한다.

2. 구성 요소의 크기를 줄인다.

a. 자바스크립트 파일이나 스타일시트 파일에서 불필요한 공백이나 주석 등을 제거해 파일 크기를

최소화한다.

b. Gzip 압축을 적용하면 파일의 크기를 더 줄일 수 있다.

3. 렌더링 향상을 위해서 아래와 같은 방법을 적용해 본다.

a. 스타일시트 파일은 페이지 제일 위에 놓고, 자바스크립트 파일은 페이지 제일 아래에 놓는다.

b. 페이지 초기 렌더링 시에는 AJAX 요청을 되도록 하지 않는다.

c. 한 번에 페이지를 모두 보여 주기보다는 점진적으로 보여 주는 방법을 적용한다.

다음 장부터는 자바스크립트로 웹 페이지를 개발할 때 적용할 수 있는 더 다양한 방법

을 설명하겠다.

22 HttpWatch의 워터폴 차트에 대한 자세한 설명은 http://www.httpwatch.com/features.htm#timecharts를 참조한다.

Page 59: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

이 장에서는 자바스크립트의 기본 요소인 반복문과 조건문, 문자열 연산과 함께 배열과 객체의

생성과 초기화, 문자열 연산, 정규 표현식, 변수 탐색 등을 어떻게 작성하면 자바스크립트의 실

행 성능을 높일 수 있는지 알아본다.

평소에 성능을 생각하지 않고 작성하던 코드나 “이렇게 하면 좀 더 좋은 성능을 내겠지?”라며

객관적인 근거 없이 작성한 코드 스타일을 살펴보고, 같은 구문에서 최적화된 성능을 낼 수 있

는 코드 스타일을 찾아본다. 또한 NHN에서 개발한 성능 측정 도구인 jsMatch로 성능을 측정해

보겠다. 같은 기능을 실행하는 다양한 코드의 성능을 비교해 어떻게 작성할 때 성능이 더 좋은

지 살펴본다.

이 장에서 나열한 예제는 자바스크립트를 조금이라도 경험한 사람이라면 쉽게 이해할 수 있는

아주 기초적인 코드지만, 이런 기초적인 코드를 어떻게 작성하느냐에 따라서도 자바스크립트

의 실행 성능에 큰 차이가 날 수도 있다. 이후 개발을 하면서 성능 향상 문제를 해결하려 다시

이 장에 언급된 성능 개선 요소를 보며 하나하나 적용하기보다 먼저 이 장에서 제시하는 최적

화된 코드의 형태로 코딩하는 습관을 몸에 익혀 놓는 것이 성능 개선에 좀 더 효과적인 방법일

것이다. 물론 경우에 따라 직접 성능을 측정하며 최적화해야만 하는 요소도 있다는 점은 알아

둬야 한다.

성능을 높이는 코드 스타일

3

Page 60: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

60 자바스크립트 성능 이야기

객체의 생성, 초기화 성능

많이 사용하지만 성능을 생각하지 않고 작성하는 코드에 객체 선언과 초기화 구문이 있

다. 배열(Array) 형식의 객체와 오브젝트(Object) 형식의 객체를 생성하고 초기화하는

방법의 성능을 측정해 보고 두 코드 사이에 어떤 차이점이 있는지 비교해 보겠다.

배열의 생성, 초기화 성능 비교

배열은 생성자 혹은 리터럴 형식([ ])을 사용해 객체를 생성할 수 있다. 다음 코드를 살

펴보자.

예제 3-1 Code #1 - 생성자를 사용한 배열 생성

// Array() 생성자를 사용한 배열 생성

var arr = new Array();

예제 3-2 Code #2 - 리터럴 형식으로 배열 생성

// 리터럴 형식으로 배열 생성

var arr = [];

이 두 방법의 성능을 jsMatch에서 비교한 결과는 다음과 같다23. 결과에서 알 수 있듯

이 객체 생성 방법에 따른 큰 차이는 없으나 리터럴 형식을 사용한 경우(Code #2)에 여

러 브라우저에서 좀 더 좋은 성능을 보인다.

그림 3-1 생성자와 리터럴을 이용한 배열 객체 생성 성능 비교24

23 그래프의 각주에 있는 URL은 jsMatch에서 비교 결과를 저장해 공유하는 주소다. 해당 URL로 접근하면 비교 결과에 대한 더 많은 정보를 확인

할 수 있다.

24 http://jindo.dev.naver.com/jsMatch/index.html?d=11&openResult=1

Page 61: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 61

배열을 사용하려면 배열의 각 요소에 데이터를 할당해 초기화해야 한다.

배열의 각 요소에 데이터를 할당하는 방법에도 여러 가지가 있다. 그 가운데 가장 많이 볼

수 있는 방법인 접근자 [ ]를 사용하는 방법과 push() 메서드를 사용하는 방법의 성능을 비

교했다. 성능 비교에서는 배열의 생성 방법이 성능에 영향을 미치지 않도록 모두 리터럴

방식으로 배열 객체를 생성했다.

예제 3-3 Code #1 - 접근자를 사용한 데이터 할당

3

var arr = [];

for (var i = 0; i < 1000; i++) {

arr[i] = i;

}

예제 3-4 Code #2 - push() 메서드를 사용한 데이터 할당

3

var arr = [];

for (var i = 0; i < 1000; i++) {

arr.push(i);

}

결과를 보면 크롬을 제외한 대부분의 브라우저에서 push() 메서드를 사용해 데이터를 할

당하는 방법보다 접근자 [ ]를 사용해 데이터를 할당하는 코드의 실행 속도가 2배 정도

더 빠르다. 접근자를 사용한 코드(Code #1)가 push() 메서드를 사용한 코드(Code #2)보

다 성능이 더 좋다는 의미다.

그림 3-2 push() 메서드와 접근자 []를 이용한 배열 객체 초기화 성능 비교25

25 http://jindo.dev.naver.com/jsMatch/index.html?d=13&openResult=1

Page 62: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

62 자바스크립트 성능 이야기

배열의 생성과 초기화 방법을 비교한 결과, 배열을 사용할 때는 리터럴 형식으로 객체

를 생성하고 Array.push() 메서드보다는 접근자 [ ]를 사용해 데이터를 추가하는 코드

를 작성하는 것이 좀 더 최적화된 배열 사용법이라는 사실을 확인할 수 있었다.

오브젝트(Object) 객체의 생성, 초기화 성능 비교

오브젝트(Object) 객체도 배열처럼 객체를 생성하고 초기화하는 다양한 방법이 있다.

여기서는 가장 많이 사용하는 방법인 리터럴({ })을 사용하는 방법과 생성자를 사용하는

방법의 성능을 테스트해 보고 어떤 방법으로 객체를 생성하고 초기화하는 것이 효과적

인지 살펴보겠다.

먼저 객체 생성 방법을 비교해 성능을 테스트해 보자. 다음 예제는 리터럴({ })을 사용해

객체를 생성하는 예제와 Obeject() 생성자로 객체를 생성하는 예제다.

예제 3-5 Code #1 - 리터럴을 사용한 오브젝트 객체 생성

3// 리터럴을 사용한 오브젝트 객체 생성

var obj = {};

예제 3-6 Code #2 - 생성자를 사용한 오브젝트 객체 생성

3// 생성자를 사용한 오브젝트 객체 생성

var obj = new Object();

다음의 성능 비교 결과를 보면 배열과는 달리 객체를 생성하기 위해 생성자 또는 리터

럴({ }) 가운데 어떤 방법의 성능이 월등히 좋다고 판가름하기 힘들다. 오브젝트 객체의

생성 방법 가운데 반드시 어떤 방식을 사용해야만 하는 이유가 없다면 리터럴 형식이

코드 크기를 좀 더 줄일 수 있는 방법이기 때문에 코드를 다운로드하는 시간 관점에서

성능에 더 좋다고 볼 수는 있다. 하지만 이와 같이 성능 차이가 거의 없는 경우에는 성능

보다는 개발과 유지 보수, 가독성까지 고려해서 코드 작성 방식을 선택하는 것이 올바

른 최적화 방법일 것이다.

Page 63: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 63

그림 3-3 생성자와 리터럴을 이용한 오브젝트 객체 생성 성능 비교26

오브젝트 객체를 초기화할 때는 다음에 제시하는 두 가지 방법으로 오브젝트 객체에 새

로운 데이터를 삽입할 수 있다.

예제 3-7 Code #1 – . 연산자를 이용한 데이터 삽입

3

var obj = {};

obj.a = 1;

obj.b = 2;

obj.c = 3;

obj.d = 4;

obj.e = 5;

obj.f = 6;

obj.g = 7;

obj.h = 8;

obj.i = 9;

obj.j = 10;

예제 3-8 Code #2 – [ ] 연산자를 이용한 데이터 삽입

var obj = {};

obj["a"] = 1;

obj["b"] = 2;

26 http://jindo.dev.naver.com/jsMatch/index.html?d=14&openResult=1

Page 64: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

64 자바스크립트 성능 이야기

obj["c"] = 3;

obj["d"] = 4;

obj["e"] = 5;

obj["f"] = 6;

obj["g"] = 7;

obj["h"] = 8;

obj["i"] = 9;

obj["j"] = 10;

오브젝트 객체에 데이터를 삽입하는 초기화 방법에 대한 성능 테스트 결과도 오브젝트

객체 생성에 대한 테스트 결과와 유사했다. 객체의 초기화도 생성과 마찬가지로 한 가

지 방식이 더 성능이 좋다고 판단할 수 없으며, 작성하는 코드의 크기와 주요 대상 브라

우저 및 코드의 가독성과 유지 보수를 감안해 적절한 방식을 택하면 되겠다.

그림 3-4 연산자와 [] 연산자를 이용한 오브젝트 객체 초기화 성능 비교27

배열에서와는 다르게 오브젝트 객체의 성능 테스트에서 도출된 결론은 ‘어느 한 쪽의

코드가 더 성능이 좋다고 말할 수 없다’다. 하지만 잘못된 정보를 고집하거나 어느 방식

이 더 나을지 고민하는 데 시간을 낭비하지 않게 테스트 결과를 공유했다.

스코프 체인 탐색과 성능

자바스크립트 성능을 다루는 책에서 항상 빠지지 않는 부분이 스코프 체인(Scope

Chain)이다. 인터프리터 언어로서 자바스크립트는 JIT(Just-In-Time) 컴파일러 도입

27 http://jindo.dev.naver.com/jsMatch/index.html?d=16&openResult=1

Page 65: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 65

등 자바스크립트의 실행을 최적화하기 위한 여러 가지 방법을 도입하고 있지만 개발자

가 작성한 코드 자체의 성능이 런타임 성능에도 많은 영향을 준다. 그렇기 때문에 코드

자체에서 성능 저해 요인을 최대한 제거하는 방식으로 코드를 작성하는 것이 개발 작업

에서 최우선으로 생각해야 할 최적화 과정이다.

런타임 환경에서 가장 많이 발생하는 브라우저의 작업 가운데 자바스크립트의 실행 성

능을 저해하는 요인이 변수, 객체, 함수 등의 메모리상의 위치를 찾는 탐색 작업이다. 이

탐색 작업이 브라우저에서 어떻게 이뤄지는지는 스코프 체인을 통해 알 수 있다. 스코

프 체인의 동작 원리를 이해하고 브라우저의 탐색 작업을 최적화해 자바스크립트 실행

속도를 끌어올릴 수 있는 방법을 찾아보자.

스코프 체인이란?

자바스크립트의 함수를 실행하면서 어떤 속성(변수, 객체 등)에 접근해야 할 때 해당 속

성을 효율적으로 탐색하도록 속성을 일정한 객체 단위로 분류하고 각 객체에 접근하기

위한 객체의 참조를 특정한 공간에 저장해 둔다. 이 공간이 바로 스코프 체인이다.

스코프 체인의 구성 요소에는 활성화 객체(Activation Object)와 전역 객체(Global

Object)가 있다. 어떤 자바스크립트 함수가 있을 때 함수에서 접근할 수 있는 모든 속성

가운데 함수 내부에서만 접근할 수 있는 함수의 지역변수나 this, arguments 객체 등의

속성은 스코프 체인의 활성화 객체에 포함돼 관리된다. 그리고 함수 외부에서도 접근

할 수 있는 window, document, 전역함수, 전역변수와 같은 속성은 스코프 체인의 전

역 객체에 포함돼 관리된다. 이 활성화 객체와 전역 객체가 실행 중인 함수의 스코프 체

인(유효 범위 체인)에 포함돼 함수에서 어떤 속성을 탐색할 때 길잡이 역할을 하게 되는

것이다.

전역 객체와 활성화 객체에 대해 좀 더 살펴보자.

window와 document 등의 전역객체는 자바스크립트 동작 시 어디서나 항상 접근

가능한 데이터를 포함하고 있기 때문에 웹 페이지의 자바스크립트가 동작하는 모든

시간 동안 존재하며, 함수 실행 시 함수의 스코프 체인에서 전역 속성을 탐색하는 데

사용된다.

Page 66: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

66 자바스크립트 성능 이야기

반면 활성화 객체는 실행 중인 함수의 내부 데이터(지역변수, this, arguments 등)를 포

함하기 때문에 전역 객체와는 달리 함수가 실행되는 동안에만 존재하며, 함수 내부에서

자주 사용하는 데이터가 모여 있는 만큼 모든 속성을 탐색할 경우 최우선으로 탐색하는

대상 객체가 된다.

특정 함수에 의해 구성되는 스코프 체인의 전체적인 모습은 다음 그림과 같다.

실행 문맥

스코프 체인 스코프 체인

0

1

활성화 객체

this함수 객체 자신

또는 global 객체

arguments함수 파라미터

값(배열)

함수 내부

지역변수 지역변수 값

전역객체

thisobject

(window 객체)

documentobject

(document 객체)

windowobject

(window 객체)

navigatorobject

(navigator 객체)

전역변수 전역변수 값

전역함수 function 객체

그림 3-5 스코프 체인 관계도

실행 문맥(Execution Context)은 함수가 동작하는 환경을 나타내며, 브라우저 내부에

서 사용되는 객체다. 실행 문맥은 함수가 실행될 때 새로 생성되고 함수가 종료될 때 소

멸되며 함수의 스코프 체인에 대한 참조를 가지고 있게 된다. 함수는 어떤 속성에 접근

해야 할 때 실행 문맥을 통해 스코프 체인에 접근한다.

Page 67: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 67

실행 문맥은 자신과 연관된 함수의 스코프 체인을 참조하고 있으며, 함수에서 접근해야

할 어떤 속성의 탐색 경로는 ‘실행 문맥 > 스코프 체인 > 활성화 객체 > 스코프 체인 > 전

역 객체’와 같이 구성된다.

그럼 코드를 예로 들어 보겠다.

파라미터 값이 0인지 판별하는 isZero() 함수의 코드는 다음과 같다.

function isZero(num) {

var res = (num === 0);

return res;

}

var result = isZero(0);

위 함수가 실행될 때 구성되는 스코프 체인은 다음과 같다.

실행 문맥(isZero(0))

스코프 체인 스코프 체인

0

1

활성화 객체

this window

arguments [0]

resundefined

-> boolean(true)

전역객체

thisobject

(window 객체)

documentobject

(document 객체)

windowobject

(window 객체)

navigatorobject

(navigator 객체)

isZero function

resultundefined

-> boolean(true)

그림 3-6 isZero() 함수의 스코프 체인 관계도

Page 68: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

68 자바스크립트 성능 이야기

isZero() 함수가 생성되면 함수의 실행 문맥은 아직 생성되지 않은 상태이므로 함수의

내부 속성(브라우저 내부 기능)의 하나인 [[Scope]] 속성에 전역 객체(Global Object)

를 가리키는 스코프 체인을 우선적으로 저장한다. 함수의 실행 문맥은 함수가 실행되는

시점에 생성된다.

isZero() 함수가 실행되면 실행 문맥이 생성되며 함수의 [[Scope]] 속성에 저장한 전역

객체를 함수의 실행 문맥이 가리키는 스코프 체인에 복사한다. 그리고 활성화 객체를

생성하고 함수의 내부에서 접근할 수 있는 속성(파라미터 변수인 num과 boolean 값을

저장하는 변수인 res 및 this, arguments 등)을 채워 넣은 후 활성화 객체를 스코프 체

인에 추가한다.

이렇게 함수를 실행할 준비를 마치면 그 후에 함수에 정의된 각 구문을 실행한다.

var res = (num === 0);

isZero() 함수에 있는 위 구문을 실행할 때 함수의 파라미터 변수인 num과 함수의 지

역변수로 선언된 res에 대해 해당 변수의 메모리에 접근해야 하는데, 둘 다 활성화 객체

에 포함돼 있는 속성이기 때문에 ‘실행 문맥 > 스코프 체인 > 활성화 객체’의 경로로 탐

색해 접근한다. 전역 객체는 탐색하지 않는다.

만약 isZero() 함수 내에서 window, document 등의 전역변수에 접근해야 한다면

‘실행 문맥 > 스코프 체인 > 활성화 객체 > 스코프 체인 > 전역 객체’의 경로로 속성

(window, document)을 탐색했을 것이다. 즉, 활성화 객체를 먼저 탐색한 후 찾는 속성

이 없을 때는 스코프 체인에 참조돼 있는 다음 탐색 대상인 전역 객체를 탐색하게 된다. 그

럼 스코프 체인에서 발생하는 속성 탐색 과정을 성능과 연관 지어 생각해 볼 수 있다.

즉, 함수에서 어떤 속성에 접근해야 하는 경우를 가정해 보면 된다. 만약 함수가 중첩될

경우에는 중첩이 깊어질수록 활성화 객체는 함수의 중첩된 깊이만큼 생성된다. 즉, 3번

중첩된 함수에서 가장 안쪽의 함수는 스코프 체인에 3개의 활성화 객체를 갖게 되는 것

이다. 그 결과는 다음 그림과 같다.

Page 69: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 69

실행 문맥

스코프 체인 스코프 체인

0

1

2

3

활성화 객체 2

··· ···

활성화 객체 3

··· ···

활성화 객체 1

··· ···

전역객체

··· ···

그림 3-7 중첩함수의 스코프 체인

스코프 체인의 최상위에는 현재 실행 중인 가장 안쪽에 중첩된 함수의 활성화 객체를

참조하며, 그 뒤로 바깥쪽 방향으로 중첩된 함수의 순서대로 각 함수의 활성화 객체를

참조하게 된다. 그리고 마지막으로 전역 객체를 참조하게 된다.

이 경우 가장 안쪽의 함수에서 전역 속성에 접근할 때는 ‘실행 문맥 > 스코프 체인 > 활

성화 객체1 > 스코프 체인 > 활성화 객체2 > 스코프 체인 > 활성화 객체3 > 스코프 체인

> 전역 객체’와 같이 긴 탐색 경로를 거쳐야 한다. 이러한 탐색 경로를 줄임으로써 실행

시간을 단축하고 자바스크립트 성능을 향상시킬 수 있다.

속성의 탐색 경로를 어떻게 줄여야 자바스크립트가 좋은 성능을 낼 수 있는지 살펴보

겠다.

지역변수를 활용한 스코프 체인 탐색 성능 개선

앞에서 스코프 체인의 탐색 방법을 살펴보면 여러 개의 활성화 객체와 전역 객체를 탐

색하면서 접근하려는 속성이 있는지 확인하는 과정이 반복된다. 그렇다면 첫 번째로 탐

색하는 활성화 객체에 찾고자 하는 속성이 있는 경우 추가로 발생할 수 있는 다른 활성

화 객체, 전역 객체를 탐색하는 과정을 줄여 성능을 향상시킬 수 있을 것이다.

Page 70: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

70 자바스크립트 성능 이야기

간단한 예제 코드를 살펴보자.

예제 3-9 Code #1 – 함수 내에서 전역 스코프 변수에 직접 접근하는 예제

window.htmlstring = [];

function makeList () {

htmlstring.push("<ul>");

for (var i = 0; i < 100; i++) {

htmlstring.push("<li>value : " + i + "</li>");

}

htmlstring.push("</ul>");

}

makeList();

makeList() 함수가 실행되면 함수 내부에서 htmlstring, i 속성에 접근하기 위해 스코

프 체인을 탐색한다. i 변수는 실행 중인 함수의 지역변수이므로 처음 탐색하는 활성화

객체에서 찾을 수 있다. 그러나 htmlstring 객체는 활성화 객체에 먼저 접근해서 탐색하

지만 찾지 못하고, 다시 전역 객체를 탐색해서 찾아야 한다. 다음과 같이 코드를 수정해

성능을 높일 수 있다.

예제 3-10 Code #2 –함수 지역변수로 참조해 전역 스코프 변수에 접근하는 예제

window.htmlstring = [];

function makeList () {

var htmlstr = htmlstring;

htmlstr.push("<ul>");

for (var i = 0; i < 100; i++) {

htmlstr.push("<li>value : " + i + "</li>");

}

htmlstr.push("</ul>");

}

makeList();

수정한 코드 중 var htmlstr = htmlstring; 부분이 성능 개선의 핵심이다. 전역 객체

에 존재하는 htmlstring 속성을 makeList() 함수의 지역변수에 저장해 활성화 객체에

서 바로 찾을 수 있게 한 것이다. 물론 var htmlstr = htmlstring; 구문을 실행하는 동안

htmlstring 속성에 접근해야 하므로 최초 한 번은 활성화 객체와 전역 객체를 모두 탐

Page 71: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 71

색해야 한다. 하지만 그 이후에는 활성화 객체에 저장된 htmlstr 속성으로 전역변수인

htmlstring 객체에 접근할 수 있으니 활성화 객체를 거쳐 전역 객체까지 탐색할 필요가

없어진다.

최적화 이전의 코드에서 htmlstring 객체를 찾으려면 ‘실행 문맥 > 스코프 체인 > 활성

화 객체 > 스코프 체인 > 전역 객체’와 같이 동일한 탐색 경로를 7번 거친다. 하지만 수

정한 코드에서는 var htmlstr = htmlstring; 구문을 실행할 경우 최초 한 번만 ‘실행 문

맥 > 스코프 체인 > 활성화 객체 > 스코프 체인 > 전역 객체’와 같은 속성 탐색 경로를 거

친다. 그 이후 window 객체의 htmlstring 속성에 접근하는 것은 지역변수 htmlstr 속

성에 접근하는 것으로 대체돼 ‘실행 문맥 > 스코프 체인 > 활성화 객체’와 같이 단축된

탐색 경로를 거치므로 실행 속도가 더 빨라진다.

다음은 jsMatch에서 테스트 코드를 실행한 결과다. 많이 복잡한 코드가 아니므로 성능

에 큰 차이는 없지만 인터넷 익스플로러에서는 성능 차이가 두드러졌다.

그림 3-8 스코프 체인 탐색 거리와 성능28

프로토타입 체인

자바스크립트의 모든 객체의 인스턴스는 new 연산자로 생성할 수 있으며, 생성된 인스

턴스 객체는 생성자의 프로토타입(prototype)을 참조하게 된다.

28 http://jindo.dev.naver.com/jsMatch/index.html?d=17&openResult=1

Page 72: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

72 자바스크립트 성능 이야기

var obj = new Object(); // obj – 인스턴스 객체, Object – 생성자 함수

이렇게 생성한 인스턴스 객체는 원본 객체 생성자 함수의 프로토타입 속성에 접근할 수

있다. 인스턴스 객체가 원본 객체 생성자 함수의 프로토타입 속성을 탐색할 때도 탐색

을 위한 체인이 생성되는데, 이를 프로토타입 체인이라 한다.

위 코드에서 Object는 자신의 프로토타입을 참조하며, var obj = new Object(); 구문

이 실행되면 obj는 Object의 프로토타입을 상속받는다. 이때 스코프 체인에서 어떤 속

성을 찾기 위해 활성화 객체로부터 전역 객체로 탐색 범위를 넓혀나간 것과 같이, 프로

토타입 체인에서도 obj는 obj의 어떤 속성을 찾기 위해 obj의 프로토타입으로부터 프

로토타입이 참조한 경로를 따라 탐색 범위를 넓혀 나가며 Object의 프로토타입 속성에

접근한다. 이 과정에서 탐색 경로가 길어질 수 있으며, 탐색 경로의 거리에 따라 프로토

타입 체인에서도 스코프 체인에서와 같은 성능 저하가 발생할 수 있다. 그러므로 프로

토타입에 존재하는 속성을 사용할 때 스코프 체인에서와 마찬가지로 지역변수에 담아

서 사용한다면 불필요한 탐색 과정을 줄여 성능을 높일 수 있다.

그 외 스코프 체인 탐색 성능에 영향을 미치는 요소

앞에서 설명한 요소 외에도 여러 가지 스코프 체인의 변화와 그에 따른 속성의 탐색 시

간에 영향을 미치는 요소가 있다. 그 가운데 with 구문을 살펴보겠다. with 구문은 쓰지

말라는 구문으로 자주 등장하는데, 그 이유를 스코프 체인 탐색 관점에서 살펴보자.

with 구문을 사용하면 참조하는 멤버가 속한 객체를 지정하는 과정을 생략해서 코드의

용량을 줄일 수 있다는 장점이 있다.

obj.name = "test";

obj.age = 22;

obj.address = "seoul";

위와 같이 객체의 속성을 빈번하게 참조하는 구문은 with 구문을 사용해 다음과 같이

줄일 수 있다.

Page 73: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 73

with (obj) {

name = "test";

age = 22;

address = "seoul";

}

사람에 따라선 코드를 읽기 편해졌다고 생각할 수도 있을 만큼 코드가 간결해졌으며,

“obj.”이라는 중복 코드가 제거돼 obj 객체의 멤버를 많이 참조할수록 with 구문을 사

용하지 않은 코드보다 코드의 양을 줄일 수 있다. 자바스크립트 파일의 용량이 줄어들

었으므로 다운로드 시간이 단축될 것이다.

하지만 with 구문 내에서는 with 구문의 인자인 ‘obj’ 객체의 멤버로 구성된 활성화 객

체가 스코프 체인의 탐색 순위에서 최우선권을 갖는다. 이로 인해 with 구문 내에서

with 구문이 포함된 영역의 지역변수 등에 접근하는 것조차 탐색 경로를 2번 거쳐야

하므로 성능이 저하된다. try-catch 구문의 catch 구문도 with 구문과 같은 방식으로

동작하기 때문에 동일한 이유로 인한 성능 저하가 발생한다. 그러므로 with 구문이나

catch 구문을 사용할 때는 이러한 스코프 체인 탐색에서의 성능 문제를 감안해야 한다.

반복문과 성능

자바스크립트의 반복문(Loop)인 for, for-in, while, do-while 구문에도 성능 차이가

있다. 반복문의 성능을 측정해 보고 각 반복문의 성능 차이가 어떤지, 반복문의 성능을

올리는 방법은 무엇인지 알아보겠다.

반복문의 성능 비교

같은 로직을 실행하는 반복문 4개(for, for-in, while, do-while)의 성능을 측정해 보

자.

다음의 ‘사전코드’는 나머지 4개의 반복문에서 사용할 데이터를 만드는 코드로,

jsMatch의 사전코드에 입력한다.

Page 74: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

74 자바스크립트 성능 이야기

예제 3-11 배열을 초기화하는 사전코드

arr = [];

for (var i = 0; i < 400; i++) {

arr[i] = i;

}

그림 3-9 jsMatch의 사전코드 입력란

그리고 다음 4개의 코드가 각 반복문을 테스트하는 코드다.

예제 3-12 Code #1 - for 구문

for (var i = 0, len = arr.length; i < len; i++) {

arr[i]++;

}

예제 3-13 Code #2 - for-in 구문

for (var i in arr) {

arr[i]++;

}

예제 3-14 Code #3 - while 구문

var i = 0, len = arr.length;

while (i < len) {

arr[i] = i;

i++;

}

Page 75: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 75

예제 3-15 Code #4 - do-while 구문

var i = 0, len = arr.length;

do {

arr[i] = i;

i++

} while (i < len);

위의 코드를 jsMatch에서 테스트한 결과는 다음과 같다.

그림 3-10 반복문의 기본적인 성능 비교29

결과를 보면 for-in 구문(Code #2)은 인터넷 익스플로러 이외의 브라우저에서는 두

드러질 정도로 성능이 좋지 않다. 대신 인터넷 익스플로러에서의 성능은 비교적 좋

게 나타난다. 성능 측정 결과로 미뤄봤을 때, 인터넷 익스플로러를 주요 브라우저로

생각하고 서비스를 개발하고 있다면 for-in 구문을 사용해도 큰 문제가 없을 것으

로 보인다. 하지만 인터넷 익스플로러 외에 다른 브라우저까지 고려한다면 브라우저

에 따라 성능이 저하될 수 있다. 또한 for-in 구문은 다른 브라우저에 비해 인터넷 익

스플로러에서 실행 시간이 빠르긴 하지만 위와 같은 간단한 코드를 테스트하는 데도

0.3~0.5초 걸렸다. 따라서 빠른 응답시간을 구현하려면 되도록 for-in 구문을 사용하

지 않는 것이 좋다.

그렇다면 왜 for-in 구문의 성능이 유독 낮은 것일까?

29 http://jindo.dev.naver.com/jsMatch/index.html?d=18&openResult=1

Page 76: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

76 자바스크립트 성능 이야기

for-in 구문 이외의 반복문은 주어진 배열 객체를 배열의 특성에 맞게 순차적으로 모든

요소를 탐색한다. 반면 for-in 구문은 인자로 주어진 배열을 배열이 아닌 일반 객체로

취급하며, 반복 시점마다 객체의 모든 속성을 무작위로 탐색한다. 이러한 탐색 방법의

차이로 for-in 구문은 그 외의 반복문에 비해 배열 탐색에서 현저하게 느리다.

for-in 구문은 그 목적 자체가 객체의 속성을 탐색하는 것이다. 그렇기 때문에 모든 속

성이 순차적으로 정렬돼 있어 선형적인 색인(index)으로 접근할 수 있는 배열보다는 속

성의 이름이 제각각이라 색인으로는 접근할 수 없는 객체의 속성을 탐색하는 데만 사용

하길 권장한다.

for, while, do-while 구문의 최적화

앞서 for-in 구문의 실행 속도가 느리다는 것을 확인했다. 그러나 “반복문은 for-in 외

의 구문만 사용하자”라고 성급한 결론을 내리기 전에 “그럼 for, while, do-while 구문

을 어떻게 사용해야 좀 더 좋은 성능을 낼 수 있을까?”라는 의문을 품어보기 바란다.

for, while, do-while 구문의 주요 사용처를 생각해 보자. 근본적인 사용 목적은 같은

동작을 원하는 만큼 반복시키는 것이지만 서비스를 개발하다 보면 유독 배열 객체를 대

상으로 반복문을 실행하는 경우가 많다.

다음은 배열의 모든 요소를 탐색하는 다양한 형식의 반복문이다.

예제 3-16 for 구문을 이용한 배열 탐색

var arr = [ … ];

for (int i = 0; i < arr.length; i++) {

}

예제 3-17 while 구문을 이용한 배열 탐색

var arr = [ … ];

var i = 0;

while (i < arr.length) {

i++;

}

Page 77: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 77

예제 3-18 do-while 구문을 이용한 배열 탐색

var arr = [ … ];

var i = 0;

do {

i++;

} while (i < arr.length);

for-in 구문을 사용한 반복문보다는 for, while, do-while 구문을 사용한 반복문의 성

능이 더 좋다는 것을 이미 확인했다. 그럼 반복문의 성능을 좀 더 향상시키려면 어떤 부

분을 최적화해야 할까?

스코프 체인과 프로토타입 체인의 내용으로 돌아가자. 스코프 체인과 프로토타입 체

인에서 성능에 영향을 주는 것은 속성의 탐색 시간이었으며, 원하는 속성에 접근하기

위해 스코프 체인에 속해 있는 활성화 객체를 얼마나 많이 거쳐야 하는가가 속성의 탐

색 시간을 결정했다. 그리고 스코프 체인의 탐색 경로를 줄이고 자바스크립트의 성능

을 최대화하기 위해 스코프 체인의 탐색 경로에서 멀리 떨어진 활성화 객체의 속성에

대한 참조를 가장 빨리 접근할 수 있는 활성화 객체에 복사해 두고 사용하는 방식을 사

용했다.

이 내용을 기억하고 앞의 코드를 보자.

각 반복문에서 arr, i는 모두 지역변수로 선언됐다. 그리고 반복 횟수만큼 arr.length 속

성을 참조하는 것을 볼 수 있다. length 속성은 arr 객체의 속성이므로 arr 객체를 찾기

위한 스코프 체인 탐색 과정과 arr 속성인 length 속성을 찾기 위한 arr 객체의 프로토

타입 체인 탐색 과정을 거쳐야 한다. length 속성에 접근하기 위한 탐색 과정을 줄여 성

능을 향상시킨 코드는 다음과 같다.

예제 3-19 for 구문

var arr = [ … ];

for (var i = 0, len = arr.length; i < len; i++) {

}

Page 78: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

78 자바스크립트 성능 이야기

예제 3-20 while 구문

var arr = [ … ];

var i = 0, len = arr.length;

while (i < len) {

i++;

}

예제 3-21 do-while 구문

var arr = [ … ];

var i = 0, len = arr.length;

do {

i++;

} while (i < len);

arr 객체의 length 속성을 지역변수로 복사해 스코프 체인과 프로토타입 체인의 탐색

경로를 축소시켰다. 이렇게 최적화한 코드와 최적화 이전의 코드는 반복문을 실행하는

횟수만큼 성능에 차이가 생길 것이다.

자바스크립트에서 수백만 번 반복해서 실행되는 코드를 사용할 일은 많지 않으니 500

번 정도 실행되는 반복문을 만들어 테스트해 보겠다. jsMatch의 사전 코드에 다음 코드

를 추가해 요소가 500개인 배열을 만들고 각 반복문을 실행했다.

예제 3-22 for 구문 최적화 테스트용 사전 코드

3

arr = [];

for (var i = 0; i < 500; i++) {

arr[i] = i;

}

예제 3-23 Code #1 - 최적화 이전의 for 구문

3

for (var i = 0; i < arr.length; i++) {

arr[i]++;

}

Page 79: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 79

예제 3-24 Code #2 - 최적화 이후의 for 구문

for (var i = 0, len = arr.length; i < len; i++) {

arr[i]++;

}

다음의 성능 측정 결과에서 알 수 있듯이 length 속성의 탐색 경로를 축소한 코드(Code

#2)가 모든 브라우저에서 눈에 띌 만큼 좋은 성능을 보여 준다.

그림 3-11 for 구문의 성능 최적화30

그럼 같은 로직을 실행하는 코드를 while 구문으로 작성해 보자.

예제 3-25 while 구문 최적화 테스트용 사전 코드

arr = [];

for (var i = 0; i < 500; i++) {

arr[i] = i;

}

예제 3-26 Code #1 - 최적화 이전의 while 구문

var i = 0;

while (i < arr.length) {

arr[i]++;

i++;

}

30 http://jindo.dev.naver.com/jsMatch/index.html?d=19&openResult=1

Page 80: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

80 자바스크립트 성능 이야기

예제 3-27 Code #2 - 최적화 이후의 while 구문

var i = 0, len = arr.length;

while (i < len) {

arr[i]++;

i++;

}

코드의 실행 결과는 다음과 같다. while 구문도 for 구문과 비슷하게 최적화를 실행한

Code #2의 성능이 더 좋다.

그림 3-12 while 구문의 성능 최적화31

마지막으로 do-while 구문도 살펴보자.

예제 3-28 do-while 구문의 최적화 테스트용 사전 코드

arr = [];

for (var i = 0; i < 500; i++) {

arr[i] = i;

}

예제 3-29 Code #1 - 최적화 이전의 do-while 구문

var i = 0;

do {

arr[i]++;

i++;

} while(i < arr.length);

31 http://jindo.dev.naver.com/jsMatch/index.html?d=20&openResult=1

Page 81: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 81

예제 3-30 Code #2 - 최적화 이후의 do-while 구문

var i = 0, len = arr.length;

do {

arr[i]++;

i++;

} while (i < len);

do-while 구문의 성능 측정 결과는 while 구문과 거의 같다. do-while 구문 역시 최적

화를 실행한 Code #2의 성능이 더 좋다.

그림 3-13 do-while 구문의 성능 최적화32

앞에서 살펴본 for, while, do-while 구문 예제는 모두 같은 부분(var len = arr.length)

에 대한 성능 최적화를 수행했으므로 성능 향상 비율이 구문에 따라 큰 차이가 없었다.

그럼에도 각 구문의 성능을 측정한 결과를 설명한 이유는 for, while, do-while 구문

모두 반복문의 조건절(i < arr.length)에 대해 모든 브라우저가 내부적으로 별다른 최적

화를 하고 있지 않는 것으로 보이며, 그에 따른 최적화 전후의 성능 차이가 뚜렷하다는

것을 보여 주기 위해서다.

효율적인 알고리즘 구현을 통한 성능 개선

반복문을 동일한 횟수로 실행할 때 반복문을 어떻게 작성하느냐에 따라 성능에 차이가

있다는 것을 알았다. 앞에서 설명한 방법으로 반복문을 최적화해 성능을 향상시킬 수도

있지만, 그보다 먼저 생각해야 하는 성능 개선 방법이 있다.

32 http://jindo.dev.naver.com/jsMatch/index.html?d=21&openResult=1

Page 82: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

82 자바스크립트 성능 이야기

바로 반복 횟수를 최소화하는 효율적인 알고리즘을 사용하는 것이다. 잘 짜인 알고리즘

으로 반복문의 호출 횟수를 줄이는 방법이 그 어떤 방법보다 성능을 개선할 수 있는 좋

은 방법이며, 반복문을 사용할 때 어떤 최적화보다 먼저 적용해야 하는 성능 개선 작업

이다. 반복 횟수를 최소화하는 알고리즘에는 반복문을 실행하는 도중 목적을 달성했을

때 break 등을 호출해 더는 불필요한 반복문을 호출하지 않고 반복 구분을 빠져 나오게

하는 것을 예로 들 수 있다.

만약 데이터를 정렬(sort)하거나 탐색하는 데 반복문을 사용한다면 퀵 소트(quick-

sort), 머지 소트(merge-sort), BFS(Breadth First Search), DFS(Depth First Search)

등과 같은 증명된 알고리즘을 함께 사용해 반복 횟수를 줄여 자바스크립트 실행 성능을

향상시킬 수 있다. 알고리즘에 대한 자세한 설명은 이 책의 주제를 벗어나므로 각종 알

고리즘 관련 서적을 참고하기 바란다.

조건문과 성능

반복문만큼이나 조건문 또한 자바스크립트를 포함한 프로그래밍 전반에서 필수적으로

사용하는 요소다. 자바스크립트에는 if, if-else, switch, 삼항연산자(? :) 등의 조건문이

있다. 조건문의 성능을 비교해 보고 언제 어떤 형식의 조건문을 사용하면 자바스크립트

의 실행 성능을 향상시킬 수 있을지 살펴보겠다.

조건문의 성능 비교

먼저 true와 false만 판단하는 최소한의 조건 분기를 처리하는 코드로 if(-else), switch,

삼항연산자(? : )의 성능을 측정했다. 성능 측정에 사용할 코드는 다음과 같다. 1부터 10

까지의 숫자를 영어 단어로 반환하는 함수다.

예제 3-31 Code #1 – if-else 구문을 활용한 조건분기

function toEnglish(value) {

var number = "zero";

if(value == = 1) {

Page 83: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 83

number = "one";

} else if(value === 2) {

number = "two";

} else if(value === 3) {

number = "three";

} else if(value === 4) {

number = "four";

} else if(value === 5) {

number = "five";

} else if(value === 6) {

number = "six";

} else if(value === 7) {

number = "seven";

} else if(value === 8) {

number = "eight";

} else if(value === 9) {

number = "nine";

} else if(value === 10) {

number = "ten";

} else {

number = "null";

}

return number;

}

for(var i = 0; i < 12; i++) {

toEnglish(i);

}

예제 3-32 Code #2 – switch-case 구문을 활용한 조건분기

function toEnglish(value) {

var number = "zero";

switch (value) {

case 1:

number = "one";

break;

case 2:

number = "two";

break;

Page 84: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

84 자바스크립트 성능 이야기

case 3:

number = "three";

break;

case 4:

number = "four";

break;

case 5:

number = "five";

break;

case 6:

number = "six";

break;

case 7:

number = "seven";

break;

case 8:

number = "eight";

break;

case 9:

number = "nine";

break;

case 10:

number = "ten";

break;

default :

number = "null";

break;

}

return number;

}

for(var i = 0; i < 12; i++) {

toEnglish(i);

}

예제 3-33 Code #3 - 삼항연산자를 활용한 조건분기

function toEnglish(value) {

var number = false;

number = (value === 1) ?

"one" : (value === 2) ?

"two" : (value === 3) ?

Page 85: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 85

"three" : (value === 4) ?

"four" : (value === 5) ?

"five" : (value === 6) ?

"six" : (value === 7) ?

"seven" : (value === 8) ?

"eight" : (value === 9) ?

"nine" : (value === 10) ?

"ten" : "null";

return number;

}

for(var i = 0; i < 12; i++) {

toEnglish(i);

}

코드마다 10개 정도의 조건문을 사용했기 때문에 코드를 읽기는 어렵지 않지만, 이렇

게 조건문을 나열하는 방식은 되도록 사용하지 않기를 권장한다. 조건이 100개, 200개

로 늘어난다면 코드를 작성하기도 어렵고 코드의 양도 늘어나 자바스크립트 파일의 크

기가 커지고 코드를 관리하기도 어렵다. 조건이 많을 때는 배열이나 JSON, 해시(hash)

형식의 자바스크립트 객체를 사용하는 편이 개발은 물론 이후의 유지 보수 작업도 더

수월해질 것이다.

그럼 위에서 작성한 코드의 성능을 측정한 결과를 보자.

그림 3-14 조건문의 성능 비교 결과33

33 http://jindo.dev.naver.com/jsMatch/index.html?d=27&openResult=1

Page 86: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

86 자바스크립트 성능 이야기

조건의 개수가 많지 않으므로 사파리(Safari)를 제외한 대부분의 브라우저에서는 조

건문 사이에 별다른 성능 차이가 없다. 단지, 일반적으로 조건 판단 요소가 많아질수록

switch-case 구문의 성능이 좀 더 좋다는 점은 알아 두기 바란다.

조건문 사이에 성능 차이가 크지는 않지만 여러 개의 조건문을 나열하는 형식으로 사용

하는 것은 성능 문제 외에도 개발의 효율성이 떨어지므로 권장하지 않는다. 그래도 부

득이하게 조건문을 나열하는 형식으로 사용해야 할 때가 있다. 특정 값이 맞는지 확인

하는 == 형식의 비교가 아닌 다른 형식(>, <, &&, ||)의 비교연산자를 사용할 때는 if-

else 구문이 아닌 다른 조건문을 이용하기가 어렵기 때문에 조건문을 나열하는 형식으

로 기능을 구현해야 한다.

하지만 이때도 조건문 구현 방식을 바꿔 성능을 향상시킬 수 있다. 다음은 입력된 숫자

의 범위를 반환하는 두 가지 형식의 코드다.

예제 3-34 Code #0 - 단순한 구조의 조건 비교

function number_range(value) {

var range = "";

if(value <= 10) {

range = "~10";

} else if(value <= 20) {

range = "11~20";

} else if(value <= 30) {

range = "21~30";

} else if(value <= 40) {

range = "31~40";

} else if(value <= 50) {

range = "41~50";

} else if(value <= 60) {

range = "51~60";

} else if(value <= 70) {

range = "61~70";

} else {

range = "71~";

}

return range;

}

Page 87: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 87

예제 3-35 Code #1 - 계층 구조를 활용한 조건 비교

function number_range(value) {

var range = "";

if (value <= 40) {

if (value <= 20) {

if (value <= 10) {

range = "~10";

} else {

range = "11~20";

}

} else {

if (value <= 30) {

range = "21~30";

} else {

range = "31~40";

}

}

} else {

if (value <= 60) {

if (value <= 50) {

range = "41~50";

}

else {

range = "51~60";

}

} else {

if (value <= 70) {

range = "61~70";

}

else {

range = "71~";

}

}

}

return range;

}

number_range(42);

Page 88: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

88 자바스크립트 성능 이야기

두 코드 가운데 어느 것이 다른 것보다 항상 성능이 더 좋다고 말할 수는 없다. 두 코드

는 각각 다른 성능 관점에서 작성된 코드이며, 입력되는 데이터에 따라 둘 중 하나의 성

능이 다른 것보다 좋을 수 있다. 예제 3-34가 예제 3-35보다 가독성이 좋지만 입력

값에 따라 두 코드의 비교 연산 횟수가 달라진다. 경우에 따라서는 예제 3-35가 예제

3-34의 가독성을 무시할 만큼 성능을 향상시킬 수도 있다.

예제 3-34의 입력값이 1일 때는 첫 번째 조건문의 비교 연산으로 충분하지만 입력값이

71 이상의 수일 때는 최대 8번의 비교 연산과 조건문을 실행해야 한다. 반면 예제 3-35

는 가독성은 좋지 않지만 어느 범위의 숫자가 입력값으로 사용되든 3번의 비교 연산과

조건문 실행으로 원하는 결과를 얻을 수 있다.

어느 범위의 입력값이 주로 사용되느냐에 따라 두 코드의 실행 성능은 다를 것이다. 입

력값의 패턴을 알고 있다면 입력값 패턴에 따른 적절한 조건 분기 방식을 선택해 코드

의 실행 성능을 높일 수 있다.

조건문 최적화

조건문 최적화는 조건 분기 처리 방식을 조정하는 방법 외에도 다른 방법이 있다. 조건

문을 최소화하고 배열이나 해시 객체를 사용하는 방법이다. 먼저 배열을 사용하는 예제

를 보자.

예제 3-36 Code #2 – 배열을 활용한 조건 비교

function number_range(value) {

var arr_range = ["~10", "11~20", "21~30", "31~40", "41~50", "51~60", "61~70",

"71~"];

var arr_range_index = Math.ceil(value/10) - 1;

if (arr_range_index < 0) {

arr_range_index = 0;

} else if (arr_range_index >= (arr_range.length)) {

arr_range_index = arr_range.length-1;

}

Page 89: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 89

return arr_range[arr_range_index];

}

number_range(42);

위 코드는 배열을 이용해, 예제 3-34와 예제 3-35의 함수와 같은 역할을 하는 함수다.

조건문은 하나의 if-else 구문으로 감소됐다.

예제 3-36과 거의 유사한 코드지만 배열 대신 해시 객체를 사용해 다음과 같이 작성할

수도 있다.

예제 3-37 Code #3 – 해시 객체를 활용한 조건 비교

function number_range(value) {

var hash_range = { 2 : "11~20", 3 : "21~30", 4 : "31~40", 5 : "41~50", 6 : "51~60",

7 : "61~70" };

var hash_range_key = Math.ceil(value/10);

if (hash_range[hash_range_key]) {

return hash_range[hash_range_key];

}

if (value <= 10) {

return "~10";

}

return "71~";

}

number_range(42);

코드의 가독성은 예제 3-36이나 예제 3-37보다 예제 3-35가 더 좋겠지만 예제 3-35

보다 예제 3-36과 예제 3-37의 용량이 더 작고 코드에서 조건 비교 횟수가 더 적으므

로 다운로드 시간이나 조건문 실행을 고려하면 예제 3-36과 예제 3-37의 실행 속도가

예제 3-35보다 빠를 것이라고 예상할 것이다.

하지만 실행 성능 측정 결과는 다음과 같다. 그래프에서 Code #1은 예제 3-35를 실행

한 결과다.

Page 90: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

90 자바스크립트 성능 이야기

그림 3-15 조건문 구현 방식 비교34

왜 예제 3-35(Code #1)의 실행 속도가 가장 빠를까? 앞에서 설명한 대로 코드에서 조

건문이 호출되는 횟수는 예제 3-36과 예제 3-37이 예제 3-35보다 적다. 하지만 예제

3-36과 예제 3-37에서는 조건문의 실행에 필수적인 배열과 해시 객체를 생성하고 탐

색하는 시간과 자바스크립트 내장 함수인 Math.ceil() 함수를 호출하는 시간이 성능에

영향을 미쳤다. 그래서 추가적인 비용이 없는 예제 3-35가 상대적으로 더 좋은 성능을

보였다. 즉, 이 비교에서는 조건문의 호출 횟수가 성능 개선에서 중요한 요소가 아니었

다고 할 수 있다.

이렇게 복잡한 로직을 구현할 때는 성능 최적화 작업을 코드의 유지 보수와 연관해서

생각하는 것도 좋다.

예제 3-34와 예제 3-35가 예제 3-36과 예제 3-37보다 가독성이 더 좋기 때문에 쉽게

이해하고 수정할 수 있을 것이다. 하지만 예제 3-36과 예제 3-37은 입력값의 범위를

판별하는 조건이 추가, 삭제됐을 때 수정해야 하는 범위가 작기 때문에 조건의 추가, 삭

제가 더 쉽다는 장점이 있다.

코드의 유지 보수에서 코드의 용량이 얼마나 많이 늘거나 줄지, 코드를 유지 보수할 때

로직을 이해하고 작업하기 쉬운 코드는 무엇인지를 고려해 상황에 적합한 형태의 코드

를 사용하는 것이 복잡한 로직을 구현할 때 가장 적합한 최적화 방법일 것이다.

34 http://jindo.dev.naver.com/jsMatch/index.html?d=23&openResult=1

Page 91: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 91

코드별 장단점 비교

예제 3-34(Code #0)와 예제 3-35(Code #1), 예제 3-36(Code #2), 예제

3-37(Code #3)의 장단점을 성능 측면에서 몇 가지만 정리했다. 여기서 정리한 것 외에

도 많은 장단점을 찾을 수 있을 것이다.

표 3-1 조건문 구현 방식의 장단점 비교

코드 장점 단점

Code #0, Code #1

• 코드가 직관적이며 알아보기 쉽다(Code #0의 경우)

• 단순한 값 비교의 조건문만 있을 뿐 자바스크립트 라이브러리나 배열과 같은 객체를 사용하지 않아 실행 성능이 좋다.

• Code #2와 Code #3에 비해 코드의 용량이 크다. 비교 조건이 추가될수록 용량 차이가 커져서 자바스크립트 파일의 다운로드 성능이 저하된다.

• 비교 조건이 추가되거나 변경될 때 코드의 수정 범위가 Code #2와 Code #3에 비해 넓다.

Code #2, Code #3

• 코드 용량이 작고, 비교 조건이 추가돼도 코드 용량의 변화가 Code #0과 Code #1에 비해 작기 때문에 자바스크립트의 다운로드 성능 저하에 영향을 덜 미친다.

• 조건을 추가할 때 코드의 변경 범위가 배열, 해시 객체로 한정되어 수정 범위가 넓지 않고, 그만큼 다른 코드에 미치는 영향도 작다.

• 수식이 포함돼 있기 때문에 코드가 직관적이지 않으며, 알고리즘을 이해하는 과정이 필요하다.

• 배열, 해시 객체를 생성, 초기화해야 하는 비용 때문에 실행 성능이 떨어진다.

각 코드의 장단점을 봤을 때 비교 조건의 개수가 많지 않고 추가, 삭제될 일도 거의 없으

며 실행 성능이 중요할 때는 예제 3-34(Code #0)와 예제 3-35(Code #1)의 방식으로

조건문을 작성하는 것이 좋을 것이다. 조건의 개수가 많고 추가, 삭제가 빈번하며, 함께

다운로드하는 리소스(이미지, 스타일시트, 자바스크립트 등)가 많아 최대한 자바스크립

트 파일의 용량을 줄이는 것이 중요할 때는 예제 3-36(Code #2)과 예제 3-37(Code

#3)의 방식으로 조건문을 작성하는 것이 좋을 것이다.

이렇게 애매모호한 결론을 제시한 이유는 특정 코드 형식이 항상 최고의 성능을 낸다는

변치 않는 믿음을 갖기보다는 여러 가지 성능 관점(실행 성능, 다운로드 속도 등)을 고

려해 현재 작업에서 중요한 기준에 맞춰 개발하는 것이 중요하기 때문이다. 자신이 생

각했던 좋은 성능을 위한 코드가 여러 가지 다른 요소 때문에 실제로는 좋은 성능을 내

지 못하는 경우도 적지 않다. 의심이 생긴다면 해당 부분의 성능을 직접 측정해 보는 습

관을 들이는 것이 가장 중요하다.

Page 92: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

92 자바스크립트 성능 이야기

문자열 연산과 성능

자바스크립트에서 주로 하는 작업 가운데 하나가 바로 문자열에 대한 연산이다. 자바스크

립트로 개발하다 보면 문자열을 여러 가지 방식으로 조합, 편집해 브라우저에 출력하는 코

드를 자주 작성하게 된다. 이러한 문자열 연산에서 실행 성능을 좀 더 높일 수 있는 방법을

찾아보자.

문자열 생성 성능 비교

자바스크립트가 제공하는 문자열 생성 방법에는 두 가지가 있다. String 객체를 이용한

방법과 문자열 리터럴(" ")을 이용하는 방법이다. 먼저 단순히 문자열을 생성하는 코드

의 성능을 비교해 보자.

예제 3-38 Code #1 – String 객체를 이용한 문자열 생성

var str = new String("abcdefghijklmnopqrstuvwxyz");

예제 3-39 Code #2 – 리터럴을 이용한 문자열 생성

var str = "abcdefghijklmnopqrstuvwxyz";

측정한 시간이 지극히 짧지만 String 객체를 이용한 방법과 리터럴을 이용한 방법에는

생각보다 성능 차이가 크다. 특히 리터럴을 이용해 문자열을 생성할 때는 브라우저 사

이에 성능이 비슷하지만 String 객체로 문자열을 생성하면 브라우저마다 성능이 다르

고 차이가 크다. 문자열을 생성할 때는 되도록 String 객체보다는 리터럴을 사용하자.

그림 3-16 문자열 생성 성능 비교35

35 http://jindo.dev.naver.com/jsMatch/index.html?d=26&openResult=1

Page 93: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 93

문자열 연산 성능 비교

다음은 문자열 연산에 대해 살펴보겠다.

자바스크립트로 개발하다 보면 여러 가지 데이터를 조합해 긴 문자열을 생성해야 할

때가 많다. 이때 가장 많이 사용하는 방법이 +, += 연산자를 이용하거나 Array.join()

메서드를 이용하는 방법이다. 문자열을 연산할 때 += 연산자와 Array.join() 메서드 가

운데 어느 것을 사용하는 것이 좋다고 주장하고 이를 뒷받침할 측정 결과를 제시하는

글은 인터넷에 수없이 많다. 그러나 세월이 흐르고 여러 가지 브라우저가 출시되면서

그 많은 글 가운데 대부분이 더는 지금의 웹 환경에 맞지 않게 됐다. 그래서 현재(2012

년 7월) 사용하는 브라우저를 대상으로 문자열 연산 방법의 성능을 측정해 보는 것에

의미를 두고 문자열 연산 성능을 검토했다.

다음은 성능 측정을 위한 문자열 병합 예제 코드와 그 측정 결과다.

예제 3-40 Code #1 – += 연산자를 이용한 문자열 병합

str = "";

for(var i = 0; i < 100; i++) {

str += "test";

}

예제 3-41 Code #2 – Array.join() 메서드를 이용한 문자열 병합

arr = [];

for(var i = 0; i < 100; i++) {

arr[i] = "test";

}

arr.join("");

대체로 Array.join() 메서드를 이용한 문자열 병합이 여러 브라우저에서 안정적으로 좋

은 성능을 나타낸다.

Page 94: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

94 자바스크립트 성능 이야기

그림 3-17 문자열 병합 성능 비교36

물론 이 그래프는 100번이라는 횟수와 문자열 요소의 길이를 고정시킨 병합 연산의 결

과이므로 실제로 자신에게 맞는 최적화 코드를 찾으려면 자신이 반복할 문자열 병합 횟

수와 문자열 요소의 길이를 기반으로 테스트를 거치는 것이 좋다. 필자는 경험상 100번

이상의 문자열 병합을 반복할 일이 자바스크립트를 이용하는 환경에서는 드문 상황이

라 가정했다. 그리고 다양한 횟수와 다양한 길이의 문자열에 대한 각 브라우저의 성능

측정 결과를 다루려면 지면이 많이 필요해서 제한적인 상황을 구현한 코드로 브라우저

의 실행 성능을 비교했다.

+= 연산자는 두 문자열을 합친 새로운 문자열(str += "test")을 만들고 새로운 메모리

위치에 저장함과 동시에 기존 문자열(str)에 대한 참조를 변경하는 연산을 반복적으로

실행해야 한다. 하지만 Array.join() 메서드로 연산하면 비교적 메모리에 효율적으로

접근할 수 있는 배열을 사용한다. 배열에 저장된 문자열을 모두 합쳐 하나의 문자열을

생성하고 저장하므로 문자열이 병합될수록 점점 더 큰 문자열을 생성하고 저장해야 하

는 += 연산에 비해 불필요한 문자열 참조 변경과 재생성 작업이 없다.

보통 인터넷 익스플로러 8 버전 이하의 브라우저는 문자열 병합 연산 방식에 따른 성능

차이가 크다. 인터넷 익스플로러 8 이후에 출시된 브라우저부터는 브라우저 내부의 최

적화 작업을 통해 두 연산자의 성능 차이가 점점 줄어들고 있다.

문자열 연산을 할 때 모든 조건에서 어떤 연산자를 사용하라고 말할 수는 없지만 위의

결과를 토대로 말하자면 아직(2012년 7월)까지는 += 연산자보다 Array.join() 메서드

36 http://jindo.dev.naver.com/jsMatch/index.html?d=25&openResult=1

Page 95: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 95

로 병합 연산을 하는 것이 전반적인 브라우저 환경에서 좀 더 좋은 성능을 얻을 수 있다.

하지만 인터넷 익스플로러 8 이후의 브라우저에서는 브라우저 내부에서 최적화 작업을

실행하고 있다고 하니, Array.join() 메서드와 += 연산 가운데 선택해야 한다면 최신 브

라우저에서 테스트해 보고 두 연산자의 성능 차이를 살펴보며 개발할 것을 권장한다.

정규 표현식과 성능

정규 표현식(Regular Expression)에 대해서는 간단하게 언급하기로 한다. 정규 표현식

자체가 다양한 조건의 여러 가지 상황에 대처하기 위해 만들어진 것이라 짧은 지면에

담기에는 범위가 너무 넓다. 정규 표현식의 최적화에 대해서는 시중에 판매되는 다양한

정규 표현식 관련 전문 서적을 참고하기 바란다.

여기서는 앞에서 살펴본 문자열 연산자의 성능 관련 내용에 추가해, 문자열 앞뒤의 공

백을 제거하는 trim 연산으로 정규 표현식 최적화 방법에서 주요한 점 몇 가지를 간략

하게 살펴보겠다. 앞으로 설명할 정규 표현식 최적화 방법의 요점만 기억하고 그때그때

jsMatch나 dynaTrace37, jsPerf38 등과 같은 성능 측정 도구로 최적화 작업을 실행하

기 바란다.

trim 연산의 핵심은 문자열 앞뒤의 공백 문자를 탐색하고 이를 빈 문자(" ")로 치환하는

것이다. 제거 대상이 되는 공백을 탐색하기 위해서 문자열에 속한 문자를 하나하나 탐

색하기도 하지만 보통은 정규 표현식으로 간단하게 해결한다. 하지만 trim 연산에 사용

하는 정규 표현식도 어떻게 식을 구성해 사용하는지에 따라 성능에 차이가 있다.

탐색 대상 축소를 통한 성능 향상

다음 예제는 trim 연산에서 사용하는 정규 표현식을 나타낸 것이다. trim 연산이 해야

하는 일을 직관적으로 표현하고 있다.

37 AJAX 성능을 테스트하는 도구다. dynaTrace에 대한 더 자세한 내용은 http://www.compuware.com/application-performance-

management/ajax-performance-testing.html을 참조한다.

38 자바스크립트 코드 블록의 성능을 테스트하는 도구다. jsPerf에 대한 더 자세한 내용은 http://jsperf.com/을 참조한다.

Page 96: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

96 자바스크립트 성능 이야기

str.replace(/^\s+|\s+$/g, "");

이 코드를 말로 표현하면, “문자열이 공백으로 시작하는지 보면서 공백 다음에 문자열

끝이 오는지도 확인한다. 그리고 문자열 전체를 돌면서 항상 앞뒤에 위에서 말한 조건

이 맞는지 살펴본다.”라고 할 수 있다.

다음은 trim 연산에 최적화된 정규 표현식으로 자주 언급되는 예제다. 의미는 “문자열

이 공백으로 시작되는지 보고 공백이 끝나는 데까지 공백을 일단 제거한 후 공백이 제

거된 문자열에서 공백 다음에 문자열 끝이 있는지 본다. 공백 다음에 문자열 끝이 있다

면 문자열 끝 앞에 있는 공백을 제거한다.”라고 할 수 있다.

str.replace(/^\s+/, "").replace(/\s+$/, "");

어느 정규 표현식의 성능이 더 좋을까?

앞뒤 공백을 분리된 정규 표현식으로 찾은 두 번째 식은 첫 번째 식보다 문자열 앞쪽의

공백을 찾아 내고 제거하는 데 시간적인 이득이 있다. 반면 첫 번째 식은 뒤쪽의 공백을

찾을 때도 항상 앞쪽의 공백을 함께 찾아야 하기 때문에 그만큼 성능이 저하된다. 문자

열 앞쪽 공백은 정규 표현식으로 찾아서 제거하고, 뒤쪽 공백은 문자열 뒤로부터 반복

문으로 공백을 제거하는 방법도 사용하지만, 이 방법은 성능상 이득은 있어도 코드의

양도 늘어나고 가독성이 떨어지는 문제가 있다.

장황하게 설명했지만 정규 표현식 최적화의 핵심 내용은 ‘탐색 대상의 축소’다. 앞의 공

백을 탐색하는 횟수를 줄여 성능 향상을 가져온 만큼, 정규 표현식을 사용할 때 불필요

한 탐색 과정이 반복되지 않도록 주의해야 한다39.

컴파일 횟수 축소를 통한 성능 향상

trim 연산을 상당 횟수만큼 반복적으로 실행한다고 가정하자. 정규 표현식은 브라우저

에서 컴파일된 후 실행돼야 하는 기능이다. 앞에서 작성한 /^\s+|\s+$/와 같이 슬래시

39 정규 표현식을 사용해 trim 연산을 하는 다양한 방법은, 정규 표현식 및 자바스크립트 최적화 책의 저자인 스티븐 레비탄(Steven Levithan)의

블로그(http://blog.stevenlevithan.com/archives/faster-trim-javascript)에 잘 정리돼 있으니 참고하기 바란다.

Page 97: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 97

(/) 문자 사이에 정의된 정규 표현식은 브라우저에서 컴파일된 후 사용된다. trim 연산

에 사용하는 정규 표현식은 비교적 간단한 정규 표현식이긴 하지만 반복되는 컴파일 과

정 또한 불필요한 작업임에 틀림없다.

코드로 설명하자면 예제 3-42의 코드보다 예제 3-43과 같은 형식의 코드를 권장한다.

이 두 코드의 성능 차이는 정규 표현식이 복잡해질수록 더 커질 것이다.

예제 3-42 Code #1 - 정규 표현식의 컴파일과 실행을 반복하는 경우

for (var i = 0; i < 100; i++) {

str.replace(/^\s+/, "").replace(/\s+$/, "");

}

예제 3-43 Code #2 – 정규 표현식의 컴파일을 최초 1번만 실행하는 경우

var reg1 = /^\s+/;

var reg2 = /\s+$/;

for (var i = 0; i < 100; i++) {

str.replace(reg1, "").replace(reg2, "");

}

여기서 얻은 정규 표현식 최적화 규칙은 ‘컴파일 횟수 축소’다. 최적화 작업의 중요한 부

분이 불필요한 작업을 줄이는 것인 만큼 정규 표현식을 사용할 때도 불필요한 작업을

줄인다면 좀 더 좋은 성능으로 정규 표현식을 사용할 수 있을 것이다. 하지만 정규 표현

식의 복잡도에 따라 컴파일 성능에 차이가 있고, 무한한 확장성을 가진 정규 표현식에

대한 성능 지표를 제공하긴 어렵기 때문에 사용할 정규 표현식과 문자열로 성능을 테스

트하며 최적화하기를 권장한다.

그 외에도 정규 표현식의 개념에 좀 더 깊숙이 다가간다면 다양한 정규 표현식의 요소

와 특성에 기반을 둔 최적화 방법을 찾을 수 있을 것이다.

요약

지금까지 자주 사용하는 자바스크립트의 요소 몇 가지를 선택해 성능 최적화 관점에서

살펴봤다. 이 장에서 설명한 내용을 바탕으로 작은 요소까지도 최적화된 코드로 작성한

Page 98: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

98 자바스크립트 성능 이야기

다면, 작은 부분에서 이룬 성능 향상 효과가 쌓여 코드 전체로 봤을 때 최적화되지 않은

코드보다 더 뛰어난 성능을 보일 수 있을 것이다. 지금까지 설명한 최적화 방법을 간단

히 정리하면 다음과 같다.

• 배열은 [ ]를 이용해 리터럴 방식으로 생성하고 접근자([ ])로 데이터를 할당하는 코드의 성능이 더

좋다.

• 오브젝트 객체는 생성자를 이용하는 방식과 { }를 이용하는 리터럴 방식 가운데 어떤 방법의 성능이

더 좋다고 판가름하기 어렵다. 오브젝트 객체에 데이터를 할당하는 . 연산자와 [ ] 연산자의 성능도

큰 차이가 없다. 오브젝트 객체를 사용할 때는 작성하는 코드의 크기와 주요 대상 브라우저, 코드의

가독성, 유지 보수의 편리성 등을 감안해 적절한 방식을 선택한다.

• 스코프 체인과 프로토타입 체인을 활용하는 코드에서 성능을 향상시키려면 탐색 경로를 줄여 실행

시간을 단축시킨다.

• 반복문 가운데 for-in 구문은 인터넷 익스플로러에서는 성능이 좋았지만 인터넷 익스플로러 외

에 다른 브라우저까지 고려한다면 사용을 자제하는 것이 좋다. 다른 반복문인 for, for-in, while,

do-while 구문의 성능은 비슷하지만 속성의 탐색 경로를 최소화하고 반복 횟수를 최소화해서 성

능을 더 향상시킬 수 있다.

• 조건문은 종류에 따른 성능 차이가 거의 없다. 조건문을 사용할 때는 코드의 가독성, 유지 보수의

편리성, 코드의 크기 등을 고려해 적합한 형태로 작성하고 성능을 직접 테스트해서 확인하는 것이

좋다.

• 문자열은 생성자보다 리터럴 방식을 이용해 생성하는 것이 브라우저에 따른 성능 차이가 크지 않고

성능도 더 좋다. 문자열을 연산할 때는 += 연산보다 Array.join() 메서드로 병합하는 것이 대체로

더 성능이 좋다. 그러나 브라우저의 기능 개선이 계속 이뤄지고 있어 최신 브라우저에서 사용할 때

는 성능을 테스트해서 확인한 다음 선택하기를 권장한다.

• 정규 표현식은 불필요한 탐색 과정을 없애 탐색 횟수를 줄이고 컴파일과 실행을 반복하지 않도록

작성해서 성능을 향상시킬 수 있다. 그 외에도 더 다양한 최적화 방법을 찾을 수 있을 것이다.

단순하고 이해하기 쉬운 코드를 예제로 사용했기 때문에 이 장에서 제시하는 최적화된

코드의 성능 향상 정도는 아주 미미하다. 하지만 ‘티끌 모아 태산’이라는 말이 있듯이 효

율적으로 작성된 작은 구문이 쌓일수록 효율을 생각하지 않고 작성한 코드보다 점점 더

큰 차이로 성능이 나아질 수 있다.

여러 가지를 테스트하며 기록하고 판단한 최적화 기법도 시간이 지나면 새로운 환경

에 맞지 않게 된다. 그러므로 브라우저의 버전이 업그레이드되면 반드시 자주 사용하

는 코드 패턴의 성능을 측정하고 성능 최적화를 위한 올바른 코드 패턴을 다시 정립해

야 한다.

Page 99: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

3장 성능을 높이는 코드 스타일 99

jsMatch를 이용한 성능 측정

jsMatch는 NHN에서 개발한 성능 테스트 도구로, 자바스크립트 구문을 브라우저별로 테스트

해서 성능을 비교한다. NHN에서는 jsMatch를 네이버 개발자센터에 공개해 직접 성능을 테스

트할 수 있게 하고 있다.

• jsMatch : http://jindo.dev.naver.com/jsMatch/

자바스크립트를 실행할 수 있는 브라우저는 굉장히 많고 최근에는 모바일 브라우저도 있기

때문에 사실상 모든 브라우저를 테스트하기에는 한계가 있다. 이런 어려움을 해결할 수 있는

도구로 jsMatch를 개발했다. jsMatch에 성능을 비교할 코드를 입력한 후 비교하기 버튼을

클릭하면 코드의 성능을 비교할 수 있다.

jsMatch는 특정 시간(0.1초)에 도달할 때까지 코드를 실행한 횟수를 측정한 다음 이 값을

이용해 한 번 실행하는 데 걸리는 시간을 계산한다. 하지만 브라우저가 자바스크립트를 실행

할 때마다 시스템이나 기타 환경에 영향을 받으므로 결과가 약간씩 다를 수 있다. 따라서 한

번에 측정을 끝내는 것이 아니라 같은 과정을 20번 반복해서 가장 빠른 결과 값을 가져온다.

성능을 측정한 결과는 다음 그림과 같이 그래프로 표시된다. 자바스크립트 코드를 한 번 실행

할 때 걸리는 시간을 비교하므로 그래프에서 막대의 높이가 낮은 코드가 실행 속도가 빠르고

더 성능이 좋은 코드다. 비교한 코드 가운데 가장 성능이 좋은 코드는 막대를 녹색(이 책에서

는 더 진한 색)으로 표시한다.

Page 100: 자바스크립트 성능 이야기 : NHN은 이렇게 한다!

100 자바스크립트 성능 이야기

이렇게 측정한 결과를 저장해서 테스트 결과를 공유할 수 있는 URL을 얻을 수도 있다. 생성

된 URL을 볼 수 있는 화면 오른쪽에는 모바일 브라우저를 위한 QR코드도 함께 제공되므로

모바일에서 코드의 성능을 비교하기 편하다.