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

HTTP 요청 최소화

HTTP 요청 최소화는 최적화에서 가장 기본이면서도 중요한 부분이다. 웹 사이트는 주로 마크업, 이미지, 스타일시트, 자바스크립트 등으로 구성된다. 각 구성 요소는 모두 웹 서버에 있다. 이 구성 요소를 사용자의 컴퓨터로 가져오는 데는 네트워크 비용이 든다. 네트워크 비용은 곧 응답 시간으로 이어진다. 그러므로 다운로드 해야 하는 구성 요소의 개수를 줄이는 것은 가장 효과가 크고 중요한 최적화 방법이다.

CSS 스프라이트

순수한 HTML 마크업만으로는 표현에 한계가 있기 때문에 어쩔 수 없이 이미지를 사용한다. 하지만 이미지를 많이 사용하면 HTTP 요청이 많아질 수 밖에 없다. 이미지를 많이 사용하면서도 HTTP 요청을 최소화하는 방법 가운데 하나가 CSS 스프라이트 기법이다.

CSS 스프라이트 기법은 이미지 여러 개를 하나로 만들고 스타일시트에서 background-position 속성을 설정해 필요한 부분의 이미지만 보여 주는 기술이다. 여러 이미지를 하나의 이미지로 합치기 때문에 HTTP 요청 횟수를 줄일 수 있고, 이미지의 컬러 테이블과 같은 메타데이터를 하나로 합칠 수 있어 파일 크기가 줄어든다.

CSS 스프라이트 기법은 이미지를 관리하기 어렵고 웹 접근성을 나쁘게 하는 요소가 있다는 단점이 있다. 하지만 많은 양의 이미지를 사용한다면 많은 시간을 줄일 수 있다.

헤더에 만료 날짜 추가

헤더에 만료 날짜를 추가하는 이유는 웹 페이지를 구성하는 여러 요소들을 사용자의 컴퓨터의 캐시에 저장해서 재사용하기 위해서다. 사용자가 처음 웹 페이지에 방문하면 만료 날짜가 설정된 요소를 사용자 컴퓨터에 저장한다. 이후 같은 웹 페이지에 다시 방문하면 유효한 요소는 서버에 요청하지 않고 사용자 컴퓨터에서 바로 읽어 온다.

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

캐시에 저장된 파일에는 만료 날짜가 없는 파일도 있고 만료 날짜가 있는 파일도 있다. 모든 구성 요소에 무조건 만료 날짜를 설정하는 것이 아니라 특정 기간까지 변경되지 않아도 서비스에 문제가 없는 요소에만 적용해야 한다.
만약 만료 날짜 전에 수정 사항이 있어 파일을 변경해야 한다면 파일 이름을 변경하거나 파일 이름 뒤에 쿼리스트링을 추가해 새로 추가된 파일임을 알려야 바로 반영된다.

1
2
3
4
5
6
7
<script type="text/javascript" src="test.js"></script>

// 파일 이름을 변경
<script type="text/javascript" src="test_0202.js"></script>

// 쿼리스트링 추가
<script type="text/javascript" src="test?0202.js"></script>

브라우저에서 캐싱된 파일을 이용할지 서버에 요청할지 판단하는 기준은 컬럼 이름을 기준으로 이름과 인터넷 주소다. 그렇기 때문에 파일 이름이나 파일의 주소를 바꾸지 않으면 계속 같은 파일로 인식하고 사용자 컴퓨터에 있는 파일을 로딩한다.

자바스크립트 파일 통합

기능이 향상됨에 따라 자바스크립트 파일은 개수도 많아지고 크기도 커지고 있다. 이럴 때 성능을 높이는 방법은 여러 개의 자바스크립트 파일을 하나의 파일로 합쳐 파일 개수를 최소화하는 것이다. 웹 사이트의 성능을 개선할 때는 파일의 용량보다 파일의 개수가 중요하다. 아주 용량이 작은 파일이라도 원격 서버에서 가져와야 한다면 네트워크 비용이 든다.

파일 크기 최소화

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

점점 커지는 자바스크립트 파일과 스타일시트 파일의 크기를 줄이는 가장 효과적이고 쉬운 방법은 파일을 압축하는 것이다. 아파치 웹 서버에서 파일을 압축하는 대표적인 인코딩 방식에는 Gzip과 deflate 두 가지가 있는데, deflate 방식은 지원하지 않는 브라우저가 많고 효과도 떨어지기 때문에 대부분 Gzip 방식을 사용한다.

