렌더링이란
html, css, js 등 개발자가 작성한 문서(웹 페이지 구성을 위한 자료)를 해석하여 사용자의 장치에 맞게 그래픽 형태로 변환해 출력하는 과정입니다.
브라우저 | 렌더링 엔진 | 부가 설명 |
---|---|---|
파이어폭스 FireFox | 게코 Gecko | 모질라 재단에서 만든 레이아웃 엔진 |
사파리 Safari | 웹킷 Webkit | 애플에서 개발한 오픈소스 현재는 웹킷2를 사용 |
크롬 Chrome | 블링크(Blink) | 애플의 웹킷 사용 중 웹킷을 fork해서 블링크(Blink) 엔진을 자체 구현 및 사용 |
익스플로러 IE | 트라이던트 Trident | 마이크로소프트의 레이아웃 엔진 |
크로스 브라우징 이슈 : 모든 브라우저가 같은 렌더링 엔진을 사용하지 않기 때문에 같은 소스를 사용하더라도 다른 결과가 보여질 수 있습니다. 렌더링 엔진이 다른 만큼 렌더링 과정도 차이가 있습니다.
사용자가 브라우저의 *UI 주소창을 통해 원하는 페이지(혹은 네트워크로 연결된 장치)를 찾기 위해서는 해당 페이지의 *ip 주소(Internet Protocol address)를 알아야합니다.
*UI(User Interface) : 이전/다음, 주소 표시줄, 북마크 메뉴, 새로고침 등..
*ip 주소 (Internet Protocol address) : 인터넷을 사용하는 모든 네트워크 장치들이 통신하기 위해 사용하는 특수한 번호
사용자가 ip 주소(93.184.216.34)를 외워서 사용하기란 많이 불편합니다.
DNS는 특정 컴퓨터(네트워크 장치)의 주소를 찾기 위해 사람이 이해하기 쉬운 도메인 이름을 숫자로 된 식별 번호(IP 주소)로 변환해줍니다.
사용자가 도메인 이름을 검색하게 되면 도메인과 ip 주소를 가지고 있는 저장소(Domain Name Server)에서 사용자가 검색한 도메인에 해당하는 ip 주소를 반환해줍니다.
만약 입력받은 도메인이 이미 방문한적이 있는 주소라면 DNS 서버에게 물어보고 찾아보고 하는 일이 자원적 낭비입니다.
따라서 브라우저는 도메인 요청이 일어나면 가장 먼저 로컬의 캐시 영역에서 해당 도메인에 맞는 ip 주소를 찾기 시작합니다.
방문한적 없는 주소라면 DNS 서버에서 해당 ip 주소를 찾아 로컬 캐시 영역에 저장합니다.
다음에 다시 방문하게 된다면 case 1에 해당됩니다.
그렇게 브라우저는 DNS에게 받은 ip 주소에 해당하는 서버에 데이터를 요청하게 됩니다.
앞서 얘기한 *사용자 인터페이스(UI), *자료 저장소(Data Persistence)와 브라우저 엔진의 관계는 위와 같습니다.
이제 브라우저는 서버에게 데이터를 요청할 수 있고 서버는 요청에 맞는 응답을 할 수 있습니다.
*브라우저 = 클라이언트
해당 내용은 본 주제를 다루기에 너무 많은 내용이라 간략하게 정리합니다.
서버로부터 html, css, js, img 등.. 웹에 필요한 리소스를 다운로드 받습니다.
브라우저가 페이지를 렌더링하기 위해서는 *렌더링 트리(Rendering Tree)가 필요합니다.
렌더링 트리를 만들기 위해 html의 DOM 트리와 css의 CSSOM 트리가 필요합니다.
서버로 부터 받아온 html 코드가 DOM 트리로 변환되는 과정입니다.
바이트 변환 ➡️ 토큰 식별 ➡️ 노드 변환 ➡️ DOM 트리 빌드
브라우저와 서버의 통신 과정에서 서버는 브라우저로부터 요청 받은 html 파일을 읽은 후 메모리에 저장한 뒤, 메모리에 저장된 바이트를 응답하게 됩니다.
<html>
, <body>
위 과정을 Pasing한다 합니다.
브라우저는 html 마크업을 처리할 때 마다 위의 단계를 수행합니다.
html 문서를 한줄씩 읽으며 순차적으러 파싱하며 DOM을 생성하던 중 css의 link 태그 혹은 style 태그를 만나게 되면 html 파싱을 중지한 뒤 css 파일을 서버에 요청/응답 후 css 파싱을 시작합니다.
css의 파싱 과정은 바이트 변환 ➡️ 토큰 식별 ➡️ 노드 변환 ➡️ CSSOM 트리 빌드로 동일합니다.
자바스크립트는 scirpt 태그로 분류되는데 렌더링 엔진이 해당 태그를 만나게 되면 모든 권한이 자바스크립트 엔진에게 넘어가며 자바스크립트를 파싱합니다.
이때 자바스크립트 엔진의 작동 방식에 대해서 알아야하는데, 자바스크립트 엔진은 연산 및 출력이 가능하도록 CPU가 이해할 수 있는 언어로 자바스크립트 코드를 파싱하여 html, css와 마찬가지로 *트리를 생성합니다.
자바스크립트 엔진의 작동 방식을 간략하게 살펴보자면 먼저 엔진이 실행할 자바스크립트 파일을 받으면 파싱, AST(Abstract Syntax Tree)를 구축하는 과정을 거치게됩니다. 다음으로 인터프리터가 코드를 읽으며 실행합니다.
자바스크립트에 대한 해당 내용은 중요한 내용이므로 간략하게만 정리하고 따로 글을 작성합니다.
브라우저가 html 문서를 파싱 중 스크립트 태그를 만나면 자바스크립트 엔진에게 권한을 넘겨주기 때문에 html은 파싱을 중지합니다.
파서를 강제로 중단 시키기 때문에 "파서 차단 리소스"(Parser Blocking Resource)라 부릅니다.
즉, 자바스크립트가 실행되는 동안은 html을 파싱할 수 없기 때문에 웹페이지의 로딩이 늦춰질 수 있습니다.
혹은 DOM이 완성되지 않은 상태에서 자바스크립트 코드가 DOM Api를 통해 DOM 조작을 시도하여 에러를 발생시킬 수 있습니다.
html의 파싱이 끝난 뒤 다시 말해 <body>
태그의 최하단에 스크립트 태그를 넣는 이유가 해당 이유 때문입니다.
이러한 이슈 때문에 scirpt 태그에 async
, defer
속성을 넣어 사용하는 방법도 있습니다.
해당 내용 또한 따로 글을 작성합니다.
반면 css의 link 태그는 <head>
안에 정의합니다.
이유는 stylesheets의 경우 렌더링에 필수적인 요소이기 때문에 브라우저는 css 파일을 빠르게 다운로드하고 파싱하는게 중요합니다.
렌더링에 필수적인 요소란, 사용자의 화면에 컨텐츠를 빠르게 렌더링하기 위함
css는 DOM 트리를 변경하지 않기 때문에 DOM 파싱을 중단하거나 하는 이슈가 발생하지 않습니다.
DOM 트리와 CSSOM 트리가 만들어지면, 이 둘을 결합해서 렌더링 트리를 생성합니다.
렌더 트리의 생성은 최상단 부모 즉, DOM 트리의 루트 노드부터 시작하며 서치합니다.
구조와 텍스트 형태의 DOM 트리 최상단 노드부터 각 노드에 대해 CSSOM의 규칙을 적용하여 렌더 트리를 완성시킵니다.
이때 렌더 트리에는 페이지를 렌더링 하는데 필요한 노드만 포합됩니다.
display: none;
과 같은 속성은 CSSOM 트리에만 포함되지 렌더 트리에는 포함되지 않는다는 의미입니다.meta
태그와 실제로 보여지지 않는 태그도 포함되지 않습니다.이렇게 완성된 렌더 트리는 Layout 과정이 필요합니다.
브라우저의 뷰포트 내에서 각 노드들의 정확한 위치와 크기를 정확하게 캡처하는 Box 모델이 출력됩니다. 또한 %
, vh
, vw
와 같은 상대적인 측정값의 속성값들은 모두 절대적인 px단위로 변환됩니다.
레이아웃이 완료되고 요소들의 위치 및 크기 그리고 스타일 계산이 완료된 렌더 트리를 사용해 브라우저는 각 노드 요소들을 실제 화면에 그리게 됩니다.
대부분의 웹은 사용자에게 정적인 화면이 아닌, 동적인 화면을 제공합니다.
즉, 렌더링 과정이 끝나더라도 지속적으로 웹에는 변화가 일어납니다.
특정한 이벤트를 통해서 기존 노드들에게 변화가 생기면 렌더 트리 생성과 레이아웃 과정을 다시 수행해야하며 이러한 과정을 Reflow라 합니다.
Example : Reflow
대표적인 Reflow 속성들
position, width, height, margin, padding, border, border-width, font-size, font-weight, line-height, text-align, vertical-align, overflow
또한 이렇게 변경된 렌더 트리와 레이아웃을 실제로 화면에 그리기 위해 다시 한번 페인팅 과정이 필요합니다. 이를 Repaint라 합니다.
대표적인 Repaint 속성들
background, background-image, background-position, background-repeat, background-size, border-radius, border-style, box-shadow, color, line-style, outline,outline-color, outline-style, outline-width, text-decoration, visibilty
하지만 무조건 변경 사항이 생길 때마다 Reflow-Repaint가 일어나는것은 아닙니다.
레이아웃에 영향이 없을 경우에는 Repaint만 다시 수행합니다.
브라우저 렌더링이 빠를 수록 사용자는 쾌적한 UI/UX를 기대할 수 있습니다.
즉, 렌더링 최적화가 필요합니다.
Reflow 최소화
당연한 말이지만 렌더 트리와 레이아웃을 다시 수행해야 하는 Reflow는 최소화하는것이 좋습니다.
Reflow는 항상 Repaint를 불러옵니다. 하지만 Repaint는 Reflow없이 발생할 수 있기 때문에 되도록 Repaint만 발생하는 속성을 사용하는 것이 좋습니다.
프레임
사용자에게 부드러운 화면을 제공하기 위한 애니메이션은 렌더링 최적화는 반비례합니다.
애니메이션을 적은 시간/이동 단위로 사용하는 것보다 큰 시간/이동로 사용하는것이 Reflow를 줄일 수 있기 때문에 적당한 타협이 필요합니다.
영향을 받지 않는 노드를 최대한 사용하는것 즉 영향을 주는 노드들의 사용을 줄이는 방법 또한 좋은 방법입니다.