티스토리 뷰

HTTP, JS

[WEB] 브라우저의 렌더링 과정

안톨리니 2022. 8. 6. 13:03

Reference

  • 모던 자바스크립트 Deep Dive
  • MDN web docs_
  • HTTP완벽 가이드
  • developer.mozilla.org

 

우리가 보통 특정 사이트에 이동하기 위해서 브라우저의 주소창에

해당 사이트의 주소(URL)를 입력하곤 합니다.

 

필자는 주로 네이버를 자주 이용하기 때문에

항상 크롬 브라우저를 켜자마자 주소창에 www.naver.com을 입력하여 네이버로 이동합니다.

 

아마 대부분의 사람들은 브라우저 내에서 자신이 원하는 무언가를 하기 위해

사이트에 있는 링크를 클릭할 때도 있고

아니면 아이디와 비밀번호를 입력한 뒤 로그인 버튼을 눌러 로그인할 때도 있고

때로는 필자처럼 직접 브라우저 주소창에 해당 웹사이트의 주소를 입력하곤 합니다.

우리가 웹사이트 안에서 무언가를 요청하면

순식간에 그 요청에 대한 결과가 눈앞에 펼쳐지지만

내부적으로는 아주 복잡한 동작을 합니다.

 

웹 브라우저가 웹 서버에게 HTTP요청 메시지를 보내고

웹 서버는 다시 웹 브라우저에게 HTTP응답 메시지를 보냅니다.

 

사실 이 응답 메시지가 오고가는 사이에도 아주 복잡한 동작이 순식간에 일어납니다.

간단하게 요약하자면

만약 우리가 무언가를 얻기위해 주소창에

www.naver.com이라고 입력한다고 가정했을 때 보통 이러한 과정을 거칩니다.

 

1. 브라우저가 www.naver.com(사용자가 입력한)라는 호스트명을 추출합니다.

2. DNS를 통해 해당 호스트의 IP를 얻습니다.

3. 브라우저가 포트 번호를 얻습니다.

4. 브라우저가 얻은 IP주소와 포트번호로 TCP커넥션을 생성합니다.

5. 브라우저가 서버로 HTTP GET 요청 메시지를 보냅니다. 

6. 브라우저가 서버에서 온 HTTP응답 메시지를 읽습니다.

7. 브라우저가 커넥션을 끊습니다.

-HTTP완벽 가이드-

 

사실 이러한 과정 외에도 프락시, 캐시, 게이트웨이 등등

수많은 동작이 내부에서 이뤄집니다.

( 이러한 내부동작 하나하나는 책 한 권으로도 부족할 정도로 내용이 깊습니다. )

 

오늘 필자가 소개할 내용은 6번에 해당합니다.

 

서버로부터 우리가 요청한 데이터의 응답이 도착했을 때

브라우저는 어떠한 과정을 통해 이 데이터들을 우리에게 보여주는 걸까요??

(우리가 지금 보는 화면처럼)

 

 

 


HTTP응답 메시지

 

일단 브라우저가 응답 메시지를 어떻게 해석하는지 알아보기 전에

응답 메시지가 어떻게 생겼는지부터 파악해야겠습니다.

 

서버는 브라우저에게 요청한 리소스에 대한 응답으로 응답 메시지를 전달합니다.

HTTP Response Message

실제로 브라우저는 이런 형식의 응답 메시지를 받는데

1. Status Line : HTTP버전, 상태 코드

2. Response Headers : Date, Content-Length, Content-Type, Server ...

  • Date --> 서버가 응답을 만들어낸 시각
  • Content-Length --> xx바이트의 데이터로 이루어진 엔터티 본문
  • Content-Type --> 엔터티 본문의 MIME타입
  • Server --> 사용하는 웹서버

3. Response Message Body: 엔터티 본문

 

우리에게 중요한 부분은 가장 마지막인 엔터티 본문입니다.

택배로 따지면 엔터티 본문이 우리가 요청한 것에 대한 실질적인 화물이라고 볼 수 있습니다.

( 쿠팡에서 바나나를 시켰다면 도착한 택배 안에 있는 바나나 같은 )

 

