- 브라우저 렌더링
- 폰트 최적화
그동안 계속 정리해왔던 브라우저 렌더링을 하나로 합쳐보자.
브라우저가 렌더링을 하기 위해서는 데이터가 필요하고, 우리는 브라우저에서
URL
을 통해 그 데이터를 요청한다.
주소창에 URL을 입력하면,
1. 브라우저 캐시
2. OS 캐시
3. 라우터 캐시
4. DNS 서버
순서대로 찾아가며 대상 IP를 탐색한다.
URL
을 통해 찾은 IP
로 데이터를 요청하기 위해서 HTTP 요청 메시지를 생성한다.
HTTP
는 보내는 데이터에 대해 정해진 규약.
생성된 HTTP 요청은 TCP
통신을 통해서 대상 IP
로 전달된다.
3-way handshake
(SYN / SYN, ACK / ACK)HTTP 통신은 요청과 응답이 하나의 세트로 이루어져 있어서 찾아간 IP에서 데이터를 HTTP 응답 메시지로 만든다.
HTTP 응답은 다시 TCP 통신을 통해 요청 IP로 보내진다.
일련의 통신을 통해 받은 데이터를 화면에 그리기 위해서는 적절한 가공이 필요하며, parsing, style, layout, paint, composite의 단계를 거친다.
<!DOCTYPE html>
<html>
<head>
<meta>
<link>
...
</head>
<body>
<div></div>
...
</body>
</html>
통신을 통해 받은 HTML은 우선 파싱을 통해 DOM으로 만들어야 한다.
meta
태그를 만나는 경우, content
를 확인하고 비어있지 않으면, refresh가 일어난다.srcset
, imagesrcset
, media
와 같은 특수한 속성을 지닌 경우를 제외하면, 모두 파싱 후에 로드한다.rel
속성 link
가 직접적으로 참조하는 리소스 뿐만 아니라 하위에 연결된 모든 리소스가 로드될 때까지 지연시킨다. > 렌더 블로킹preload
: 파싱 중에 가능한 빠른 순서로 로드해서 캐싱as
속성 값으로 리소스 타입을 예측하여 로드stylesheet
의 경우에는 @import
로 불러오는 외부 리소스도 모두 로드될 때까지 지연시킨다. > 렌더 블로킹javascript
코드 부분으로 태그를 만나는 즉시 파싱하여 실행한다. > 렌더 블로킹async
: 비동기적으로 코드를 로드 및 파싱 후 실행defer
: 의도적인 지연 발생, 문서 파싱이 끝난 후 실행defer
속성을 가진 스크립트는 순차적으로 실행되기 때문에, 실행 순서를 보장할 수 있다.CSS
를 참조하는 경우, 즉시 실행이 중단된다. > 스크립트 블로킹파서에 의해서 트리 구조의 오브젝트를 생성
(+) 문서를 모두 파싱하면, load
이벤트가 발생한다.
파싱 과정에서 생성된 DOM 트리와 CSSOM 트리를 결합해서 Render 트리를 생성한다.
렌더 트리는 화면에 표시되는 요소만을 오브젝트화 사용하기 때문에, <head>
태그와 display: none
속성을 가진 요소들은 제외된다.
각 요소들의 크기와 위치를 픽셀로 계산하여 전체적인 화면의 레이아웃 구성한다.
더티 비트
개념은 이 단계에서 확인하고 사용된다.
더티 비트
: 브라우저는 변경되었거나 추가된 요소에 더티
상태를 부여하고, 렌더링할 때 더티
상태를 추적하여 그 요소만 다시 렌더링한다.
이 단계에서는 실제 화면에 표시될 요소들을 layer
별로 그린다.
레이어는 기본적으로 root
레이어가 있다.
will-change
, position (absolute, sticky, fixed)
, transform (3D 변환)
, canvas (3D 요소 그려진)
요소들은 개별 레이어로 분리된다.
각 요소는 벡터로 구성되어 있다가 픽셀화되어 레이어에 얹어진다.
요소 별로 그 순서는 정의되어 있다.
요소 (table
제외)
table
layer
별로 그려진 것을 합쳐서 하나의 화면으로 표시한다.
GPU 가속을 통해서 성능을 향상시킬 수 있다.
브라우저 렌더링 과정에서 폰트를 최적화할 수 있는 방법은 뭘까.
rel="preload"
활용<link rel="preload" as="font" />
rel="preload"
값을 가진 link
태그는 가능한 빠른 시점에 해당 리소스를 로드하기 때문에, 폰트 로딩으로 인한 지연을 줄일 수 있다.
@font-face {
font-family: Gothic;
src: local('Gothic'), # 로컬 폰트 우선 사용
url(Gothic.woff2) format('woff2'),
url(Gothic.woff) format('woff'),
url(Gothic.ttf) format('ttf'),
}
local
을 적용해서 현재 환경에 해당 폰트가 존재하는 경우에는 외부 리소스를 불러오지 않고 로컬 폰트를 적용한다.
폰트는 형식에 따라 같은 폰트여도 파일의 크기가 다르다.
일반적으로 woff
, woff2
형식이 웹용 폰트 형식으로 다른 형식의 폰트에 비해 크기가 작아, 로딩을 줄이는 방향으로 개선 가능하다.
형식 | 사이즈 (예시) |
---|---|
woff2 | 814KB |
woff | 1,153KB |
ttf | 2,615KB |
otf | 1,601KB |
서브셋 폰트
: 사용 빈도가 낮아 필요성이 낮은 글자를 제거하여 크기를 줄인 폰트
한글은 조합에 따라 글자수가 많아지기 때문에 자연스럽게 폰트 파일의 크기가 커지게 된다.
이러한 단점을 보완하기 위해서 사용 빈도가 낮은 글자들을 제거하여 폰트 파일의 크기를 줄여 로딩 속도를 개선할 수 있다.
예를 들어 나눔바른고딕은 11,172자에서 불필요한 글자를 제거하여 2,350자로 줄임으로써 2.4MB
에서 586KB
로 크기를 줄였다.
unicode-range
적용unicode-range
속성은 기본적으로 정해진 글자에만 폰트를 적용할 때 사용된다.
최적화의 관점에서 unicode-range
속성의 장점은 전체 텍스트에 정해진 글자가 포함되지 않는 경우에는 폰트를 다운로드 하지 않는다는 것에 있다.
@font-face {
font-family: 'korea font';
font-weight: 500;
src: local('korea font'),
url('path/to/korea/font') format('woff2'),
url('path/to/korea/font') format('woff'),
unicode-range: U+1100-U+11FF;
}
FOIT (Flash Of Invisible Text)
FOUT (Flash Of Unstyled Text)
font-display
적용을 통해서 조절
폰트 차단 기간 : @font-face
가 로드되지 않았을 때, 보이지 않는 대체 폰트로 렌더링 되는 시간 → 로드되면 폰트 교체
폰트 교체 기간 : @font-face
가 로드되지 않았을 때, 대체 폰트로 렌더링 되는 시간 → 로드되면 폰트 교체
폰트 실패 기간 : @font-face
가 로드되지 않은 경우, 대체 폰트를 정상 폰트로 인식하는 시간
@font-face {
font-family: ExampleFont;
src: url(/path/to/fonts/examplefont.eot) format('eot'),
url(/path/to/fonts/examplefont.woff) format('woff');
font-weight: 400;
font-style: normal;
font-display: fallback;
}
font-display: auto; # 유저 에이전트에 의해 결정
font-display: block; # 짧은 차단 기간 -> 무한대의 교체 기간
font-display: swap; # 매우 작은 차단 기간 -> 무한대의 교체 기간
font-display: fallback; # 매우 작은 차단 기간 -> 짧은 교체 기간
font-display: optional; # 매우 작은 차단 기간 -> 매우 작은 교체 기간
두께별로 개별 폰트를 지정하는 방법도 최적화의 방법 중 하나이다.
@font-face {
font-family: ExampleFont;
src: url(/path/to/fonts/examplefont-bold.woff) format('woff')
font-weight: 700;
}
@font-face {
font-family: ExampleFont;
src: url(/path/to/fonts/examplefont-normal.woff) format('woff')
font-weight: 500;
}
@font-face {
font-family: ExampleFont;
src: url(/path/to/fonts/examplefont-thin.woff) format('woff')
font-weight: 300;
}
이 부분은 좀 더 확인이 필요해보인다.