팁&테크

리눅스서버 HTTP 웹 성능 개선하기

이온디 2020.07.02 09:34 조회 수 : 115

관련 URL 1  
관련 URL 2  
관련 URL 3  
관련 URL 4  
소스코드  

웹 성능에 중요한 부분

웹 페이지를 불러오는 데 전반적으로 영향을 미치는 네트워크 수준의 지표

  • 지연 시간 : IP 패킷이 한 지점에서 다른 지점으로 이동하는 데 걸리는 시간. 왕복 시간(Round-Trip Time, RTT) 는 지연 시간의 두 배.
  • 대역폭
  • DNS 조회
  • 연결 시간
  • TLS 협상 시간 : HTTPS 연결 시 SSL(Secure Socket Layer), TLS(Transport Layer Security) 협상이 필요. 왕복 시간이 더 추가


서버 자체의 내용이나 성능에 좀 더 의존적인 지표

  • TTFB(Time To First Byte) : 클라이언트가 웹 페이지 탐색을 시작한 때부터 기준 페이지 응답의 첫 번째 바이트를 수신한 때까지 걸린 시간
  • 콘텐츠 다운로드 시간 : 요청한 개체에 대한 TTLB(Time To Last Byte)
  • 렌더링 시작 시간 : 사용자가 빈 페이지를 바라본 시간
  • 문서 완성 시간 ( 또는 페이지 로딩 시간 )


병목을 유발하는 요소들이 갈수록 증가

  • 바이트 수의 증가 : 페이지 크기, 이미지 크기, 자바스크립트와 CSS의 크기가 증가
  • 개체 수의 증가
  • 복잡도의 증가 : 많은 기능이 추가된 페이지는 렌더링 시간이 길어진다. 모바일 환경은 처리 능력이 떨어지기 때문에 체감이 더 된다.
  • 호스트 수의 증가 : 수많은 참조 호스트들은 추가적인 DNS 조회 시간, 연결 시간, TLS 협상 시간을 의미
  • TCP 소켓 수의 증가 : 이는 호스트당 연결 협상 오버헤드를 증가, 디바이스의 부하를 가중, 네트워크 연결 과부하, 재전송과 버퍼블로트(bufferbloat)로 실효 대역폭 저하를 유발


HOL 블로킹

브라우저는 대개 한 번에 많은 개체를 가져오려고 한다. 단일 연결 상 브라우저는 요청 하나를 보내고 그 응답을 수신한 후에 또 다른 요청을 보낸다. 이러한 여러 요청이나 응답 중 어디엔가 문제가 발생하면 그 요청/응답을 뒤따르는 다른 모든 것들이 막힌다. 이 현상을 HOL 블로킹(Head of line blocking)이라고 한다.


이로 인해 웹 페이지의 전송과 렌더링이 중단될 수 있다.


TCP 혼잡 회피 메커니즘

혼잡 윈도우(Congestion Window) : 수신자가 확인(ACK)하기 전까지 송신자가 전송할 수 있는 TCP 패킷의 수


느린 시작(Slow Start) : 새로운 연결이 네트워크 상태를 감지해 이미 혼잡한 네트워크를 악화시키지 않게 함. 송신자가 ACK를 수신할 때마다 패킷 수를 늘려서 전송이 가능


경기장 효과(Stadium Effect)

수만 명의 사람들이 동시에 경기장과 같은 장소에 있을 때 , 모바일 통신 대역폭이 빠르게 소진된다.


응답 패킷에서는 개체 크기 대비 헤더 크기의 비율이 매우 낮지만, 요청 패킷에서는 헤더가 대부분의 바이트를 차지한다. 업로드 대역폭은 보통 네트워크의 제약을 받는다. 특히 모바일 환경에서 그렇다. 혼잡 윈도우의 크기가 처음부터 충분히 커지지 않아 더 많은 왕복을 유발할 수 있다.


헤더를 압축해 요청 크기를 줄이면, 경기장 효과에 도움을 주고 시스템 부하도 줄어듦.


DNS 조회 최적화하기

  1. 도메인/호스트이름의 수 제한하기
  2. 조회 지연 시간을 줄이기
  3. 초기 HTML이나 응답에 대해 DNS prefetch 활용하기. 초기 HTML을 내려받아 처리하는 동안 그 페이지에 있는 특정 호스트이름들의 DNS 조회를 시작할 것