이런 응답 메시지는 브라우저의 개발자 도구 네트워크 탭에서 확인할 수 있는데

 

See the Pen Untitled by Antoliny0919 (@antoliny0919) on CodePen.

필자가 만든 이 허접한 사이트를 통해 확인해보면

Response Headers

응답 메시지의 헤더들을 볼 수 있으며 왼쪽 Name밑에 있는 부분들을 보면

가장 먼저 html파일에 대한 요청을 수행합니다.

(※ 정적 파일의 경로를 통해 꼭 html이 아닌 다른 리소스를 요청할 수 있습니다.)

그리고 필자가 원하는 해당 요청에 대한 응답의 엔티티 본문은 Response탭에서 확인할 수 있습니다.

일반적으로 필자처럼 웹에서 정보를 얻기 위해 이동하면

가장 먼저 html문서의 요청을 하기 때문에 응답 엔터티 본문에 해당 서버에서 온

html코드가 브라우저에게 전달됩니다.

 

그렇다면 브라우저는 어떤 과정을 거쳐 문자열로만 이루어진 html파일의 내용을

지금 우리가 보는 화면처럼 만들어 내는 걸까요?

 

 


DOM(Document Object Model)

 

브라우저가 받은 리소스를 렌더링 하기 위해선

브라우저가 이해할 수 있는

자료구조(DOM)로 변환하는 작업을 거쳐야 합니다.

( html문서라 할지라도 응답으로는 바이트 형태로 응답받습니다. )

 

먼저 첫 번째로 일어나는 일은 응답된 바이트 형태의 텍스트 문서를 디코딩하는 과정입니다.

해당 문서를 디코딩하기 위해선 인코딩 방식을 알아야 하는데

브라우저가 인코딩 방식을 파악하는 방법은 다양합니다.

 

먼저 응답 헤더의 Content-Type부분에 있는 힌트로 파악할 수도 있고

BOM을 통한 분석 또는

HTML문서에 있는 <meta>테그 charset어트리뷰트를 통해 인코딩 방식을 파악합니다.

네이버 응답 헤더 content-type부분을 보자 (charset=UTF-8)

바이트 형태의 문서를 인코딩 방식의 기준에 따라 문자열로 변환했다면

브라우저는 토큰화, 어휘 분석이라는 프로세스를 이용하여

HTML코드 덩어리들을 개별 토큰으로 분해하는 과정을 시작합니다.

 

브라우저는 HTML코드를 쭉 읽어가며

시작태그와 엔드태그 그리고 그 사이에 있는 문자들을

토큰화 합니다.

이러한 토큰들을 객체로 변환하여 노드들을 생성하고

그 노드들로 구성된 트리 자료구조를 만드는데 그걸 DOM이라고 부릅니다.

developer.mozilla.org

DOM을 구성하는 여러 노드들은 전부 다 객체이기 때문에

각 객체마다 여러 어트리뷰트를 가지고 있습니다.

(요소 노드, 어트리뷰트 노드, 텍스트 노드...)

 

복잡한 계층구조인 HTML문서를 부자 관계를 반영하여 트리 자료구조로 만들어서

각 노드에 대한 접근과

스타일에 대한 변경을 JS를 통해 동적으로 제어할 수 있습니다.

 

DOM은 HTML문서를 파싱한 결과물이자

웹 페이지의 객체 지향적 표현이라고 볼 수 있고

결국은 브라우저가 DOM에 대한 해석을 통해 우리에게 보이는 화면을 도출하게 됩니다.

(아마 웹페이지를 조금이라도 만들어본 사람에게는 이해가 쉬울 거라고 생각합니다.)

토큰 --> 노드 --> DOM

오늘은 브라우저의 렌더링 과정에 대한 포스팅이니

DOM에 대해 이정도만 설명하고

다시 렌더링 관련 이야기를 하자면

브라우저의 렌더링 엔진이 HTML문서를 처음부터 한 줄씩 순차적으로 파싱 하여

DOM을 하나하나 생성해 나가는 구조입니다.

 

그러다가 만약 CSS파일이나 JS파일을 만나게 되면(link태그나 script태그를 만났을 때)

