• 브라우저의 동작 (2) - CSS : 내 말대로 꾸며줘!

    2020. 6. 9.

    by. 나나 (nykim)

    320x100

     

     

     

    프롤로그

    아직 개인홈피*^-^*가 유행하던 시절, 저도 홈페이지를 만들고 싶어서 금손님들의 스킨을 다운받곤 했었는데요. 그때 css파일을 보고 이건 뭘까... 얘는 왜 메모장으로 열리지... 했던 기억이 납니다. 네, TMI였습니다. 그래서(?) 오늘은 CSS를 배웠습니다.

    익숙치 않은 개념이라 혹시나 잘못된 내용이 있으면 언제든 말씀 부탁드립니다. (천사님들 사랑합니다!)

     

     

     


     

     

    CSSOM

     

     

    CSS로 트리 만들기

    지난 글에서 브라우저는 HTML을 보고 토큰화 과정을 통해 DOM을 만들어낸다고 했습니다. CSS도 이와 유사합니다.

    한쪽에서 마크업을 해독하는 동안, 한쪽에서는 스타일시트를 보고 CSS Object Model(=CSSOM)을 만들어냅니다.

     

    <link href="style.css" rel=stylesheet/>

     

    자, 브라우저가 HTML을 보고 뚝딱뚝딱 DOM을 설계하는 동안 head 섹션에 있는 외부 스타일시트를 만났습니다.

    페이지를 렌더링하기 위해 이 CSS가 필요하다고 생각되면 자원을 요청해 내용을 받아옵니다.

     

    body { font-size: 16px }
    p { font-weight: bold }
    span { color: red }
    p span { display: none }
    img { float: right }

     

    우리가 작성한 CSS를 잘 받아왔네요. 그런데 브라우저는 어떻게 이걸 찰떡같이 알아듣고 스타일링을 해주는 걸까요?

     

    우선, HTML과 마찬가지로 CSS 규칙을 브라우저가 이해할 수 있는 형태로 바꿔야 합니다.

    이 과정은 DOM과 비슷하게, Bytes → Characters → Tokens → Nodes → Object Model 의 흐름으로 이어집니다.

    이렇게 만들어진 CSSOM 역시 트리 구조를 지니고 있어 CSSOM 트리라고 부르기도 합니다.

     

     

     

    출처: Google developers

     

     

     

    페이지 내 모든 요소들에게 적용할 스타일을 계산할 때, 브라우저는 해당 노드에 적용할 수 있는 가장 일반적인 규칙부터 시작합니다.

    예를 들면, <p>는 <body>의 자식이니까 <body>에 적용된 스타일인 font-size:16px이 적용됩니다.

    그 다음에 좀 더 세부적인 스타일이 적용됩니다. <p>는 font-weight:bold;스타일이 추가로 적용됩니다.

     

    한편, 모든 브라우저는 'User agent style'이라는 기본 스타일 세트를 제공하고 있습니다. 우리가 'reset.css' 등으로 오버라이딩하지 않으면 이 기본 스타일이 적용됩니다.

     

     

     

     

    그러니까 CSS는 짧게

    h1 { color: blue;} /*more faster*/
    div p { color: red;} 

    여담으로 불필요한 캐스케이딩이 영 좋지 못한 이유도 이와 관련이 있습니다.

    h1 {color: blue;}div p {color: red;}보다 빠른 이유입니다.

    후자의 경우 트리에서 p를 찾고, 한 번 거슬러 올라가 그 부모가 div인지 판단해야 하기 때문입니다.

    할 일이 많아지면 시간이 더 오래 걸리리는 건 당연하죠. (참고: Udacity 강의)

     

     

     

     

    선 파서, 후 렌더링 

    그런데 이렇게 CSSOM을 만드는 동안 렌더링은 일어나지 않습니다.

    'a 태그는 파란 색으로 칠해 줘.'라고 말하자마자 쌩하니 가버리면, '근데 여기 있는 a는 회색으로... 야! 내 말 다 듣고 가!' 같은 상황이 일어나면 안 되니까요 💦

     

    그래서 CSS가 완전히 파싱되기 전까지 브라우저는 렌더링 작업을 진행하지 않습니다.

    이를 달리 말하면 CSS를 꾹꾹 눌러 압축해서 제공하면 렌더링 최적화가 가능하다는 뜻이죠. 

     

    (사족으로 HTML과 JavaScript도 페이지 렌더링을 막습니다.

    HTML의 경우 돔이 만들어져야 렌더링을 할 수 있을 테니 큰 의미는 없지만,

    JavaScript의 경우 <body> 태그 하단에 작성하는 이유도 이와 관련이 있습니다.)

     

     

     


     

     

     

    렌더 트리

     

    이렇게 DOM과 CSSOM 트리가 형성되면 이 둘을 결합해 '렌더 트리'를 만듭니다.

     

    이때 브라우저는 '눈에 보이는 노드'들만 렌더링합니다. 즉, <meta>나 <script> 태그 등은 포함되지 않으며, display:none;처리 된 요소들도 포함되지 않습니다.

    그리고 이러한 노드들에게 적절한 CSSOM 규칙을 적용합니다.

    최종적으로는 화면에 표시되는 모든 노드의 콘텐츠와 스타일 정보를 포함하는 렌더 트리가 구현됩니다.

     

     

     

     

     

     

    예를 들어, 위와 같은 형태의 DOM과 CSSOM이 있다고 하면 아래처럼 렌더 트리가 구성됩니다.

     

     

     

     

     


     

     

     

    레이아웃 (리플로우)

     

    표시할 노드와 해당 노드의 스타일의 계산까지 끝났습니다. 그럼 이제 이대로 픽셀로 그려내면 되는 걸까요?

    아직 페인트를 칠하기에는 단계 하나가 남아 있습니다.

     

    기기의 뷰포트 내에서 이 노드들을 정확히 어디에 얼마만큼 그려야하는지는 각을 재는 작업이 필요하거든요. 

    예를 들어, width:50%는 무엇에 대한 50퍼센트인지 알아야 합니다. font-size: 4em은 어디서부터 계산해야하는지도 알아야하고요.

    WebKit은 이 단계를 Layout, Gecko는 Reflow라고 부릅니다.

     

    브라우저는 렌더 트리의 루트부터 순회화면서 페이지 내 각 개체의 정확한 위치와 크기를 파악해냅니다.

    일반적인 '흐름' 상, 나중에 나오는 요소는 앞서 나온 요소에 영향을 미치지 않기 때문에 배치는 왼쪽->오른쪽 또는 위->아래로 진행됩니다.

    이렇게 측정된 값은 화면에서 절대적인 픽셀값으로 변환됩니다.

    'width: 50%는 250px이고, font-size:4em은 22px이야'처럼 정확한 요구명세서를 받게 되는 셈이죠.

     

    만약 width: 50vw상태에서 브라우저 너비가 달라진다면 이 값은 다시 계산해야 합니다.

    하지만 렌더 트리에 변화가 있는 것은 아니므로, Layout(Reflow) 단계부터 다시 계산합니다.

     

    또, 부모요소에게 Reflow 일어나면 자식요소도 영향을 받기 때문에 최대한 DOM 구조 하위에 있는 노드에게 변화를 주는 것이 좋습니다.

     

     


     

     

    페인팅

     

    드디어 붓을 들어 화면에 그리는 단계입니다! 

    레이아웃 프로세스가 완료되면 브라우저는 'Paint Setup' 및 'Paint' 이벤트를 발생시킵니다. 드디어 렌더링 트리를 화면의 픽셀로 변환하게 됩니다.

    '여기 그라디언트도 넣고 그림자도 넣어줘!'처럼 복잡한 일을 많이 시킬수록 처리하는 데 시간이 오래 걸립니다. 

     

     

     

     

    앗, 미안한데 다시 그려줄래?

    여기까지 잘 그려냈는데, 모종의 이유로 다시 그려야할 때가 있습니다.

    사용자를 위한 인터랙션이나 애니메이션 등이 들어가서 크기와 색상이 달라지는 경우, 브라우저는 다시 레이아웃을 그리고 칠하는 작업(Reflow & Repaint)을 해야합니다.

     

    음... 색깔이 달라져서 다시 칠하는 것까진 참을 수 있는데 위치 계산까지 다시 해야한다면 브라우저도 화가 날 수 있습니다.

    그래서 이러한 '다시 그리기' 작업을 최대한 피하는 것도 CSS 최적화의 방법 중 하나입니다.

    CPU의 레이아웃 재계산 없이 GPU만으로 처리 가능한 transform 속성 등을 쓰는 거죠.

     

    브라우저에게 악덕 클라이언트가 되지 않기 위해(?) 이것도 다음에 정리해 보려구요 ;)

     

     

     


     

     

    되짚기

     

    즉, 브라우저(정확히는 렌더링 엔진이지만요!)가 우리의 마크업과 스타일을 이해하는 과정은 아래와 같았습니다.

     

    1. HTML을 보고 DOM을 만든다.

    2. CSS를 보고 CSSOM을 만든다.

    3. DOM과 CSSOM을 합쳐 렌더 트리를 만든다.

    4. 렌더 트리에서 레이아웃을 실행하여 각 노드의 위치와 크기를 계산한다.

    5. 각 노드를 노드를 화면에 페인팅한다.

     

     

    출처: Udacity

     

     

    만약 DOM이나 CSSOM이 수정된다면, 화면에서 다시 렌더링할 픽셀을 파악하기 위해 위의 프로세스를 반복해야합니다.

    따라서 1~5단계에 소요되는 시간을 줄이면 빠르고 쾌적한 사용자 경험을 줄 수 있습니다.

     

     


     

     

    참고 글

     

    Render-tree Construction, Layout, and Paint :: Google Developers

    The Critical Rendering Path :: Udactiy

    브라우저는 웹페이지를 어떻게 그리나요? :: 네이버 포스트

    브라우저는 어떻게 동작하는가? :: D2

     

     

    시리즈의 이전 글: 브라우저의 동작 (1) - HTML : 내 마크업을 이해해 줘!
    시리즈의 다음 글: 브라우저의 동작 (3) - 웹 페이지 최적화

     

    728x90

    댓글