기본적인 웹 사이트 최적화 방법 (3) – 렌더링 성능 향상

등록일: 2014. 11. 13

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

  • 박재성, 심상민, 양정권, 황준호 지음
  • 396쪽
  • 18,000원
  • 2012년 09월 18일

렌더링 성능 향상

전체적인 로딩 속도는 동일한데 빈 페이지가 계속 보이다 갑자기 콘텐츠가 나타나는 페이지가 있고, 처음부터 콘텐츠가 조금씩 보이며 화면이 빠르게 나타나는 페이지가 있다. 콘텐츠가 조금씩 보이는 화면이 더 빠르게 느껴지는 것은 체감 속도 때문이다. 렌더링 성능 향상의 목표는 페이지를 요청했을 때 사용자가 대기하는 시간을 최대한 줄여서 이 체감 속도를 높이는 것이다.

자세한 내용을 알아보기 전에 브라우저가 어떤 순서로 마크업을 파싱해서 화면에 보여주는지 기본적인 흐름을 살펴보자.

  1. HTML 파싱과 DOM 트리 구성 사용자가 페이지를 요청하면 네트워크를 통해 마크업을 받아 온다. 그러고 나서 마크업 문자열을 토큰 형태로 잘라서(Tokenizer) 트리를 구축하고 파싱 작업을 시작한다. 그런 다음 DOM 트리(DOM Tree)1를 생성한다.

  2. 렌더 트리 구성(DOM+스타일 규칙) DOM 트리를 생성한 다음 바로 화면을 그리지는 않는다. 스타일시트의 정보를 적용해야 하기 때문이다. DOM 트리 정보와 스타일시트의 스타일 규칙을 결합해 렌더 트리( Render Tree)를 만든다. display:none 속성처럼 DOM 트리에는 있지만 화면에 보이면 안 되는 요소를 걸러낸 결과가 렌더 트리다.

  3. 렌더 트리의 배치 최종적으로 스타일 규칙에 따라 각 요소를 화면의 어디에 배치할지 좌표를 설정한다.

  4. 렌더 트리 그리기 요소의 좌표가 설정되면 브라우저에 순차적으로 화면을 그린다. 이때 사용자는 화면을 조금씩 보게 된다.

기본적으로 위와 같은 단계를 거치는데 실수로 또는 잘 몰라서 이 흐름을 방해하는 방법으로 페이지를 개발하면 성능에 영향을 주게 된다. 어떻게 하면 성능에 영향을 주지 않고 더 빠르게 화면을 보여줘 쾌적한 서비스를 제공할 수 있는지 알아보겠다2.

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

스타일시트 파일은 페이지 제일 위쪽에 놓고 자바스크립트 파일은 페이지 맨 아래쪽에 놓아야 한다.

브라우저 렌더링 단계에 따르면 사용자에게 화면을 보여 주기 전에 렌더 트리를 생성해야 하는데, 이때 스타일시트 파일이 반드시 필요하다. 스타일시트 파일을 최대한 빨리 다운로드해야 하는 이유다. 그리고 파이어폭스나 인터넷 익스플로러는 스타일시트 파일을 모두 다운로드할 때까지 화면을 렌더링하지 않고 기다린다. 그래서 스타일시트 파일은 페이지의 제일 위쪽인 <head> 태그와 </head> 태그 사이에 놓아서 최대한 빨리 다운로드해야 한다.

자바스크립트 파일을 페이지 아래에 놓아야 하는 가장 큰 이유는 파일을 다운로드해서 실행하기 전까지 브라우저가 DOM 파싱도 중지하고 아무것도 렌더링하지 않기 때문이다. 자바스크립트에는 document.write() 메서드가 있어 마크업을 렌더링하는 도중에도 DOM을 추가할 수 있다. 이로 인해 이미 서버 통신을 완료하고 필요한 구성 요소를 모두 브라우저에 가져왔음에도 자바스크립트를 수행하느라 렌더링이 멈추게 된다. 이때 사용자에게는 마치 화면이 멈춘 것처럼 보여 체감 속도가 느려진다.

따라서 자바스크립트 파일은 </body> 태그 바로 위에 놓는 것이 좋다.

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

