3 분 소요

[TIL]

WEB - 브라우저

파싱과 DOM 트리 구축

일반 파서

  1. 일반적인 파싱이란?

    문서 파싱은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것.
    파싱 결과는 문서 구조를 나타내는 노드 트리인 파싱 트리(parse tree) 또는 문법 트리(syntax tree) 가 된다.

  2. 문법

    인간의 대화에서는 문법을 조금 지키지 않아도 문맥만 있으면 뜻을 파악할 수 있지만 컴퓨터는 아니다.

    컴퓨터는 무언가를 해석하기 위해 대상이 특정한 어휘와 문법 규칙을 따라야 해석할 수 있다.

    그러니 문맥이 없어도 이러한 규칙들만 잘 따르면 뜻을 파악할 수 있다는 것이다. (문맥 자유 문법)

  3. 파서-어휘 분석기 조합

  • 어휘 분석: 자료를 토큰으로 분해하는 과정. (토큰: 유효하게 구성된 단위의 집합체 / 사전에 등장하는 모든 단어)
  • 구문 분석: 언어의 구문 규칙을 적용하는 과정
  1. 파싱

    1. 파싱 과정 파서는 보통
    • 어휘 분석: 자료를 유효한 토큰으로 분해 (어휘 분석기/토큰 변환기) / 공백이나 줄바꿈 등의 의미 없는 문자를 제거
    • 구문 분석: 언어 구문 규칙에 따라 문서 구조를 분석함으로써 파싱 트리를 생성

2) 파싱 과정의 반복

파서는 어휘 분석기로부터 새 토큰을 받아서 구문 규칙과 일치하는지 확인.

규칙에 맞다면 토큰에 해당하는 노드가 파싱 트리에 추가, 파서는 또 다른 토큰을 요청.

규칙에 맞지 않으면 파서는 토큰을 내부적으로 저장하고 토큰과 일치하는 규칙이 발견될 때까지 요청.

맞는 규칙이 없는 경우(문서가 유효하지 않거나 구문 오류를 포함하고 있는 경우) 예외 처리

3) 변환

파서 트리는 최종 결과물이 아니다.
파싱하면서 문서를 다른 양식으로 변환한다(컴파일)

소스 코드를 기계 코드로 만드는 컴파일러는 파싱 트리 생성 후 이를 기계 코드 문서로 변환.

HTML 파서