1

<link rel="dns-prefetch" href="//ajax.googleapis.com">

cs


DNS의 고정적인 오버헤드를 최소화시켜 줌


TCP 연결 최적화하기

  1. preconnect 활용하기. 필요하기 전에 미리 연결을 수립함으로써 폭포수 임계 경로(waterfall critical path)에서 연결 시간을 제거
  2. 조기 종료(early termination)을 사용. 콘텐츠 전송 네트워크(Content Delievery Network, CDN)를 활용하면, 요청하는 클라이언트와 가까이 위치한 인터넷 경계(edge)에서 연결을 종료시킬 수 있음. 왕복 지연을 최소화 해줌
  3. HTTPS 최적화 시 최신 TLS 모범 사례를 실행하기


1

<link rel="preconnect" href="//fonts.example.com" crossorigin>

cs


리다이렉션 피하기

  • CDN 활용하여 클라이언트 대신 '클라우드에서' 리다이렉션 수행하기
  • 동일한 호스트 리다이렉션일 경우 대신, 웹 서버에서 'Rewrite Rules'를 사용하여 원하는 자원으로 연결시키기


리다이렉션은 종종 검색 엔진 최적화(Search Engine Optimization, SEO)에서 단기 검색 결과 순위나, 백엔드 정보 레이아웃 변경을 피하는데 사용한다. SEO의 이득만큼의 가치가 있는지 판단할 것


클라이언트에 캐싱하기

TTL(Time To Live) : 개체를 얼마나 오랫동안 캐싱할지를 브라우저에게 알려줌. 최적의 TTL을 찾기위한 지침은 다음과 같다.


Chache-Control HTTP 헤더의 max-age (초 단위) 키를 사용하거나, Expires 헤더로 설정


  • 이미지 또는 버전이 관리되는 콘텐츠와 같은 정적 콘텐츠는 영구적으로 클라이언트에 캐싱할 수 있음. 그러나 이후 콘텐츠를 다시 원본으로부터 가져와야 할 때가 있기 때문에, 실제 TTL은 유저 환경에 달려있다.
  • CSS/JS와 맞춤형 개체의 경우, 평균 세션 기간의 2배 정도 캐싱. 통계적으로 대부분의 사용자가 해당 웹사이트를 돌아다니는 동안 로컬에서 자워늘 가져올 수 있을 만큼 길고, 다음 세션에서 네트워크에서 새로운 콘텐츠를 가져오게 할 만큼 짧다.
  • 그 외, 요구 사항을 근거로 최적의 결정


네트워크 경계에 캐싱하기

네트워크 경계에 캐싱할 경우, 모든 사용자가 클라우드 내의 공유 캐시의 혜택을 얻음. 더 빠른 경험을 제공하고 서비스 인프라에서 많은 양의 트래픽을 덜 수 있다.


  • 여러 사용자 간 공유 가능한 자원
  • 어느 정도 노후도(stateness)를 수용할 수 있는 자원


유저의 개인정보같은 민감한 정보는 캐싱하면 안 된다.


조건부 캐싱

캐시 TTL이 만료될 시, 클라이언트는 서버에게 요청한다. 만약 그 응답이 캐싱된 사본과 동일하다면, 굳이 할 필요가 없다. HTTP에서 조건부 캐싱을 제공한다.


어떤 개체가 자주 변경되지 않지만, 그 개체의 최신 버전을 빨리 사용할 수 있게 하는 것이 중요한 경우에 사용하면, 대역폭과 성능 면에서 유리


  • If-Modified-Since HTTP 헤더를 요청에 포함. 최신 콘텐츠가 헤더에 명시된 시점 이후에 갱신된 경우에만 전체 콘텐츠를 반환. 그렇지 않으면 응답 헤더에 새 타임스탬프 Date 를 포함한 304 응답을 반환.
  • 개체를 식별하는 고유한 엔티티 태그, ETag 를 요청에 포함. 서버가 현재 ETag와 요청 헤더의 ETag와 비교한 후, 동일하면 304를 반환, 다르면 전체 콘텐츠를 반환.


대부분의 웹 서버에서 이미지와 CSS/JS에 이 기법을 적용. 다른 캐싱된 콘텐츠에도 적용하는지 확인해봐야 함.


압축과 축소화

