개발노트

1. 웹성능 최적화 본문

웹 성능 최적화

1. 웹성능 최적화

aloha2jh 2021. 9. 10. 13:09
1. 웹성능 최적화의 필요성
2. 웹성능 테스트 방법
3. 성능예산
4. 웹최적화 세가지 방법
5. 브라우저 performance timing 속성으로 성능 측정

 

웹성능 최적화의 필요성

 

웹성능 

웹성능은 페이지가 로드되는 속도를 의미한다

 

3초의 법칙

로딩이 0~2초내로 완료되었을때 구매율이 가장 높았고, 3초내에 로딩되지 않을경우 53% 이상 사람들이 이탈율이 높게 나온다는 연구 결과가 있다.  이를 3초의 법칙 이라고도 한다.

SEO 검색엔진 최적화가 될경우 다른 사이트에 비해 자주 상단에 노출된다.

이처럼 성능 최적화 또한 신뢰성과 재방문율에 있어서 효과적일 것임.

 

웹 성능 최적화 Web Performance Optimization WPO

한국처럼 인터넷 전송 속도가 빠른 나라에서는 웹성능 최적화가 큰 관심을 받진 못했으나,

글로벌 서비스를 지향하며 진행하는 경우, 웹성능 최적화 기술에 주목하고 있다.

 

웹성능에 영향을 주는 환경

1. 사용자 환경 2.전달 환경 3.공급자환경 4.디바이스 5.전용선,유선망,모바일망 6.백엔드&프론트엔드

 

 

폰트 같은 경우 CDN 으로 가져오거나, 한번 다운받았을 경우 브라우저 캐시를 사용하는것도 좋은 방법


웹성능 테스트 방법

 

 

크롬 개발자 도구

캐시비우기 새로고침 network 탭에서 확인

 

 

 

 

Web Page Test (WPT)

무료로 웹 사이트 로딩 속도 테스트할수 있는 사이트

https://www.webpagetest.org/

 

0. security score - 보안분석 XSS(교차사이트스크립팅), HTTP strict-transport-security 업격보안정책 등 을 적용 했는가?

1. first byte time - 웹서버에서 받은 콘텐츠의 첫번째 바이트가 얼마 만에 도착 했는가?

2. keep-alive enabled - TCP연결을 재사용하기 위한 keep-alive가 설정되어 있었는가?

3. compress transfer - 스크립트 파일이 content-encoding으로 압축되어 있었는가?

4. compress images - 이미지를 압축해 최적화 했는가?

5. cache static content - 정적 파일에 브라우저 캐시가 설정되어 있었는가?

6. effective use of CDN - CDN을 효과적으로 적용했는가?

 

 

 

 

 

 

 

구글 PageSpeed

https://developers.google.com/speed/pagespeed/insights/

 

 

 

 

 

FCP (First Contentful Paint) 첫번째 텍스트 또는 이미지가 표시되는데 걸린 시간

SI (Speed Index) 페이지 콘텐츠가 얼마나 빨리 표시되는지에 대한 정보

LCP (Larget Contentful Paint) 가장 큰 텍스트 또는 이미지가 표시된 시간

TTI (Time to Interactive) 사용자가 페이지와 완전히 상호작용 할수 있을때 까지의 시간

TBT (Total Blocking Time) FCP와 TTI 사이 모든 시간의 합. 작업지속시간이 50ms 초과하면 밀로차 단위로 표현됨

CLS (Cumulative Layout shift) 표시 영역 안에 보이는 요소들이 얼마나 이동하는지에 대한 정보

 

 

 

 

 

 

 

 

 

크롬 개발자도구 Lighthouse 

 

FCP와 LCP 차이가 있어 처음 표시되는 콘텐츠와 가장 큰 콘텐츠의 로딩시간 차이가 나는걸 확인 가능

 

 


 

성능예산

 

웹개발팀의 성능 관련목표 예시

- 메인 페이지의 모든 object file 크기는 10MB를 넘지 않도록, 미만으로 제한한다.

- 모든 웹 페이지의 각 페이지내 자바스크립트 크기는 1MB를 넘지 않아야 한다.

- LTE 환경에서 모바일 기기의 Time to Interactive 는 5초 미만이어야 한다.

 

초기 사이트 설계,개발시에 웹디자이너는 고해상도 이미지를 몇개까지 추가할것인지, 어떤 크기의 웹폰트파일을 사용할건지 결정시에 성능예산을 고려한다.

