사용자는 로드가 빠르고 상호작용이 원활한 컨텐츠로 이루어진 웹 경험을 원한다.
따라서 개발자는 이 두가지 목표를 달성하기 위해 노력해야한다.
웹 성능에 있어서 두가지 중요한 문제는 지연시간과 브라우저가 대부분 싱글 쓰레드로 동작한다는 점이다.
빠른 로딩을 하는데 있어서 지연시간은 이겨내야할 중요한 문제이다.
최대한 빠르게 요청하는 것, 네트워크를 통해 컴퓨터로 바이트를 빠르게 전송하는 것 (네트워크 지연시간)
.
.
.
웹 최적화는 페이지 로드가 최대한 빠르게 이루어질 수 있도록 하는 것이다.
대부분 브라우저는 싱글 쓰레드이다.
원활한 상호작용을 위한 개발자의 목표는 부드러운 스크롤부터 매우 기민하게 반응하는 터치에 이르기까지 성능이 뛰어난 상호 작용을 보장하는 것 이다.
메인 쓰레드가 요청된 모든 작업을 수행하면서도 유저와의 상호작용에 반응 할 수 있도록 보장하기 위해서는 렌더링 시간이 가장 중요하다.
브라우저가 싱글 쓰레드로 동작한다는 점을 이해하고 가능한 메인 쓰레드의 책임을 줄여주는 방식으로 웹 성능 향상을 이룰 수 있다. 이렇게 하면 렌더링은 부드럽고 상호작용에 대한 응답은 즉각적일 것이다.
즉, 브라우저는 한 번에 한 가지 일만 할 수 있어서, 웹사이트가 느려지지 않게 하려면 메인 쓰레드가 빨리 빨리 일할 수 있도록 도와야한다는 뜻이다.
목차
1. 탐색
2. 응답
3. 구문 분석
4. 랜더
5. 상호작용
1. 탐색 (Navigation)
웹 페이지를 로딩하는 첫 단계
사용자가 주소창에 URL을 입력하거나, 링크를 클릭하고, 폼(form)을 제출하는 등의 동작을 통해 요청을 보낼 때 마다 발생한다.
http://greenyellow-s.tistory.com/ → 요청 → 탐색
<form></form>
웹 최적화 목표 중 하나,
탐색이 완료될 때까지의 시간을 최소화하는 것
탐색 순서
1. DNS 조회 (DNS Lookup)
보통 URL은 IP 주소가 아닌 도메인주소로 사용자에게 보여진다.
http://greenyellow-s.tistory.com/
이 주소의 IP 주소의 서버 위치를 찾아야 해당 홈페이지를 방문할 수 있다.
따라서, DNS 조회가 필요하다.
브라우저는 DNS 조회를 요청하고 이는 최종적으로 서버에 의해 처리되며 IP 주소로 응답한다.
최초 요청 이후에, IP는 일정 기간 동안 캐시된다. 따라서 후속 요청 속도를 높인다.
DNS 조회는 보통 호스트 이름 하나당 한 번만 수행된다. 하지만 요청된 페이지에서 참조하는 다른 호스트 이름에 대해서는 각각 수행해야한다.
2. TCP 핸드 셰이크 (TCP Handshake)
IP 주소를 알고 난 후에는, 브라우저는 서버와 TCP 3방향 핸드 셰이크를 통해 연결을 설정한다. 이 방식은 데이터를 전송하기 전에 통신하려는 두 주체가 TCP 소켓 연결을 위한 매개변수를 주고 받을 수 있도록 만들어졌다.
TCP 3방향 핸드셰이크 기술은 "SYN-SYN-ACK" 로 불리기도 한다. 두 컴퓨터 간 TCP 세션을 협상하고 시작하기 위해서 TCP가 3개의 메세지를 전달하기 때문이다. 이는 요청이 보내지기 전에 3개의 추가적인 메세지가 컴퓨터 사이에 주고 받아진다는 의미이다.
즉, 데이터를 실제로 요청하기 전에 안전하게 연결되었는지 확인 메시지를 3번 주고받는 과정을 이야기 하는것이다.
메세지 갔어? (SYN) → 응 왔어 ! 너도 갔어?(SYN-ACK) → 응 나도 왔어 ! (ACK)
3. TLS 협상 (TLS Negotiation)
HTTPS를 이용한 보안성 있는 연결을 위해서는 또 다른 핸드셰이크가 필요하다. 이 핸드 셰이크는 통신 암호화에 쓰일 암호를 결정하고, 서버를 확인하고, 실제 데이터 전송 전에 안전한 연결을 이루어지도록 한다.
이를 위해 자원에 대한 실제 요청 전에 클라이언트에서 서버로 3번 더 왕복해야한다.
이는 서로 암호를 정하고 정한 암호가 서로 맞게 오고 가는지 확인하는 작업이다.
암호방식 전송 (ClientHello) → 암호 방식 확인, 서버 인증서 전송 (ServerHello & Certificate) → 비밀 키 자료 전송 (ClientKey) → 클라이언트 설정 완료 응답 (Finished) → 서버 설정 완료 응답 (Finished)
2. 응답 (Response)
웹 서버로 한 번 연결 성립 되면,
브라우저는 유저 대신 초기 HTTP GET request를 보낸다.
웹 사이트는 대게 HTML 파일을 요청한다.
서버가 요청을 받으면, 관련 응답 헤더와 함께 HTML의 내용을 응답하게 된다.
초기 요청에 대한 응답은 수신된 첫 바이트 데이터를 포함하고 있다.
Time to First Byte는 사용자가 요청을 보내고 HTML의 첫 패킷을 받는데 걸린 시간을 말한다.
첫 번째 청크는 일반적으로 14kb 크기의 데이터이다.
혼합 제어 / TCP 슬로우 스타트
TCP 패킷은 전송 중에 세그먼트로 분할된다. TCP는 패킷의 순서를 보장하기 때문에 서버는 일정 개수가 세그먼트를 전송한 후 클라이언트로부터 ACK 패킷 형태로 승인을 받아야한다.
서버 | → | 세그먼트 | → | 클라이언트 |
← | ACK | ← |
문제.
서버가 각 세그먼트마다 ACK 기다림 → 빈번한 ACK 발생, 저부하 네트워크 상황에서도 전송 시간 증가
한번에 너무 많은 세그먼트 전송 → 사용량이 많은 네트워크는 클라이언트가 세그먼트를 받을 수 없어 계속 ACK만 응답,
서버가 세그먼트 계속 재전송
전송되는 세그먼트 균형을 맞추기 위해 TCP 슬로우 스타트 알고리즘을 사용하여 네트워크 대역폭이 결정될 때까지 전송되는 데이터의 양을 점차적으로 늘리고 네트워크 부하가 높은 경우 전송되는 데이터의 양을 줄인다.
전송할 세그먼트 수는 혼잡도 윈도우(CWND) 의 값에 의해 제어된다.
ACK 수신 O → 혼잡 윈도우의 값이 두 배 증가 → 더 많은 세그먼트 전송 가능
ACK 수신 X → 혼잡 윈도우의 값이 반으로 줄어듬 → 너무 많은 세그먼트를 보내거나 너무 적게 보내는 것 사이의 균형을 맞춘다.
3. 구문 분석 (Parsing)
브라우저는 첫 번쩨 청크를 받으면, 수신된 정보를 구문 분석하기 시작한다.
구문 분석
브라우저가 네트워크를 통해 받은 데이터를 DOM이나 CSSOM으로 바꾸는 단계
브라우저는 HTML 데이터를 받자마자 DOM이나 CSSOM으로 바꾸는 구문 분석 작업을 시작한다.
이 과정이 완료되어야 비로소 렌더링(화면 그리기)이 가능해지고, 빠른 첫 화면을 위해서는 처음 14KB 안에 핵심 HTML/CSS 가 담겨 있어야 성능이 유리하다.
중요한 렌더링 경로를 다섯 가지 단계로 설명한다.
1. HTML 파싱 → DOM 트리 생성
2. CSS 파싱 → CSSOM 트리 생성
3. DOM + CSSOM 트리 생성
4. 레이아웃
5. 페인팅
1. DOM 트리 구축
HTML 구문 분석은 토큰화와 트리 생성을 포함한다. HTML 토큰은 시작 및 종료 태그 그리고 속성 이름 및 값을 포함한다.
만약 문서가 잘 구성되어 있다면 구문 분석은 명확하고 빠르게 이루어진다. 구문 분석기는 토큰화된 입력을 분석하여 DOM 트리를 만든다.
즉, 브라우저는 HTML을 읽고 해석하며 DOM 트리라는 구조를 만든다.
DOM 트리는 문서의 내용을 설명한다.
- HTML 요소는 시작하는 태그이고 DOM 트리의 루트(root) 노드 이다.
- 트리는 다른 태그간의 관계와 계층을 반영한다.
- 다른 태그에 감싸져 있는 태그는 자식 노드 이다.
- DOM 노드의 개수가 많아질수록, DOM 트리를 만드는데 오랜 시간이 걸린다.
HTML | ||
HEAD | BODY | |
⌙→ META | ⌙→ H1 | - class: "heading" |
⌙→ TITLE | ⌙→ P | |
⌙→ LINK | ⌙→ A | |
⌙→ SCRIPT | ⌙→ DIV |
이미지과 같은 논 블로킹 지원을 발견하면, 브라우저는 해당 지원을 요청하고 분석을 계속한다. (+ CSS 도 마찬가지 이다.)
하지만, async나 defer 같은 설정이 되어있지 않은 <script> 태그는 랜더링을 막고 HTML의 분석을 중지시킨다.
따라서 과도한 스크립트는 병목구간이 될 수 있다.
프리로드 스캐너
브라우저가 DOM 트리를 만드는 프로세스는 메인 스레드를 차지한다.
그렇기 때문에, 프리로드 스캐너는 사용 가능한 컨텐츠를 분석하고 CSS나 Javascript, 웹 폰트 같이 우선순위가 높은 자원을 요청한다.
이 덕분에 구문 분석기가 외부 자원에 대한 참조를 찾아가 요청하기 까지 기다리지 않아도 된다.
프리로드 스캐너가 제공하는 최적화는 블록킹을 줄여준다.
<link rel="stylesheet" src="styles.css" />
<script src="myscript.js" async></script>
<img src="myimage.jpg" alt="image description" />
<script src="anotherscript.js" async></script>
HTML과 CSS를 분석하고 있을 때,
프리로드 스캐너는 스크립트와 이미지를 찾아 다운로드하기 시작한다.
Javascript의 분석과 실행 순서가 중요하지 않고 스크립트가 프로세스를 막지 않도록 하려면, async 속성이나 defer 속성을 추가하면 된다.
CSS를 다운하는 것은 HTML 분석이나 다운로드를 막지 않는다. 하지만 Javascript는 종종 요소에 영향을 주는 CSS 속성들을 조작하기 때문에 Javascript의 실행은 막는다.
2. CSSOM 구축
CSS를 처리하고 CSSOM 트리를 만드는 것
CSS 규칙을 이해할 수 있고 작업을 진행할 수 있도록 스타일 맵으로 변환한다. 브라우저는 CSS에 있는 각각의 규칙을 읽고, 트리 노드를 만든다. CSS 선택기에 기반해서 부모 노드, 자식 노드, 형제 관계의 노드를 만들어진다.
p{color: red} → p태그에 color:red 가 적용
또한, CSS는 계단식(Cascading)이기 때문에 기본 규칙부터 시작해서 점점 더 구체적인 규칙으로 덮어씌운다.
CSSOM을 만드는데 드는 시간은 일반적으로 한 번의 DNS 조회하는 시간보다 짧기 때문에 웹 성능 최적화의 관점에서 CSSOM는 성능 향상에 큰 기여를 할 수 있는 영역은 아니다.
[ 다른 작업들 ]
Javascript 컴파일
- CSS가 분석되고 CSSOM이 생성되는 동안, 프리 스캐너 덕에 Javascript 파일 같은 다른 자원도 다운로드 된다.
접근성 트리 구축
- 브라우저는 접근성 트리를 만든다. 보조 장치는 이 트리를 이용해 내용을 분석하고 해석한다.
4. 랜더 (Render)
랜더링 과정에는 스타일, 레이아웃, 페인트 그리고 때때로 합성이 포함된다.
CSSOM과 DOM 트리는 구문 분석되는 과정에서 생성되고 랜더 트리로 합성된다.
랜드 트리는 보이는 요소의 레이아웃을 계산하고 요소가 화면에 페인트 된다.
어떤 경우에는 컨텐츠가 자신만의 레이어를 가지도록 조작되고, 나중에 합성된다.
화면 일부분을 CPU 대신 GPU가 그리면서 메인 쓰레드의 부담이 줄고 성능이 향상된다.
3. 스타일(Style)
DOM + CSSOM → 랜더 트리 생성
<head>와 그 자식 요소 혹은 사용자 정의 스타일 시트에 정의된 script { display: none; } 처럼 display: none 스타일 속성을 가진 요소와 같이, 화면에 나타나지 않는 태그의 경우 랜더링 결과에 나타나지 않을 것이기 때문에 랜더 트리에 포함되지 않는다.
visibility: hidden 속성을 가진 요소는 자리를 차지하기 때문에 랜더 트리에 포함된다.
사용자 에이전트가 기본으로 설정한 값을 오버라이드하는 정의를 하지 않으면, script 노드는 랜더 트리에 포함되지 않는다.
각각 보이는 노드는 그 노드에 적용된 CSSOM 규칙이 있다. 렌더 트리가 보이는 모든 노드의 내용과 계산된 스타일을 가지고 있다. DOM 트리에서 보이는 모든 노드에 관련된 스타일을 모두 마춰보고, CSS 캐스케이드 방식에 따라서 각 노드의 계산된 스타일이 무엇일지 결정한다.
4. 레이아웃(Layout)
랜더 트리를 기반으로 각 노드의 도형 값을 계산하기 위해 레이아웃을 실행하는 것
레이아웃은 랜더 트리에 있는 모든 노드의 너비, 높이, 위치를 결정하는 프로세스이다. 추가로 페이지에서 각 객체의 크기와 위치를 계산한다.
리플로우는 레이아웃 이후에 있는 페이지의 일부분이나 전체 문서에 대한 크기나 위치에 대한 결정이다.
- 랜더 트리가 한 번 만들어지고 나면, 레이아웃이 시작 (보이지 않아도 시작)
- 랜더 트리는 계산된 스타일과 함께 어떤 노드가 화면에 표시될지 식별 (각 노드의 위치나 좌표는 모름)
- 각 객체의 정확한 크기와 위치를 결정하기 위해, 랜더 트리의 루트부터 순회 (브라우저)
웹 페이지는 대부분 박스 형태이다.
브라우저는 화면에 표시될 모든 다른 상자의 크기를 결정하고 뷰 포트의 크기를 기본으로 하며, 레이아웃은 일반적으로 본문에서 시작해 모든 후손의 크기를 각 요소의 박스 모델 속성을 통해 계산한다.
이미지와 같이 크기를 모르는 요소를 위해 위치 표시 공간을 남겨둔다.
처음 노드의 사이즈와 위치가 결정되는 것을 레이아웃 이라고 부른다.
이후에 노드의 크기와 위치를 다시 계산하는 것은 리플로우 라고 부른다.
예를 들어, 첫 레이아웃이 이미지가 오기 전에 일어났을때,
이미지의 크기를 선언하지 않았기 때문에 이미지 크기를 알게 된 이후 리플로우가 한 번 있을 것이다.
5. 페인트 (Paint)
화면에 페인팅 하는 것
처음 일어나는 것을 첫 번째 의미 있는 페인트 라고 부른다.
페인팅 혹은 레지스터화 단계에서, 브라우저는 레이아웃 단계에서 계산된 각 박스를 실제 화면의 픽셀로 변환한다.
페인팅에서 텍스트, 색깔, 결계, 그림자 및 버튼이나 이미지 같은 대체 요소를 포함하여 모든 요소의 시각적인 부분을 화면에 그리는 작업이 포함된다.
브라우저는 이 작업을 매우 빠르게 해야한다.
부드러운 스크롤이나 애니메이션을 위해서, 스타일 계산, 리플로우, 페인팅과 같이 메인 쓰레드를 점유하고 있는 모든 작업은 브라우저를 16.67ms 미만만 차지해야한다. 따라서, 픽셀은 매우 빠르게 페인팅 되어야한다. 첫 페인팅 보다 다시 페인팅하는 것이 더 빠르게 마무리되기 위해서 화면에 그리는 작업은 일반적으로 몇 개의 레이어로 구분된다.
이것이 일어나면 합성이 필요하다.
페인팅은 레이아웃 트리의 요소를 레이어로 분리할 수 있다. 컨텐츠를 CPU의 메인 쓰레드에서 GPU 레이어로 격상하는 것은 페인트 및 리페인트 성능을 높인다. 레이어를 가동시키는 구체적인 속성과 요소가 있다. 요소에는 <video>, <canvas>가 포함되어 있다. 구체적인 속성에는 opacity, 3D, transform, will-change등이 있다.
자손 노드가 위의 이유 중 하나로 자신만의 레이어를 필요로하는 것이 아니라면, 이 노드는 그들의 레이어에서 그들의 자손과 함께 그려진다.
레이어는 성능을 향상시킨다. 하지만 메모리 관리 즉면으로 봤을 때는 비싼 작업이다. 따라서 웹 성능 최적화 전략으로 과도하게 쓰이지는 않아야한다.
6. 합성
문서의 각 세션이 다른 레이어에서 그려질 때, 세션을 겹쳐 놓으면서 그것들이 올바른 순서로 화면에 그려지는 것과 정확한 렌더링을 보장하기 위해 합성이 필요하다.
페이지가 계속해서 자원을 로드하면, 리플로우가 일어날 수 있다. 리플로우는 리페인트와 재합성을 일으킬 수 있다. 이미지의 사이즈를 미리 정해놨다면 리플로우는 필요하지 않을 것이다. 그리고 리페인트 되어야할 레이어만 다시 리페인트 하고 필요하다면 합성할 것이다.
5. 상호작용 (interactivity)
메인 쓰레드가 페이지를 그리는 것을 완료하면, 모든 것이 준비되었다고 생각할 수 있지만 꼭 그렇지는 않는다.
만약, 지연된 Javascript를 다운했다면, 그리고 onload 이벤트가 발생할 때 코드가 실행된다면, 메인 쓰레드는 여전히 바쁠 것이다.
그래서 스크롤링, 터치 등 다른 상호작용이 불가능할 것이다.
Time to Interactive(TTI) 는 DNS 조회와 SSL 연결이 이루어지는 첫 요청부터 페이지가 상호작용할 준비가 될 때까지 얼마나 걸리는지를 측정하는 단위이다.
만약, 이미지는 매우 빠르게 로드되었지만, Javascript파일이 2MB이고 네트워크 연결이 느렸다면
사용자는 페이지를 빠르게 볼 수 있지만, 스크립트가 다운로드 되고, 분석되고 실행되기 까지는 버벅거리는 스크롤을 할 수 밖에 없다.
이것은 좋은 사용자의 경험이 아니기 때문에, 메인 쓰레드를 점유하는 것은 피하는게 좋다.
참고 문서
https://developer.mozilla.org/ko/docs/Web/Performance/Guides/How_browsers_work
웹페이지를 표시한다는 것: 브라우저는 어떻게 동작하는가 - 웹 성능 | MDN
사용자는 로드가 빠르고 상호작용이 원활한 컨텐츠로 이루어진 웹 경험을 원합니다. 따라서 개발자는 이 두 가지 목표를 달성하기 위해서 부단히 노력해야합니다.
developer.mozilla.org
'개발 지식 > 기본지식' 카테고리의 다른 글
호스팅이란 (0) | 2025.05.02 |
---|---|
도메인 이름이란? (0) | 2025.05.02 |
DNS와 그 작동 원리 (0) | 2025.04.30 |
HTTP 란 (0) | 2025.04.24 |
인터넷은 어떻게 동작하는가 (2) | 2025.04.24 |