네이버 캘린더나 메일, N드라이브와 같은 도구형 서비스는 화면 전체를 자바스크립트(AJAX 통신)로 그리고 컨트롤한다. 동적인 웹 사이트에서 화면을 그리는 단계는 일반적으로 다음과 같다.

  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 요청을 최소화한다.

마크업 최적화

마크업을 최적화해 사용자가 실제 느끼는 체감 속도를 빠르게 하는 방법을 소개하겠다. 마크업 최적화의 목표는 빈 페이지가 한참 있다가 전체 화면이 한꺼번에 나타나는 것이 아니라 영역별로 차츰 렌더링하게 하는 것이다. 끊김 없는 처리를 위한 여러 가지 방법이 있지만 여기서는 <table> 태그 처리에 대한 특성을 살펴보겠다.

DHTML3 기술이 발표된 이후에는 페이지 전체의 레이아웃을 <table> 태그로 구성하는 방법을 사용하지 않게 됐다. 그러나 어쩔 수 없이 <table> 태그를 사용해야 하는 경우도 있고 기존에 만들어 둔 페이지를 개편하지 못한 경우도 있다.

인터넷 익스플로러에서는 <table> 태그를 렌더링할 때 표 안에 있는 텍스트와 이미지 등을 모두 파싱할 때까지 화면 표를 그리지 않는다. 만약 <body> 태그부터 시작해서 전체 화면을 그리는 데 <table> 태그를 사용했다면, 한참 있다가 전체 화면이 한번에 보일 것이다. 반면에 파이어폭스에서는 <table> 태그가 모두 완료되기 전에도 표 안에 있는 각 요소가 보이기 때문에 부분적인 렌더링이 가능하다.

여기서는 특정 브라우저보다는 모든 브라우저에 대응할 수 있는 렌더링 방법을 정리하겠다.

  1. HTML 코드가 올바른지 검사한다. http://validator.w3.org/ 사이트를 이용하면 손쉽게 검사할 수 있다.
  2. 태그의 중첩을 최소화한다. 다음과 같은 화면을 그리려면 태그가 어느 정도나 중첩됐을까? 외곽 테두리와 알림 문구, 이미지 태그 정도일 것이라 예상할 수 있지만 실제로는 7개 정도가 필요하다. 각 테두리에 그라데이션 효과를 주기 위해 추가로 중첩된 마크업이 필요하다. 이런 디자인은 디자이너와 협의해 속도를 위해서 좋은 방법을 찾아야 한다.

    다음에 나오는 코드를 브라우저 주소창에 입력하면 해당 페이지의 전체 태그 개수를 알 수 있다.

    javascript:alert(document.getElementsByTagName("*").length);void(0);
    

    전체 태그의 개수(보통 1000개 이하를 권장)를 줄이는 것도 중요하지만 중첩된 태그를 최소로 하는 것이 더 중요하다. 전체적으로는 태그의 개수를 줄이고 부분적으로는 중첩된 태그를 최소화해 간결하게 디자인하는 것이 렌더링 속도를 높이는 방법이다.

  3. 본문 전체를 <table> 태그로 감싸지 않는다.
  4. 되도록 <div> 태그와 스타일시트를 이용해 레이아웃을 구성한다.
  5. 이미지 크기가 너무 크지 않게 한다.

NHN의 마크업 복잡도 체크 도구 - Markup Complexity Checker

NHN에서는 마크업의 복잡도를 체크하는 도구인 Markup Complexity Checker를 개발해 최적화에 활용하고 있다. Markup Complexity Checker은 http://html.nhndesign.com/markup_tools/complexity에 공개돼 있어 누구나 이용할 수 있다. 복잡도를 체크할 웹 페이지의 URL을 입력하거나 마크업 코드를 직접 입력하면 요소의 개수, 평균 중첩도 등을 확인해서 보여 준다.


  1. DOM 트리에 대한 자세한 내용은 http://www.w3schools.com/htmldom/dom_nodetree.asp를 참조한다. 

  2. 렌더링 성능을 향상시키는 방법은 “5. UI 스레드와 타이머의 활용(127페이지)”과 “6. DOM 스크립팅(153페이지)”에서 더 자세히 설명한다. 

  3. DHTML에 대한 자세한 설명은 http://ko.wikipedia.org/wiki/DHTML을 참조한다.