초기에 갸늠하기 쉽지 않으므로 비슷한 산업군의 사이트 또는 경쟁사 사이트를 참고한다. 혹은 스마트폰 LTE 환경에서 로딩하는 시간이 몇초 미만일것 이정도의 목표를 세운다.

 

성능예산3분류

1. 경량 기반 지표 - image, script, font 등 필요한 요소의 한계값

2. 시간 기반 지표 - FCP ,TTI

3. 규칙 기반 지표 - page speed, web page test, google Lighthouse의 성능 점수

 

 


 

웹 최적화

 

웹 최적화 세가지 방법

1. 프론트엔드 최적화 - html, js, css, image 다운로드, 로딩, 렌더링 최적화

2. 백엔드 최적화 - 웹어플리케이션서버, 데이터베이스, 로드밸런싱, DNS 서버

3. 프로토콜 최적화 - HTTP, HTTPS 프로토콜 최적화로 웹 콘텐츠를 더 빠르게 요청하고 응답하도록 업그레이드 하는 과정

 

프로토콜 최적화

1. TCP/IP (Transmission Control Protocol/ Internet Protocol) 

2. HTTP (HTTP Protocol)

 

 

1.TCP/IP

TCP/IP란? 인터넷의 기본통신언어로 데이터 패킷을 컴파일하고 올바른 위치로 전송하여 인터넷을 통해 다른 컴퓨터와 통신이 가능한 것이다. 네트워크 패킷은 전송되는 데이터 단위이다. TCP/IP에는 두개의 계층이 있으며 최상위 계층인 TCP는 많은 양의 데이터를 가져와 패킷으로 컴파일한 다음 동료 TCP계층에서 수신하도록 전송하여 패킷을 정보/데이터로 바꾸는 역할을 한다. IP는 맨아래 계층으로 정보패킷을 올바른 위치로 보내고 받을수 있도록 한다. 지도로 비유하면 패킷GPS역할을 한다 자동차로 비유하면 도로 표지판으로 올바른 목적지로 전달하는 역할을 하는것

 

TCP네트워크의 대표적인 성능 지표는 대역폭과 지연시간

대역폭 - 특정시간 얼마나 많은 네트워크 트래픽을 보낼 수 있는지, 시간당 전송량을 의미한다.

(예를들어 크기가 큰 이미지 파일을 다운로드 하려면 완료 시간은 클라이언트와 서버 사이 대역폭에 영향을 받는다)

 

지연 시간 - 클라이언트와 서버간 콘텐츠를 전달하는데 걸리는 물리적인 시간을 의미한다.

(일반적으로 클라이언트 서버 사이 요청, 전달, 응답까지 걸리는 시간)

 

서버와 클라이언트 두 호스트를 왕복하는데 걸리는 지연 시간은 RoundTripTime(RTT) 라고 하며 이 값이 게임, 화상채팅에 영향을 준다.

성능요소 해결하는 TCP기술들

 

(1) TCP 혼잡제어 (congestion control) - TCP 네트워크 통신량 조절

TCP 혼잡붕괴(congestion collapse) - TCP네트워크 통신량이 실제 처리량보다 많아서 문제가발생하는것

패킷을 보내는 쪽에서 수용할수 있는 양만큼의 패킷을 보내는것. 느린시작, 빠른재전송, 흐름제어 등이 있다.

 

 

2. HTTP 프로토콜

웹은 http 프로토콜 통해 전달되므로 http 성능에 영향을 받는다.

http 연결 재사용은 하나의 tcp 연결을 사용하여 여러개의 http요청/응답을 받는 개념 

<-> 매 요청/응답 마다 새로운 요청을 여는것과 반대되는 개념

const httpAgent = new http.Agent({ keepAlive: true });
const httpsAgent = new https.Agent({ keepAlive: true });

const instance = axios.create({
  httpAgent,  // httpAgent: httpAgent -> for non es6 syntax
  httpsAgent,
});

 

서버에 연결된 모든 클라이언트의 TCP 연결이 늘어나면 자원이 고갈되어 많은 클라이언트가 접속할때 대처할수없는 문제가 발생할수 있으므로 메인 페이지 같은 많은 클라이언트가 접속하는 페이지는 서버의 성능을 고려해서 지속적 연결기능을 사용할지결정 해야함

 

