탐색으로 돌아가기
Browser12 / 24 단계

CSSOM

스타일 트리

CSS를 파싱해 만든 객체 모델. 어떤 요소에 어떤 스타일이 적용되는지(상속·우선순위 포함) 계산한다.

CSSOM(CSS Object Model)은 브라우저가 모든 CSS 소스(외부 stylesheet, <style> 블록, 인라인 style, User-Agent 기본 스타일시트)를 bytes → characters → tokens → nodes 순서로 파싱해 만든 트리 형태의 객체 모델로, DOM과 별개로 존재하지만 최종적으로 결합되어 각 요소의 스타일을 계산하는 데 쓰인다. CSSOM은 부분적으로 적용할 수 없는데, 뒤에 나오는 규칙이 앞의 규칙을 덮어쓸 수 있어(cascade) CSSOM이 완성되기 전까지는 어떤 요소도 안전하게 렌더링할 수 없기 때문에 CSS는 기본적으로 렌더링 차단(render-blocking) 자원이다. 각 요소의 최종 스타일을 결정할 때 브라우저는 캐스케이드 원점/레이어(origin & importance) → 명시도(specificity) → 소스 순서(source order) 순으로 충돌을 해소하고, 상속(inheritance)으로 부모의 상속 가능 속성을 물려받은 뒤, 상대값(em, %, currentColor 등)을 절대값으로 환산하는 값 처리 단계(specified → computed → used → actual value)를 거친다. 명시도는 (인라인, ID, 클래스/속성/의사클래스, 타입/의사요소)의 4자리 가중치로 계산되며 !important와 CSS @layer(cascade layer)는 명시도를 넘어서는 우선순위 계층을 만든다. 최종 계산된 스타일은 getComputedStyle(el)로 조회할 수 있으며, 이것이 렌더 트리에 부착되는 실제 스타일이다.

내부 구성

스타일 규칙 (Style Rules / CSSRule)
선택자와 선언 블록의 쌍. document.styleSheets[i].cssRules로 접근하는 CSSOM의 최소 구성 단위
선택자 (Selectors)
규칙이 어떤 요소에 적용될지 매칭하는 패턴. 브라우저는 성능을 위해 오른쪽에서 왼쪽으로 매칭한다
명시도 (Specificity)
여러 규칙이 충돌할 때 이기는 규칙을 (ID, class, type) 가중치로 계산해 결정
캐스케이드 (Cascade)
원점(UA/user/author)·@layer·importance·명시도·소스순서를 순서대로 적용해 승자 선언을 확정하는 알고리즘
상속 (Inheritance)
명시적으로 지정되지 않은 상속 가능 속성을 부모의 계산값에서 물려받는 메커니즘
계산된 스타일 (Computed Style)
캐스케이드·상속·초기값 해소 후 각 요소가 갖는 최종 속성값 집합. 렌더 트리에 부착됨
캐스케이드 레이어 (@layer / Cascade Layers)
명시도보다 상위에서 우선순위 계층을 만들어 스타일 충돌을 예측 가능하게 관리
User-Agent 스타일시트
브라우저 기본 스타일. 캐스케이드에서 가장 낮은 author 이전 원점으로 기본값을 제공
렌더링 차단 (Render Blocking)
CSSOM이 완성될 때까지 렌더링을 막아 스타일 없는 콘텐츠(FOUC)를 방지하는 브라우저 정책

핵심 포인트

  • CSSOM은 bytes→characters→tokens→nodes→CSSOM 트리로 파싱되며 DOM과 병렬로 구성된다
  • CSS는 render-blocking: CSSOM이 완성되기 전에는 첫 페인트가 지연된다 (미디어 쿼리로 비차단화 가능)
  • 캐스케이드 우선순위: 원점/importance/@layer > 명시도(specificity) > 소스 순서
  • 명시도는 (inline, ID, class·attr·pseudo-class, type·pseudo-element) 4자리로 계산된다
  • 상속(inheritance): color, font 등은 부모에서 물려받고, inherit/initial/unset/revert 키워드로 제어
  • 값 처리 파이프라인: specified value → computed value → used value → actual value
  • getComputedStyle()은 캐스케이드·상속·기본값이 모두 반영된 최종 값을 반환한다

심화

면접에서 자주 나오는 함정: CSS는 render-blocking이지만 parser-blocking은 아니다. HTML 파서는 CSS를 기다리며 DOM을 계속 만들 수 있으나, <script>(특히 인라인/동기 스크립트)를 만나면 그 스크립트가 CSSOM을 읽을 수 있으므로 브라우저는 앞선 CSS의 CSSOM 구축이 끝날 때까지 스크립트 실행을 미루고, 그동안 DOM 파싱도 멈춘다. 즉 'CSS가 JS를 막고, JS가 DOM을 막는' 연쇄가 생긴다. 최적화 포인트는 media='print'나 미디어 쿼리로 조건부 CSS를 비차단으로 만들고, critical CSS를 인라인하고, 나머지를 지연 로드하는 것이다. 또 하나 놓치기 쉬운 점: 명시도 계산에서 :is()/:not()/:has()의 인자 중 가장 높은 명시도를 취하지만 :where()는 항상 명시도 0이라는 것, 그리고 인라인 스타일과 !important, @layer, 트랜지션/애니메이션이 얽히면 캐스케이드 순서가 '@layer 밖의 일반 선언 → @layer 안의 선언'처럼 직관과 반대로 동작한다는 점이다. 이런 세부는 실제 대규모 CSS 아키텍처(디자인 시스템)에서 스타일이 왜 안 먹히는지를 디버깅할 때 결정적이다.

쉽게 말하면 각 요소에 '어떤 옷(스타일)을 입힐지' 규칙표를 정리해 두는 것.

면접 예상 질문

#CSSOM#specificity#상속#렌더 블로킹