CSS 이야기: 속성 값 처리 방식

사용자 에이전트는 CSS를 이용해서 스타일을 요소에 적용시킵니다. 이렇게 스타일이 최종적으로 적용될 때까지 속성 값은 최대 네 번 처리되는데 CSS 2.1 권고안은 그 과정을 값 지정, 캐스케이딩, 상속을 다루는 6장에서 설명하고 있습니다. 이 과정에서 핵심이 되는 것은 “지정된 값(specified values)”, “산출 값(computed values)”, “사용되는 값(used values)”, “실제 값(actual value)”인데 이 부분을 중심으로 CSS의 속성 처리 방식을 알아보겠습니다.

† 본문 내용에 제시된 설명의 상당 부분은 제가 권고안을 바탕으로 해석한 것입니다. 따라서 정확하지 않을 확률이 높으니 이 점을 감안하시기 바랍니다. ^^;

개요

사용자 에이전트는 문서를 해석해서 문서 구조(document tree)를 만든 다음 문서 구조상의 모든 요소에 media 유형이 지원하는 모든 속성에 대한 값을 지정해야 합니다. 최종적으로 속성에 적용되는 값은 네 단계의 계산을 거치는데 먼저 정확한 값을 확인하고(지정된 값) 이 값을 상속(inheritance)에 사용될 값(산출 값)으로 변환합니다. 그 다음 변환된 값을 필요에 따라 절대 값으로 바꾸고(실제 값) 마지막으로 주어진 환경에서 구현이 불가능한 값일 경우에 적당한 값으로 변환합니다(실제 값).

지정된 값

“지정된 값”의 원문은 “specified values”인데 의미상으로 정확히 일치하지 않지만 마땅한 대치어가 생각나지 않네요. ^^;

사용자 에이전트는 먼저 아래 열거한 순서대로 지정된 값을 처리해서 각각의 속성 값으로 지정합니다.

  1. 캐스케이드로 값이 얻어지면 그 값을 사용한다.
  2. 상속이 되는 속성이고, 해당 요소가 문서 구조에서 최상위(root) 요소가 아닐 경우에는 부모 요소의 산출 값을 사용한다.
  3. 그 외의 경우에는 속성 정의에 지정되어 있는 해당 요소의 초기 값(initial value)을 사용한다.

첫 번째 캐스케이드 단계에서는 제작자(author) 스타일 시트 뿐 아니라 사용자 스타일 시트와 사용자 에이전트 스타일 시트가 전부 사용됩니다. 다시 말해서 직접 작성한 CSS에 값을 지정하지 않아도 브라우저의 기본 스타일 시트에 값이 지정되어 있으면 그 값을 사용한다는 뜻입니다. 일단 이 단계가 끝나고 남은 속성은 inherit 값을 갖거나 아니면 아예 값이 지정되지 않은 상태입니다.

두 번째 단계에서는 상속을 처리합니다. 속성 값이 명시적으로 inherit이거나 해당 속성이 부모 요소의 값을 상속한다고 정의되어 있을 경우에는 부모 요소의 산출 값을 사용합니다.

세 번째 단계에서는 속성 값이 없고 상속도 받지 않는 요소와 문서의 최상위 요소만 남습니다. 이럴 경우에는 모두 해당 속성의 초기값이 사용됩니다.

산출 값

캐스케이딩 과정에서 지정된 값이 할당되고 다시 산출 값으로 변환됩니다. 예를 들어서 상대 URI가 절대 URI로 변환되고, em이나 ex 단위가 픽셀이나 절대 길이로 변환되는데 이 과정까지도 사용자 에이전트는 문서를 렌더링하지 않습니다. 만약 사용자 에이전트가 URI를 절대 URI로 변환할 수 없는 경우에는 지정된 값을 그냥 사용합니다.

지정된 값이 inherit이 아닐 경우에 속성의 산출 값은 속성 정의에 명시된 “산출 값” 설명에 따라 변환됩니다. inherit일 경우에는 부모 요소의 산출 값을 상속받는데 앞서 설명한 “지정된 값” 계산 방식에 따라서 해당 요소의 지정된 값과 산출 값은 모두 부모 요소의 산출 값이 됩니다. 예를 들어서 다음과 같은 CSS와 마크업을 생각해보지요.