반대로 복수개의 http 요청/응답을 병렬로 동시에 처리하기 위한 파이프라이닝 기술을 사용하려면 http지속적연결 기능이 지원되어야 한다

단일시간 동안 TCP연결 수를 줄여 서버의 cpu 메모리자원 절약, 네트워크 혼잡,지연 줄이는 장점이 있음

 

 

 

 

3. DNS(Domain Name System)

인터넷 호스트명을 클라이언트와 서버가 이해할수 있는 IP주소로 변환해주는 시스템

 

최근 웹사이트는 자신의 웹 서비스 콘텐츠 뿐 아니라 다른 웹 서비스의 콘텐츠를 호출해서 사용한다 

오픈소스 이용이 활성화 되며 모듈을 무상으로 지원하는 서비스를 호출하여 사용하기도 한다 ex)CDN, google font

naver
etoos

 

해당 페이지에서 사용중인 도메인 호스트명 리스트.

그 도메인에서 어떤 콘텐츠를 가져왔는지 확인이 가능하다

 

 

 

웹성능 최적화하는 도메인 운용방법

(1) 상위 도메인(top level domian)동일 시키기

많은도메인 호스트명 사용시 DNS 질의가 늘어나 응답시간이 길어지고 웹성능에 영향을 미친다.

직접 개발한 내부 서비스에 도메인분할을 하고 싶다면 상위도메인을 동일하게 해서 DNS질의를 최대한 적게 만드는 것을 권장.

 

(2)HTML의 DNS 프리패치(prefetch) 

웹페이지에 사용된 도메인의 DNS를 조회하는 시간이 좀더 빨라진다. 

DNS프리패치란? 하나의 웹페이지에 다수의 도메인 호스트명이 섞여 있을 때, 웹 문서 페이지를 여는 시점에 멀티스레드 방식으로 미리 DNS를 조회해 빠르게 IP주소를 불러오는 기술 

(사용자가 링크를 클릭하기 전에 웹사이트의 IP주소를 확인하는 작업 )

-너무 많은 도메인을 프리패치 하면 오히려 성능저하를 가져오니 10개 이상은 사용하지 않는다. 2-4개가 적당

-같은 도메인주소를 prefetch, preconnect 하면 안됨

- 미사용 DNS를 prefetch 하는건 낭비

-dns prefetch속성 대한 지원브라우저 확인할것

 

(+ 브라우저 성능 개선) 

성능최적화라기보다, 브라우저가 처리하는 일의 우선순위를 분산하고 미리 실행하도록 해서 응답속도를 높이는 방법

<link rel="dns-prefetch" href="https://example.com" />
<link rel="preconnect" href="https://example.com" />

<link rel="prefetch" href="/style.css" as="style" />
<link rel="preload" href="/style.css" as="style" />

<link ref="prerender" href="https://example.com/about.html" />
<link rel="modulepreload" href="/script.js" />

dns-prefetch - 서버 ip 주소에 대한 DNS요청 미리 작성하도록 브라우저에 지시

preconnect - 브라우저에 미리 서버에 대한 연결 수행하도록 지시

prefetch - 순위가 낮은 백그라운드에서 리소스 미리 로드하고 캐시

preload - 우선순위 높은 백그라운드에서 리소스 미리 로드

prerender - 백그라운데에서 지정된 펭디지 미리로드

modulepreload - js모듈 스크립트를 다운로드, 캐시 및 컴파일하도록 지시

 

 

http://superjang.com/archives/2956

 

Queuing
요청이 큐 된다면

  • 해당 요청은 script/style 같은 리소스 보다 낮은 우선순위로 고려되어 랜더링 엔진에 의해 연기되었다. 이미지가 종종 큐가된다.
  • 해당 요청이 사용불가한 TCP 소캣 확보를 기다리기 위해 홀드되었다.
  • HTTP 1. 사용시 브라우저가 기본적으로 허용하는 6개의 TCP Connection 때문에 해당 요청이 홀드되었다.
  • 시간이 캐시 만드는데 사용되었다(대체로 매우 빠르다)
    (리퀘스트 처리할 시간이 캐시 만드는데 사용되어서 큐되었단 말인 듯.
    ‘대체로 매우 빠르다’는 보통은 빨라서 이것때문에 큐될일이 없다는 말인가?)