HTML 파서는 HTML 마크업을 파싱 트리로 변환한다.

  1. 문법

    전통적인 파서는 HTML에 적용할 수 없다.
    일반적인 파싱은 CSS와 자바스크립트를 파싱하는 데 사용되고,
    HTML은 파서가 요구하는 문맥 자유 문법에 의해 쉽게 정의할 수 없다.
    => HTML은 암묵적으로 태그를 생략하거나 시작/종료 태그를 생략하는 이러한 유연한 문법 때문에 일반적인 파싱으로 전통적인 구문 분석이 불가능해지므로 문맥 자유 문법이 아닌 것이다.

  2. DOM

    파싱 트리는 DOM 요소와 속성 노드의 트리로서 출력 트리가 된다.
    DOM(Document Object Model)은 HTML 문서의 객체 표현이며 외부에 있는 자바스크립트와 같은 HTML 요소의 연결 지점이다.
    이 DOM tree의 최상위 객체는 document 이다.

    HTML과 마찬가지로 DOM은 W3C에 의해 명세가 정해져 있다.

  3. 파싱 알고리즘

    HTML은 일반적인 하향식 또는 상향식 파서로 파싱이 되지 않는다.

    • 언어의 너그러운 속성
    • 잘 알려져 있는 HTML 오류에 대한 브라우저의 관용
    • 변경에 의한 재파싱 (스크립트에서 토큰을 추가하므로 중간중간 파싱이 수정됨)

    이러한 이유때문에 일반적인 파싱 기술을 사용할 수 없으므로 브라우저는 별도의 HTML 파서를 생성한다.

    네트워크 레이어에서 데이터가 들어오면

  • 토큰화: 자료를 유효한 토큰으로 분해하는 어휘 분석기(토큰 변환기)를 통해 토큰화하는 것.
  • 트리 구축: 언어 구문 규칙에 따라 문서 구조를 분석함으로써 파싱 트리를 생성하는 것.

    이 두 과정을 거쳐 파싱하여 document object라는 결과물을 만들어 낸다.

    토큰화는 토큰을 인지해서 트리 생성자로 넘기고, 다음 토큰을 확인하기 위해 다음 문자를 확인하고, 입력의 마지막까지 이 과정을 반복하게 된다.

  1. 토큰화 알고리즘

    알고리즘의 결과물은 HTML 토큰이다. 알고리즘은 현재 상태에 따라 다음 상태의 결과가 다르게 나온다.

    ex) 토큰화 과정

    <html>
      <body>
        Hello world
      </body>
    </html>
    

    초기 상태는 “자료 상태”이다.

    < 문자가 나오면 상태는 “태그 열림 상태”로 변경된다.
    그러고 (a~z)문자가 나오면 “시작 태그 토큰”을 생성하고 상태는 “태그 이름 상태”로 변경된다.
    여기서 생성된 토큰은 각 문자에 따른 토큰이다. (맨 처음은 html 토큰인 것)
    > 문자가 나오면 현재 토큰이 발행되고, 상태는 다시 “자료 상태”로 변경된다.

    해당 작업을 계속 처리해 나가다가, body 태그를 닫고 그 다음은
    Hello에서 H라는 문자를 만나면 문자 토큰이 생성되고 발행된다.
    이는 < 문자를 만날 때까지 진행된다. 토큰 발행은 Hello World의 각 문자를 위한 문자 토큰을 발행한다.

    <를 만나면 다시 “태그 열림 상태”가 된고, / 문자를 만나면 종료 태그 토큰을 생성, “태그 이름 상태”로 변경된다.
    마찬가지로 > 문자를 만날 때까지 유지되며 계속 동일하게 처리된다.

  2. 트리 구축 알고리즘

    트리 구축이 진행되는 동안에도 문서 최상단에서는 DOM 트리가 수정되고 요소가 추가된다. 토큰화에 의해 발행된 각 노드는 트리 생성자에 의해 처리된다.

    DOM 트리에 요소를 추가하는 것이 아니라면 열림 상태의 요소는 스택에 추가된다.
    이 때 부정확한 중첩과 종료되지 않은 태그를 교정한다.

    ex) 트리 구축/생성 과정

    <html>
      <body>
        Hello world
      </body>
    </html>
    

    트리 구축 단계에서는 토큰화 단계에서 만들어진 토큰들을 받아 처리된다.
    맨 처음 html 토큰을 받으면 “html 이전” 모드가 되며 HTMLhtmlElement 요소를 생성하고 문서 객체의 최상단에 추가된다.

    html 안으로 들어오면 “head 이전”모드로 바뀌고, 이 때 body 토큰을 받게 된다.
    head 토큰은 없더라도 HTMLHeadElement는 알아서 생성되어 트리에 추가된다.

    “head 안쪽”모드로 이동하여 아무 것도 없으므로 바로 “head 다음” 모드로 간다.
    body 토큰은 처리되면서 HTMLBodyElement가 생성되어 추가되면서 “body 안쪽”모드가 된다.

    이런 식으로 동일하게 처리되어서 EOF 토큰을 받으면 파싱을 종료하게 된다.

  3. 스크립트 태그

    스크립트는 파싱과 실행이 동기적으로 이루어진다. (파싱한 후 바로 실행)

    html 파서는 script 태그를 만나면 실행을 중단하기 때문에 고려하여 script 태그는 보통 body 태그 맨 밑에 넣는다.

    script가 외부에 있는 경우(부트스트랩, jQuery 등)에도 데이터를 다 가져올 때까지 실행은 중단된다.

    이를 위해 HTML5에서는 defer와 async가 도입되었다. (로드는 비동기적으로 처리되게하고, 실행은 document 파싱이 다 끝날 때까지 지연시켜두는 것)

  4. 브라우저의 오류 처리

    브라우저는 오류가 생기면 알아서 구문을 교정한다.

    파서는 토큰화된 입력 값을 파싱하여 문서를 만들고 문서 트리를 생성한다.

    파서는 이러한 오류들은 처리해야 한다.

    1. 추가하려는 태그가 금지된 것일 때 허용된 태그를 먼저 닫고 금지된 태그는 외부에 추가한다.
    2. 파서가 직접 요소를 추가하지 않는다.
    3. 태그를 추가하거나 무시할 수 있는 상태가 될 때까지 요소를 닫는다.

댓글남기기