브라우저는 텍스트로 이루어진 HTML, CSS, Javascript 파일을 읽고 웹 페이지를 그려냅니다. 이 과정을 이해하기 위해서는 먼저 파싱(parsing)과 렌더링(rendering)이라는 단어가 무엇인지 알아야 합니다. (다음의 이해와 해석은 보다 쉬운 언어로 제가 이해한 바를 풀어낸 것이기 때문에, 명확한 과정은 보다 더 공신력 있는 다른 자료를 함께 참고하시기를 바랍니다.)
파싱이란 쉽게 말해서 HTML, CSS, Javascript 등의 문서를 읽고 이를 조작할 수 있는 자료구조로 변환하는 과정입니다. 그리고 렌더링은 이렇게 문서를 파싱해 만들어낸 자료구조의 일종인 렌더 트리를 기반으로 브라우저 화면에 그 내용을 시각적으로 출력하는 것을 말합니다.
파싱과 렌더링이라는 단어를 기반으로 브라우저의 렌더링 과정을 다시 풀어내면,
브라우저는 서버로부터 전달받은 HTML, CSS, Javascript 와 같은 리소스를 파싱하여 화면에 그 내용을 렌더링합니다.
라고 정리할 수 있겠습니다.
HTML, CSS 문서는 텍스트로 이루어져 있죠. 브라우저가 이 자료를 이용해 사용자가 시각적으로 인지할 수 있는 내용을 렌더링하려면, 우선 전달된 문서를 파싱하고 이를 사용할 수 있도록 메모리에 저장하는 과정이 필요합니다.
이 과정을 담당하는 것이 브라우저의 렌더링 엔진입니다. 브라우저의 렌더링 엔진은 서버로부터 응답받은 HTML 문서를 파싱하고, 브라우저가 이해할 수 있는 문서인 DOM(Document Object Model) 을 생성합니다.
파싱의 과정은 순차적으로 위에서 아래로 진행됩니다. 그러다가 link 나 style 태그처럼 CSS 를 불러오는 태그를 만나게 되면 렌더링 엔진은 DOM 의 생성을 일시적으로 중단하고 CSS 파일을 요청해 받아옵니다. 그런 이후에 CSS 파일을 파싱해 CSSOM(CSS Object Model) 을 생성하죠.
CSS 의 파싱을 완료하게 되면 렌더링 엔진은 다시 HTML 파싱이 중단된 지점으로 돌아가 계속해서 파싱을 진행합니다. 그리고 이렇게 완료된 DOM tree 와 CSSOM tree 를 결합해 렌더 트리를 만들고 이 렌더 트리를 사용해 화면을 렌더링하게 되는 것이죠.
위에서는 자바스크립트에 대한 내용이 배제되어 있습니다. 하지만 우리는 script 태그를 이용해 자바스크립트 코드를 연동하고 이를 실행하죠.
브라우저는 HTML 을 파싱하다가 script 태그를 만나게 되면 DOM 의 생성을 중단하고 자바스크립트 리소스를 요청해 받아옵니다. 그리고 자바스크립트 코드를 파싱하게 되죠. 이 때, 이 과정을 담당하는 것은 렌더링 엔진이 아닌 자바스크립트 엔진입니다. 크롬의 V8 엔진이 그 예라고 할 수 있죠.
만약 자바스크립트 코드 내부에 DOM 이나 CSSOM 을 조작하도록 DOM API 가 사용되었다면 기존에 생성되었던 DOM/CSSOM 를 동적으로 변경하게 됩니다. 이렇게 변경된 부분이 수정반영되어 최종적으로 렌더링된다고 할 수 있죠.
브라우저의 렌더링 과정은 HTML 문서로부터 시작합니다. 파싱하는 과정에서 CSS, 자바스크립트 리소스를 사용해야 할 경우 DOM 의 생성을 중단하고 새롭게 불러온 리소스의 해석으로 넘어가게 되죠.
만약 DOM 의 생성이 완료되지 않은 상황에서 자바스크립트 리소스로 넘어가게 되고, 자바스크립트 코드에서 아직 생성되지 않은 DOM 을 조작하려고 한다면 에러가 발생할 수 있습니다. 그러나 만약 <body> 요소의 제일 아래에 <script> 태그가 위치한다면, DOM의 생성이 완료되었기 때문에 이를 조작한다 해도 문제가 발생하지 않습니다.
추가로 이렇게 스크립트 태그를 제일 마지막에 위치시키게 되면 자바스크립트가 실행되기 전에 DOM 의 생성이 완료되어 렌더링되기 때문에, 페이지의 로딩 시간이 단축된다는 이점이 있습니다.
브라우저의 렌더링 과정에 대해 간략하게 이해해봤습니다. 오늘 정리한 내용에서는 브라우저가 어떻게 서버에 리소스를 요청하고 응답받는지에 관한 부분이 생략되어 있습니다. 많은 내용을 압축하고 가능한 쉬운 것만 다루었기 때문인데요. 자세한 공부가 필요하신 분들은 모던 자바스크립트 Deep Dive
를 참고하시면 되겠습니다.