1. 서론
웹서버에 요청을 하면 브라우저는 우리가 접근하려는 웹페이지의 HTML 파일을 포함한 응답을 받는다. 유저에게 웹페이지를 보여주기 위해서 브라우저는 파일을 파싱, 렌더링, 페인트 해야 한다.
파싱이란 프로그램을 분석하고 실행 환경에서 실제로 실행할 수 있는 내부 형식으로 변환하는 것이다. 즉, 파싱은 우리가 작성한 코드(HTML, CSS)를 브라우저가 작업할 수 있는 형태로 변환하는 것을 의미한다. 파싱은 브라우저 엔진에 의해 수행된다. 브라우저 엔진에는 Gecko, Webkit, Blink 등이 있다.
2. HTML Parsing
2.1 Byte Stream Decoder
- 입력: 3C 62 6F 64 79 3E 48 65 6C 6C 6F 2C 20 3C 73 70 61 6E 3E 77 6F 72 6C 64 21 3C 2F 73 70 61 6E 3E 3C 2F 62 6F 64 79 3E
- 출력: <body>Hello, <span>world!</span></body>
브라우저가 서버로부터 받은 Unicode character stream은 네트워크나 로컬 파일 시스템에서 오는 byte stream이다. bytes는 특정 encoding에 따라 html 파일을 인코딩한 자료이므로 user agent는 bytes를 다시 디코딩해야 한다. user agents는 디코딩할 때 사용할 encoding을 결정하기 위해 encoding sniffing algorithm을 사용한다. 왜냐하면 encoding을 찾기 위해 pre-parsing하는 것은 parsing에 사용된 data structures를 버려야 할 필요성을 줄여 성능이 향상되기 때문이다.
encoding sniffing algorithm 은 user agent가 사용할 수 있는 out-of-band metadata(ex: Content-Type metadata)와 지금까지 사용 가능한 모든 bytes를 입력으로 받아들여 encoding과 confidence를 반환한다. confidence는 tentative, certain, irrelevant 중 하나이다. 인코딩과 그 인코딩에 대한 confidence가 tentative인지 certain인지를 parsing 중에 사용하여 인코딩을 변경할지 여부를 결정한다. 인코딩이 필요하지 않은 경우, 예를 들어 parser가 Unicode character stream에서 작동하고 인코딩을 전혀 사용할 필요가 없는 경우에 confidence는 irrelevant하다. 다음 5가지는 user agents가 encoding sniffing algorithm을 사용하는 유형이다.
- 사용자가 특정 인코딩을 user agent에 명시한 경우, 그 인코딩을 certain confidence로 반환한다.
- transport layer가 encoding을 지정하고 지원하는 경우, 그 인코딩을 certain confidence로 반환한다.
- 더 많은 bytes가 도착할 때까지 기다린다. 예를 들어, user agent는 500ms 또는 1024 bytes 중 더 빠른 시간동안 기다린다.
- 파일의 첫 번째 bytes가 특정 인코딩의 bytes 수보다 더 많고 특정 인코딩의 bytes와 일치하면, 특정 encoding을 certain confidence로 반환한다. 예를 들어, 파일의 첫 번째 bytes가 "FE FF"인 경우 Big-endian UTF-16 인코딩을 반환한다. 그리고 파일의 첫 번째 bytes가 "EF BB BF"인 경우 UTF-8 인코딩을 반환한다.
- 파일에서 explicit character encoding information을 검색한다.
2.2 Input Stream Preprocesser
- 입력: <body>Hello, <span>world!</span></body>
- 출력: <body>Hello, <span>world!</span></body>
Input Stream Preprocessor는 CR, LF, CRLF 등다양한 줄바꿈 문자를 일관된 문자(LF)로 변환, NULL 문자를 제거, 공백 문자를 처리 등 Token화하기 위한 전처리를 수행한다.
2.3 Tokenizer
- 입력: <body>Hello, <span>world!</span></body>
- 출력: ["StartTag: body", "Text: Hello", "StartTag: span", "Text: world!", "EndTag: span", "EndTag: body"]
Tokenizer는 HTML 문서를 개별적인 토큰으로 분해하는 작업을 수행한다. 입력된 HTML 문자열을 한 문자씩 읽어들여 < 문자를 만나면 태그의 시작으로 간주하고, 다음에 나오는 문자를 읽어 태그 이름을 확인한다. 태그 이름 뒤의 >를 만나기 전까지 모든 문자를 태그의 일부로 처리하고, 태그 이름이 끝난 후 공백이 나오면 속성의 시작으로 간주하여 속성 이름과 값을 읽어들인다. <와 > 사이의 내용을 기반으로 시작 태그(StartTag) 또는 종료 태그(EndTag) 토큰을 생성하며, 태그가 아닌 문자열은 텍스트(Text) 토큰으로 처리한다.
2.4 Tree Construction
- 입력: ["StartTag: body", "Text: Hello", "StartTag: span", "Text: world!", "EndTag: span", "EndTag: body"]
- 출력: ["<body>", {}, ["Hello", ["<span>", {}, ["world!"]]]]
Tree Construction 단계에서는 생성된 토큰을 기반으로 HTML 문서의 DOM 트리를 구성한다. StartTag 토큰을 만나면 새로운 요소 노드를 생성하고, Text 토큰을 만나면 텍스트 노드를 생성하여 현재 요소 노드의 자식으로 추가한다. 새로운 요소 노드는 현재 노드의 자식으로 추가되며, 현재 노드는 스택에 저장되어 자식 노드의 처리가 끝나면 스택에서 팝된다. EndTag 토큰을 만나면 현재 노드의 처리를 종료하고 상위 노드로 돌아가며, 이를 통해 최종적으로 계층적인 DOM 트리가 형성된다.
3. CSS Parsing
css를 parsing하는 과정도 위의 html parsing과정과 동일하다. 바이트 스트림을 디코딩한 후, 토큰화하여 트리를 만든다.
이렇게 HTML로 DOM을 만들고, CSS로 CSSOM을 만들어서 둘을 결합하면(javascript를 통해 DOM을 변경할 수도 있다 예를 들어, appendchild()) render tree가 완성된다. 이를 브라우저에 나타내면(paint) 우리가 보는 웹페이지가 완성된다.
Reference
- W3C. (n.d.). HTML5: A vocabulary and associated APIs for HTML and XHTML. Retrieved from https://dev.w3.org/html5/spec-LC/parsing.html
- MDN Web Docs. (n.d.). Parse. Retrieved from https://developer.mozilla.org/en-US/docs/Glossary/Parse
- MDN Web Docs. (n.d.). CSSOM. Retrieved from https://developer.mozilla.org/en-US/docs/Glossary/CSSOM
- Programming Soup. (n.d.). Browser Rendering. Retrieved from https://programmingsoup.com/browser-rendering
'Computer Science' 카테고리의 다른 글
CPU 스케줄링 (0) | 2024.07.19 |
---|---|
왜 리눅스는 서버OS로 많이 사용되는가: 리눅스 커널 (2) | 2024.07.16 |