압축 전송 흐름 및 확인

압축 전송은 다음과 같은 순서로 진행된다. 클라이언트에서 some.js 라는 파일을 만났을 때를 가정해보자.

  1. 클라이언트에서 헤더 정보로 인코딩 여부를 물어본다.

    Accept-Encoding: gzip, deflate (Header)

  2. 서버에서 헤더 정보로 인코딩 여부를 알려준다. 인코딩된 요소라면 다음과 같이 응답이 온다.

    Content-Encoding: gzip

  3. 클라이언트에서 인코딩된 요소를 받음과 동시에 압축을 해제한다. 이때 추가적인 CPU 연산 비용이 들어간다. 여기서 발생하는 CPU 연산 비용도 무시할 수 없기 때문에 압축할 파일과 크기를 잘 설정해야한다. 보통 스타일시트 파일과 자바스크립트 파일을 압축한다. 그리고 파일 크기가 작으면 속도 개선 효과보다 CPU 연산 비용이 더 들기 때문에 파일 크기가 일정한 크기 이상인 경우에만 압축하는 것이 좋다. 파일 크기가 1~2KB 이상일때 압축할 것을 권장한다.

압축 전송의 효과

Gzip으로 압축해 전송하면 평균 70% 정도 파일 크기가 작아지는 효과를 볼 수 있다. 모바일 환경과 같이 네트워크 환경이 불안한 상황에서는 더욱 효과적인 기술일 것이다.
파일의 공백 및 주석을 제거하고 Gzip 으로 압축을 진행하면 파일 크기를 최소화 시킬 수 있다.

쿠키 크기 최소화

필요한 정보를 저장하는 가장 손쉬운 방법이 최상위 도메인을 이용해 쿠키를 설정하는 방법이다. 이렇게 되면 자바스크립트 파일이나 스타일시트, 이미지등 쿠키 정보가 필요 없는 구성 요소를 요청할 때도 헤더 정보에 쿠키가 포함된다. 즉 헤더를 전송할 때 데이터 크기가 커진다.

쿠키의 크기를 줄이는 기본적인 방법

  • 지속적인 관리로 사용하지 않는 쿠키는 삭제한다.
  • 쿠키를 설정할 때 최상위 도메인은 되도록 사용하지 않는다.
  • 쿠키 정보가 필요없는 파일은 별도의 도메인으로 서비스한다.

렌더링 성능 향상

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

브라우저 파싱 순서

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

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

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

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

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

스타일시트 파일은 페이지 제일 위쪽에, 자바스크립트 파일은 페이지 맨 아래쪽에 놓아야 한다.
브라우저 렌더링 단계에 따르면 사용자에게 화면을 보여 주기 전에 렌더 트리를 생성해야 하는데, 이때 스타일시트 파일이 반드시 필요하다. 스타일시트 파일을 최대한 빨리 다운로드 해야 하는 이유다.

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

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

동적인 웹 사이트에서 화면을 그리는 단계는 일반적으로 다음과 같다.

  1. 사용자가 페이지를 요청
  2. 마크업을 다운로드해 렌더링 시작 (화면을 구성하는 레이아웃만 있고 실제로 보여줄 데이터는 나중에 AJAX 요청을 통해 받은 다음 그려야함)
  3. 자바스크립트 다운로드와 렌더링이 끝난 후 onload 이벤트가 발생
  4. onload 이벤트가 발생한 다음에 AJAX 통신을 실행하고 데이터를 화면에 그린다.
  5. 화면 완성

이 과정에는 두 가지 큰 문제점이 있다. AJAX 통신을 사용하지 않는 방법으로 페이지를 개발했다면 3번 단계에서 사용자는 화면을 보게 된다. 그런데 5번 단계가지 가서야 사용자는 최종 화면을 볼 수 있다. 또 다른 문제는 렌더링이 반복된다는 것이다. 1~3번 단계까지 전체 화면을 한 번 그리고 4~5번 단계에서 화면을 한 번 더 그린다.

초기 렌더링 시에 마크업 전체를 서버에서 보내는 방식으로 개발하여 체감 속도를 높일 수 있다. 1~3 단계에서 전체 화면과 데이터가 있는 화면을 모두 그리는 것이다. 그리고 사용자의 행동이 있을 때 AJAX 요청을 실행해서 데이터를 받은 다음 화면을 그리게 한다.

참조: 자바스크립트 성능 이야기

댓글

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.