Stalled/Blocking
요청이 보내지기 전까지 기다리는 시간이며 큐에 언급된 내용으로도 기다릴 수 있다.
추가로 스톨드/블럭킹 시간은 프록시 협상에 사용된 시간도 포함하고 있다.
(프록시 협상 : 요청주체와 응답주체간 컨텐츠 내용 협상 / 프록시 서버 연결협상, 컨텐츠 연결 협상등 다양한 negotiation이 해당 내용의 원인이 되는 것 같다.)

Proxy Negotiation
프록시 서버 커넥션 연결에 사용된 시간

DNS Lookup
DNS Lookup에 사용된 시간으로  페이지의 모든 새 도메인은 DNS lookup을 위해 roundtrip이 요구된다.
(브라우저에서 처음 인식되는 도메인 주소에 대해서 DNS Lookup을 하는데 DNS 프로토콜 설계상 즉시 IP응답을 주지 못하고 여러 DNS 서버간 통신 후 IP 변환이 되는 경우 시간이 걸리게된다. 여기서 말하는 roundtrip는 이 전체 과정(a full round-trip)을 의미하는 것 같다.)

Initial Connection/Connecting
TCP 핸드쉐이킹/커넥션 재시도와 SSL negotiating을 포함한 커넥션 수립에 걸린시간

SSL
SSL 핸드쉐크 완료에 사용된시간

Request Sent / Sending
이슈된 네트워크 요청에 사용된 시간. 대게 밀리세컨드의 일부이다.
(밀리세컨드보다 조금 걸린단 소린가?)

Waiting (TTFB, time to frist byte)
초기 응답 대기에 사용된 시간 그리고 최초 바이트를 위한 시간.
이 시간은 서버 roundtrip의 숨겨진 부분을 잡는다.추가적으로 서버응답을 위한 대기시간에 사용된 시간이다.

Content Download/Downloading
응답 데이터를 받는데 사용된 시간

 

 

 

 

 

 

 

 

 

 

 

 

 


 

브라우저

HTTP가 빠르게 웹 콘텐츠를 전달해도 이를 해석하는 브라우저가 빠르게 동작하지 않으면 웹성능이 느려질수밖에 없다. 따라서 웹성능 최종 테스트, 디버깅은 브라우저를 통해 진행된다. 브라우저를 이용해 웹 페이지가 열리는시간, 로딩이 완료되는 시간 차이를 계산해서 웹 페이지 로딩 시간을 알아낼수 있다.

 

네비게이션 타이밍 API

navigationStart - 이전문서가 unload를 시작한 시점. 즉 새로운페이지 로딩할 준비 완료된 시점을 의미한다.

redirectStart - HTTP 페이지 재전송 수행될때 마지막 페이지 재전송 응답의 마지막 바이트를 받은 시점

redirectEnd - HTTP 재전송이 수행될 때 마지막 페이지 재전송 응답의 마지막 바이트를 받은 시점 페이지 재전송이 없다면 0 

requestStart - 브라우저가 접속한 서버(또는어플리케이션) 캐시 시스템에 문서를 요청한 시작시간

responseStart - 브라우저가 서버(또는 캐시시스템)으로 응답 데이터의 첫번째 바이트를 받은 시간

responseEnd - 브라우저가 서버(또는 캐시시스템)으로 응답 데이터의 마지막 바이트를 받은 시간

domLoading - 브라우저가 웹 페이지 문서를 만들기 시작하는 시점

domInteractive - 브라우저가 웹 페이지 문서 준비상태를 intereactive로 변경하는 시점

domContentLoaded - 웹 페이지 문서에서 'DOMContentLoaded' 이벤트가 호출되는 시점

domComplete - 웹 페이지 문서가 준비 상태를 'complete'로 변경 하는 시점

loadEventStart - 웹 페이지의 load 이벤트가 발생하는 시점

loadEventEnd - 웹 페이지의 load 이벤트가 완료된 시점

 

option+command+j 단축키를 눌러서 접근가능

 

const log = console.log;
const timing = window.performance.timing;
//페이지 전체 로드 시간
const pageLoadTime = timing.loadEventEnd - timing.navigationStart;
log(`pageLoadTime: ${pageLoadTime} ms`)

//http요청에서 응답시간
const connectTime = timing.responseEnd - timing.requestStart;
log(`connectTime: ${connectTime} ms`);