텍스트 형태의 모든 콘텐츠(HTML, JS, CSS, SVG, XML, JSON, 폰트 등)는 압축(Compression)과 축소화(minification)의 이점을 볼 수 있다.


더 적은 바이트 수는 더 적은 왕복을 의미하고 시간도 덜 소요됨을 의미한다.


축소는 쓸데 없는 바이트를 줄여준다.

압축은 축소된 개체를 한 번 더 줄일 수 있다. 손실 없이 복원할 수 있는 알고리즘을 사용해 개체의 크기를 줄인다. 흔히 gzip과 디플레이트(deflate)를 사용하고, 최근에 등장한 브로틀리(Brotli) 등의 방식이 있다.


CSS/JS 차단을 피하기

CSS는 화면에 콘텐츠를 렌더링하는 방법과 위치를 클라이언트 브라우저에게 알려주는 역할을 한다. 화면에 그리기 전에, 모든 CSS를 내려받아 해석해야 한다. 브라우저의 프리 파서(pre-parser) 로 CSS를 어디에 두든 상관은 없지만, 문서의 헤드 섹션, JS나 이미지를 가져와 처리하기 전인 HTML 앞 부분에 두는 것을 권장한다.


JS는 HTML 내의 코드가 위치한 지점에서 반입되고 파싱되고 실행된다. 브라우저가 이 작업을 완료할 때까지 해당 JS가 위치한 지점 이후에 있는 모든 자원은 다운로드와 렌더링이 차단된다. 경우에 따라서 특정 JS를 내려받아 실행하는 동안 나머지 HTML 부분의 파싱과 실행을 차단하는 것이 나을 수도 있다.



특히 태그 관리자(tag-manager)를 설치하는 경우 또는 존재하지 않는 엔티티(entity)에 대한 참조나 경쟁 조건을 피하기 위해 JS를 먼저 실행해야 하는 경우다.


그러나 이런 기본 차단 동작은 불필요한 지연을 유발하고, 단일 장애점(single point of failure)를 유발할 수 있다.


  • JS의 사용 여부를 주기적으로 재확인하기. 더 이상 필요 없는 JS를 다운로드 받고 있는 것은 아닌지 확인하고, 필요 없는 JS를 제거하는 것이 가장 빠른 대처다.
  • JS의 실행 순서는 중요하지 않고 onload 이벤트가 시작되기 전에 JS가 실행해야 한다면, async 속성을 설정하기. 이는 HTML 파싱과 동시에 JS를 내려받으며, 전반적인 사용자 경험을 대폭 개선할 수 있다. document.write 를 사용할 경우, 페이지 표시가 중단될 수 있기 때문에 주의 깊게 테스트해야 한다. 

  • JS의 실행 순서는 중요하고 DOM이 로딩된 후에 JS를 실행해도 된다면, defer 속성을 사용하기

  • JS가 초기 화면 구성에 중요하지 않다면, onload 이벤트가 발생한 후에 JS를 가져와서 처리하는 것이 좋다.

  • 메인 onload 이벤트를 지연시키고 싶지 않다면, 메인 페이지와 별개로 처리되는 iframe 으로 JS를 가져오는 것을 고려할 수도 있다. 그러나 iframe을 통해 내려받은 JS는 메인 페이지의 요소(element)에는 접근할 수 없다.


1

<script async src="/js/myfile.js">

cs

1

<script defer src="/js/myfile.js">

cs


이미지 최적화하기

이미지의 크기는 시간이 지날수록 증가하는 추세다.


  • 메타데이터(위치, 타임스탬프, 크기 등의 정보)를 제거하기. 저작권과 ICC 프로파일 데이터는 제거하면 안 된다. PNG 의 경우 보통 10% 가량 줄어든다.
  • 사용자의 디바이스, 네트워크 상태, 기대 화질에 맞추어 이미지를 제공하기. 이미지 오버로딩(overloading) : 이미지의 원본 크기가 브라우저의 창 크기를 넘거나 이미지 해상도가 디바이스 화면의 범위를 넘어, 브라우저에 의해 이미지의 크기가 자동으로 줄어드는 것. 브라우저가 이 작업을 하면, 대역폭뿐 아니라 CPU 자원을 낭비하게 되며, 휴대용 디바이스에서 큰 영향을 미친다.




출처 : 러닝 HTTP/2


출처: https://itchipmunk.tistory.com/261 [다람쥐의 일상]