CSS 시각적 서식 모델(Visual Formatting Model)은 문서 내 각 요소를 처리하고 변환하여 CSS 박스 모델(CSS Box Model)을 따르는 하나 이상의 박스를 생성하는 알고리즘에 해당하는 모델입니다.
생성된 박스의 레이아웃은 다음과 같은 여러 속성에 따라 결정됩니다.
- 크기(Dimensions)
- 유형(Type) — 블록(block), 인라인(inline), 인라인 블록(inline-block), 원자적 인라인 레벨(atomic inline-level)
- 위치 지정(Positioning) — 일반 흐름(normal), 부동(float), 절대 위치(absolute)
- 문서 내 자식 요소 및 인접 요소와의 관계
- 외부 정보 — 뷰포트(viewport)의 크기, 이미지의 너비·높이 등
CSS 박스 생성 방식
처리된 요소는 다음 두 가지 유형으로 박스가 생성됩니다.
블록 레벨(Block Level) 요소
- 요소의 앞뒤로 강제 줄바꿈이 발생하며, 콘텐츠가 전체 폭을 필요로 하지 않더라도 사용 가능한 전체 너비를 차지합니다.
- 예시: <div>, 제목 태그(<h1> ~ <h6>) 등
인라인 레벨(Inline Level) 요소
- 앞뒤로 줄바꿈이 발생하지 않으며, 콘텐츠에 필요한 만큼의 너비만 차지합니다.
- 예시: <span>, <strong>, <em> 등
예제 1 — 블록 레벨 박스 생성
블록 레벨 박스 생성 예제를 살펴보겠습니다.
<!DOCTYPE html>
<html>
<head>
<title>HTML Heading</title>
<style>
form {
width:70%;
margin: 0 auto;
text-align: center;
}
* {
padding: 2px;
margin:5px;
}
input[type="button"] {
border-radius: 10px;
}
#headingPreview h2{
background-color: #DC3545;
}
</style>
</head>
<body>
<form>
<fieldset>
<legend>HTML Heading</legend>
<input type="text" id="textSelect" placeholder="Type Heading Here...">
<div id="radioBut">
<label>H1</label><input class="radio" type="radio" name="heading" value="h1" checked >
<label>H2</label><input class="radio" type="radio" name="heading" value="h2">
<label>H3</label><input class="radio" type="radio" name="heading" value="h3">
<label>H4</label><input class="radio" type="radio" name="heading" value="h4">
<label>H5</label><input class="radio" type="radio" name="heading" value="h5">
<label>H6</label><input class="radio" type="radio" name="heading" value="h6">
</div>
<div>Heading Preview: <span id="headingPreview">Output will show up here</span></div>
<input type="button" onclick="changeHeading()" value="Preview">
</fieldset></form>
<script>
var textSelect = document.getElementById("textSelect");
var headingPreview = document.getElementById("headingPreview");
function changeHeading() {
if(textSelect.value === '')
headingPreview.innerHTML = 'Write a Heading';
else{
for(var i=0; i<6; i++){
var radInp = document.getElementsByClassName('radio')[i];
if(radInp.checked === true && textSelect.value !== ''){
headingPreview.innerHTML = '<'+radInp.value+'>'+textSelect.value+'</'+radInp.value+'>';
headingPreview.innerHTML += '<'+radInp.value+'>'+textSelect.value+'</'+radInp.value+'>';
}
}
}
}
</script></body></html>실행 결과
위 코드는 다음과 같은 결과를 출력합니다.
텍스트 입력란이 비어 있는 상태에서 'Preview' 버튼을 클릭한 경우 —

텍스트 입력란에 값이 있고 'h2' 라디오 버튼이 선택된 상태에서 'Preview' 버튼을 클릭한 경우 —

결과에서 볼 수 있듯이, 제목(h1~h6) 요소는 각 줄마다 새로운 블록 박스를 형성하며 한 줄 전체를 차지하는 것을 확인할 수 있습니다.
예제 2 — 인라인 레벨 박스 생성
이번에는 인라인 레벨 박스 생성 예제를 살펴보겠습니다.
<!DOCTYPE html>
<html>
<head>
<title>em element</title>
<style>
form {
width:70%;
margin: 0 auto;
text-align: center;
}
* {
padding: 2px;
margin:5px;
}
input[type="button"] {
border-radius: 10px;
}
em{
background-color: #FF8A00;
}
</style>
</head>
<body>
<form>
<fieldset>
<legend>em-element</legend>
<label for="textSelect">Formatter: </label>
<input id="textSelect" type="text" placeholder="John Doe">
<input type="button" onclick="convertItalic()" value="Check">
<div id="divDisplay"></div>
</fieldset>
</form>
<script>
var divDisplay = document.getElementById("divDisplay");
var textSelect = document.getElementById("textSelect");
function convertItalic() {
for(i=0; i<2; i++){
var italicObject = document.createElement("EM");
var italicText = document.createTextNode(textSelect.value);
italicObject.appendChild(italicText);
divDisplay.appendChild(italicObject);
}
}
</script>
</body>
</html>실행 결과
위 코드는 다음과 같은 결과를 출력합니다.
'Check' 버튼을 클릭하기 전 —

'Check' 버튼을 클릭한 후 —

<em> 요소는 인라인 레벨 박스이므로 줄바꿈 없이 콘텐츠 크기만큼만 공간을 차지하며, 같은 줄에 나란히 배치되는 것을 확인할 수 있습니다.
정리
CSS 시각적 서식 모델은 문서의 모든 요소를 박스로 변환하여 화면에 배치하는 핵심 메커니즘입니다. 요소가 블록 레벨인지 인라인 레벨인지, 그리고 위치 지정 방식이 어떻게 설정되어 있는지에 따라 최종 렌더링 결과가 달라집니다. 따라서 웹 레이아웃을 정확하게 제어하려면 박스 모델과 시각적 서식 모델의 동작 원리를 함께 이해하는 것이 중요합니다.