body { font-size: 10pt }
h1 { font-size: 130% }

<body>
	<h1>a <em>large</em> heading</h1>
</body>

먼저 font-size 속성 정의를 살펴보겠습니다(필요한 부분만 가져왔습니다).

'font-size'
    상속 여부:	상속됨
    퍼센트 값:	부모 요소의 폰트 크기를 참조
    산출 값:	절대 길이

h1 요소의 font-size가 퍼센트로 지정됐기 때문에 “퍼센트 값” 항목에 설명된대로 부모 요소(body)의 font-size 값을 기준으로 계산됩니다(10pt * 130% = 13pt). 따라서 최종 산출 값은 13pt인데 이 값을 em 요소가 상속받기 때문에 em 요소의 지정된 값과 산출 값은 모두 13pt가 됩니다. 이렇게 변환된 산출 값을 상속하는 이유는 불필요한 변환 과정을 줄이기 위해서라고 생각됩니다. 다시 말해서 부모 요소의 지정된 값을 상속받으면 다시 한 번 절대 단위나 절대 URI로 변환을 해야 하는데 산출 값을 상속받으면 이런 과정이 한 번만 이루어지니까요.

산출 값은 해당 요소에 해당 속성이 적용되지 않을 경우에도 계산됩니다. 하지만 일부 속성은 속성 적용 여부를 고려해서 요소에 적용될 변환 값을 정의할 수도 있습니다.

사용되는 값

산출 값은 문서를 서식화하지 않는 선에서(레이아웃을 신경쓰지 않고) 최대한 계산됩니다. 하지만 일부 속성 값은 문서 레이아웃이 만들어진 후에만 결정됩니다. 예를 들어서 width 속성 값이 퍼센트 값으로 지정될 경우에는 포함 블록(containing block)의 width가 결정되지 않으면 계산할 수가 없습니다. “사용되는 값”은 이렇게 의존성을 고려해서 산출 값을 절대 값으로 변환한 결과입니다.

실제 값

원칙적으로 실제 문서 렌더링에 사용되는 값은 “사용되는 값”입니다. 하지만 상황에 따라서 사용자 에이전트가 이 값을 제대로 처리하지 못하는 경우가 있습니다. 예를 들어서 사용자 에이전트가 보더 두께를 픽셀 값으로만 표현할 수 있는데 사용되는 값이 픽셀이 아닌 절대 길이(예: 2mm)라면 이 값을 다시 픽셀 값으로 변환해야 합니다. 또 색을 제대로 표현하지 못하는 사용자 에이전트는 색상 값을 그레이스케일 값으로 변환할 수도 있습니다. 이렇게 사용자 에이전트의 제한으로 사용되는 값을 제대로 표현할 수 없을 때 적당한 값으로 변환되는데 이 값이 최종적인 실제 값입니다.

변환 예

margin-left 속성을 예로 들어서 실제로(정확하다고 보장할 수는 없지만^^;) 계산을 해보겠습니다. 다음과 같은 마크업과 CSS가 있다고 생각해보지요.

<div>
	<p>some text</p>
</div>

div { width: 300px; }
p { width: 100px; margin-left: 10%; }

margin-left의 속성 정의는 다음과 같습니다(필요한 부분만 가져왔습니다).

'margin-left'
    퍼센트 값:  	포함 블록의 width를 참조
    산출 값:  	지정된 퍼센트 값이나 절대 길이(the percentage as specified or the absolute length)

스타일 시트에 p 요소의 margin-left가 10%로 지정되어 있습니다. 값이 있고, 캐스케이딩에 영향을 주는 다른 규칙이 없으므로 “지정된 값”은 10%가 됩니다. 다음에 “산출 값”을 계산해야 하는데 속성에 지정된 퍼센트 값을 사용한다고 정의되어 있으므로 이 값도 10%가 됩니다. 앞서 확인했던 font-size 속성에는 산출 값이 절대 길이라고 정의되어 있는데 퍼센트 값도 부모 요소의 값을 이용해서 절대 길이로 바꿀 수 있었습니다. 하지만 margin-left 속성에 퍼센트 값이 사용될 경우에는 포함 블록의 width를 기준으로 해석되므로 산출 값을 “사용되는 값”으로 다시 변환해야 합니다. 결국 포함 블록 width의 10%인 30px이 얻어지는데 브라우저가 이 값을 그대로 표현할 수 있으면 “실제 값”으로 더 이상 변환되지 않고 그냥 최종 값으로 사용됩니다.