잠깐 DOM생성을 중단하고

 

해당 파일을 서버에 요청하고 HTML을 파싱한 방법과 동일하게 JS, CSS파일 등을 파싱합니다.

CSS파일의 파싱도 HTML과 동일한 과정을 통해 이루어집니다.

결국은 CSS파일도 트리구조를 가지게 되는데

CSS또한 부모태그에서 설정한 스타일이 자식태그에게 상속되는 형태라

HTML과 같이 트리구조를 가지게 됩니다.

CSSOM

결국은 CSS또한 CSSOM(CSS Object Model)이라는 트리구조를 생성하고

DOM과 합쳐져서 렌더 트리를 만들게 됩니다.

렌더 트리를 보면 DOM에 있던 html이나 head, meta, link같은 태그는 보이지 않습니다.

 

렌더 트리는 렌더링을 위한 자료구조이기 때문에

브라우저 화면에 렌더링 되지 않는 노드들은 포함하지 않습니다.

그렇기 때문에 html, head같은 태그나 css에 의해 화면에 숨겨지는 노드(display: None)들은

렌더트리에서 존재하지 않습니다.

렌더트리가 생성된 다음에는

'레이아웃'이라는 과정을 거치는데

말 그대로 HTML 요소들의 크기와 위치를 계산하는 과정입니다.

이 과정을 통해 우리가 CSS에서 상대적인 측정값 em, %... 등등은

pixel값으로 변환하게 되고 브라우저 화면에 픽셀을 렌더링 하는

페인팅 처리에 입력되면서

현재 우리가 보는 화면이 만들어지는 구조입니다.

 

 

 


요약

 

지금까지의 내용을 간단하게 요약하자면

브라우저가 HTTP 응답 메시지를 받게 됐을 때

 

1. HTTP문서를 파싱해서 DOM트리를 만든다

2. HTTP문서를 파싱하는 도중에 다른 파일들을 만나면 서버에 요청하고 CSS, JS파일을 만났을 때는

3. CSS, JS파일 또한 HTTP 파싱과 동일한 과정을 거쳐 DOM트리, CSSOM트리를 만든다. 

4. CSSOM트리와 DOM트리를 통해 Render트리를 만든다.

5. 렌더트리에서 레이아웃 과정을 거쳐 화면에 보여지는 노드들의 사이즈를 파악한다.

6. 렌더트리에 있는 각 노드들을 화면에 페인트 한다.

 

이러한 과정을 통해 결국은 우리에게 보이는 화면이 만들어지는 것입니다.

물론 이제 JS를 통하여 동적으로 DOM요소나 CSSOM요소를 추가/삭제, 크기/위치들을 변경할 수 있습니다.

그럴때마다 웹 브라우저는

변경된 DOM, CSSOM을 통하여 다시 Render Tree를 만들고

똑같은 과정을 반복하게 됩니다.

 

사실 이러한 구조를 이해하는 데에는 직접 HTML, CSS를 통해

웹사이트를 만들어보고 JS로 DOM요소들을 제어해보면

이해하는데 더 큰 도움이 되는 거 같습니다.

 

그런 의미로 다음 포스팅에는 DOM트리 내에 있는 다양한 노드들이 가진 속성들과

해당 노드들을 제어할 수 있는 방법에 관한 포스팅을 쓸 예정입니다 :)

 

 

 

 후기

 

사실 JS를 배우는 과정에서 브라우저의 렌더링 과정을 배우게 되었는데

진짜 필자에게 너무나도 흥미로운 내용이었다.

 

세상 브라우저의 대단함을 느끼게 되었다.

 

이런 대단한 브라우저를 보며

나도 언젠가는 브라우저를 만드는 프로젝트에 참여할 수 있을까?

라는 생각도 들고

도대체 옛날 사람들은 이런 서비스가 구축되기 전에는 어떻게 컴퓨터를 사용했는지

의문이 들었다.

 

그저 감사할 따름이다.

ㅠㅠ

미리 앞길을 틔어준 대단한 프로그래머분들 덕분에

필자가 편하게 공부하는 거 같다.

댓글
공지사항