의문점

몇 가지 의문점이 있는데 그 중 가장 중요한 문제만 생각해보겠습니다. 바로 산출 값에서 “절대 길이(absolute length)”가 무엇을 의미하는가입니다. CSS에서는 길이 단위를 절대 길이 단위와 상대 길이 단위로 나누는데 px은 상대 길이 단위에 속합니다. 따라서 절대 길이가 절대 길이 단위로 표현된 모든 길이 값을 의미한다면 산출 값을 결정할 때 px 값도 인치(in)나 포인트(pt) 같은 절대 길이 단위로 변환되어야 합니다.

하지만 CSS 2.1의 길이 설명에 제시된 예제를 보면 절대 길이가 절대 길이 단위를 사용한 값을 의미하는 것 같지 않습니다. 예제는 다음과 같습니다.

p { font-size: 10px }
p { line-height: 120% }  /* 120% of 'font-size' */

권고안에서는 p 요소의 line-height 산출 값이 12px이고, p 요소의 자식 요소도 이 값을 상속받는다고 명시하고 있습니다. 이 사실을 봤을 때 “절대 길이”라는 표현이 “확실한 길이”, 또는 “실제 길이” 등을 의미하는 것이 아닌가 하는 생각이 듭니다.

또한, W3C의 메일링 리스트WCAG 상대 단위와 절대 단위 페이지에는 웹 컨텐츠 접근성 지침(Web Content Accessibility Guidelines)에서 사용하는 “절대 길이 단위”라는 표현에 px도 포함시켜야 한다는 의견이 있습니다. 일반적인 웹 환경에서 픽셀이 대부분 절대 길이 단위처럼 취급되는 것이 한 가지 이유이고, 접근성 측면에서 WCAG가 텍스트 크기에 따라 길이가 늘어날 수 있도록 상대 단위 사용을 권장하는데 이 상대 길이 단위에 포함되는 픽셀을 사용하면 실제로 유동적인 길이를 표현하지 못하기 때문에 혼란이 있을 수 있다는 점이 다른 이유입니다. 이 부분은 WCAG 1.0의 체크포인트 3.4에 있는데 실제 내용은 다음과 같습니다.

마크업 언어와 스타일 시트의 속성 값에는 절대 단위(absolute units)보다 상대 단위(relative units)를 사용하라.
CSS를 예로 들면 절대 단위인 pt, cm 길이보다 em이나 퍼센트 길이를 사용하라. 절대 단위를 사용할 때에는 렌더링된 컨텐츠를 불편 없이 이용할 수 있는지 확인해야 한다.

WCAG 1.0이 오래된 권고안이기는 하지만 이런 점이 픽셀 단위에 대한 W3C의 모호함을 단적으로 보여주는 예라고 생각합니다.

마치며

간단히 정리하려고 했는데 잘 이해되지 않는 부분이 있어서 쉽지가 않네요. 그래도 이렇게 글을 작성하면서 속성 값이 처리되는 방식을 어느 정도 이해할 수 있게 되었습니다. 전에도 속성 정의에 제시된 산출 값이 정확히 무엇을 의미하는지 고민했던 적이 있었거든요.

서두에도 말씀드렸지만 이 글의 내용은 정확하지 않을 수 있습니다. 혹시 속성 값 산출 방식이나 절대 길이와 픽셀에 관한 정보나 의견이 있으면 꼭 알려주시기 바랍니다.

다음 글에서는 W3C 권고안에서 자주 사용되는 “CSS를 따르는 사용자 에이전트(conforming user agent)”가 무엇을 의미하는지 알아보겠습니다.

태그: , ,

댓글이 닫혔습니다.