안녕하세요! 프론트엔드 개발 강사입니다. 이번에 질문하신 내용은 CSS 레이아웃의 가장 근본적인 규칙인 '서식 컨텍스트(Formatting Contexts)'에 대한 문서네요!
"왜 이 안에서는 마진 병합이 안 일어나지?", "왜 float 된 요소를 부모가 못 감싸고 높이가 0이 되지?" 웹 개발을 하다 보면 마주치는 이런 알쏭달쏭한 레이아웃 문제들의 해답이 모두 이 문서 안에 들어 있습니다. 영문 문서라 이해하기 조금 까다로우셨을 텐데, 제가 실무에서 자주 겪는 예시들을 더해서 아주 이해하기 쉽게, 구어체로 싹 번역해 드릴게요! 😊
이 문서는 서식 컨텍스트(formatting contexts)라는 개념을 소개합니다. 서식 컨텍스트에는 블록 서식 컨텍스트(block formatting contexts), 인라인 서식 컨텍스트(inline formatting contexts), 플렉스 서식 컨텍스트(flex formatting contexts) 등 여러 가지 종류가 있습니다. 이들이 기본적으로 어떻게 동작하는지, 그리고 이 동작 방식들을 우리가 어떻게 활용할 수 있는지 알아볼게요.
웹 페이지에 있는 모든 요소는 서식 컨텍스트(formatting context)의 일부입니다. 쉽게 말해 '콘텐츠를 특정한 방식(규칙)으로 배치하기 위해 정의된 구역'이라고 생각하시면 됩니다. 블록 서식 컨텍스트(BFC, Block Formatting Context)는 자식 요소들을 블록 레이아웃 규칙에 따라 배치하고, 플렉스 서식 컨텍스트(Flex Formatting Context)는 자식 요소들을 플렉스 아이템(flex items)으로 배치합니다. 각각의 서식 컨텍스트는 그 구역 안에 있을 때 레이아웃이 어떻게 동작해야 하는지에 대한 고유한 규칙들을 가지고 있습니다.
💡 강사의 부연 설명: "서식 컨텍스트(Formatting Context)가 무슨 뜻인가요?"
'컨텍스트'를 '독립된 세상'이나 '국가'라고 상상해 보세요. 대한민국에서는 우측통행을 하고, 영국에서는 좌측통행을 하는 것처럼요. '블록 국가(BFC)' 안에 들어간 요소들은 무조건 위에서 아래로 쌓이는 법을 따라야 하고, '플렉스 국가(Flex)' 안에 들어간 요소들은 나란히 서거나 찌그러지는 등 플렉스 국가의 법을 따라야 하는 겁니다!
블록 레이아웃 규칙을 사용하는 문서의 가장 바깥쪽 요소(최상위 요소)는 최초의, 즉 초기 블록 서식 컨텍스트(initial block formatting context)를 설정합니다. 이것은 문서의 뿌리인 <html> 요소 안에 있는 모든 요소가 블록 및 인라인 레이아웃의 규칙에 따라 일반 흐름(normal flow)으로 배치된다는 것을 의미합니다. BFC(블록 서식 컨텍스트)에 참여하는 요소들은 CSS 박스 모델(Box Model)이 정한 규칙을 따르게 되며, 이 규칙은 한 요소의 마진(margins), 보더(borders), 패딩(padding)이 같은 컨텍스트 안에 있는 다른 블록들과 어떻게 상호작용하는지를 정의합니다.
<html> 요소만이 블록 서식 컨텍스트를 만들 수 있는 유일한 요소는 아닙니다. 어떤 블록 레벨 요소든 특정한 CSS 속성을 적용하면 자기 자신만의 새로운 BFC를 만들어 낼 수 있습니다.
다음과 같은 상황에서 새로운 BFC가 생성됩니다:
float을 사용해 띄워진(float) 요소들position: absolute 또는 fixed)display: inline-block을 가진 요소들display: table-cell)인 요소들 (여기에는 display: table-* 속성을 사용할 때 브라우저가 생성하는 익명(anonymous) 테이블 셀도 포함됩니다)display: table-caption)인 요소들overflow 값이 visible이 아닌(즉, hidden, auto, scroll 등인) 블록 요소들display: flow-root 또는 display: flow-root list-item을 가진 요소들contain: layout, contain: content, 또는 contain: strict를 가진 요소들column-span 값이 all로 설정된 요소들👨🏫 강사의 실무 팁! "BFC를 왜 일부러 만들까요?"
BFC가 생성되면 그 박스는 외부 세계와 완전히 단절된 "격리 구역"이 됩니다!
1. 내부에 있는 float 요소가 바깥으로 튀어나가지 못하게 꽉 잡아줍니다.
2. 바깥쪽 요소의 마진과 안쪽 요소의 마진이 서로 섞이는(마진 병합) 현상을 막아줍니다.
레이아웃이 꼬일 때, 이 BFC를 의도적으로 만들어주면 마법처럼 해결되는 경우가 아주 많아요!
새로운 BFC를 만드는 것은 매우 유용합니다. 왜냐하면 새로운 BFC는 메인 레이아웃 안에 존재하는 또 다른 '미니 레이아웃'이 되어 마치 최상위 문서처럼 행동하기 때문입니다. BFC는 그 안에 있는 모든 것을 가둡니다(contain). float와 clear 속성은 오직 같은 서식 컨텍스트 안에 있는 항목들에게만 영향을 미치고, 마진 병합(margin collapse) 현상 역시 같은 서식 컨텍스트 안에 있는 요소들 사이에서만 일어납니다.
새로운 BFC를 생성했을 때 어떤 효과가 나타나는지 확인하기 위해 이 방법들 중 몇 가지를 살펴봅시다.
아래 예제에는 테두리가 쳐진 <div> 안에 float이 적용된 요소가 있습니다. 그리고 그 <div> 안의 텍스트 콘텐츠는 float 된 요소 옆으로 나란히 흘러가고 있습니다. 여기서 float 된 요소의 내용(높이)이 옆에 있는 텍스트 콘텐츠보다 더 길기 때문에, 부모 <div>의 테두리가 float 된 요소의 중간을 뚫고 지나가 버립니다! 흐름 안과 흐름 밖 요소 가이드(guide to in-flow and out of flow elements)에서 설명했듯이, float 된 요소는 일반 흐름에서 빠져나왔기(taken out of flow) 때문에 <div>의 배경과 테두리는 일반 흐름에 남아있는 텍스트 콘텐츠만 감쌀 뿐, float 된 요소는 품어주지 못하는 것입니다.
(MDN Playground에서 실행해보기 (Play))
<div class="box">
<div class="float">I am a floated box!</div>
<p>I am content inside the container.</p>
</div>
body {
font: 1.2em sans-serif;
}
.box {
background-color: rgb(224 206 247);
border: 5px solid rebeccapurple;
}
.float {
float: left;
width: 200px;
height: 100px;
background-color: white;
border: 1px solid black;
padding: 10px;
}
이때, 부모 <div>에 새로운 BFC(블록 서식 컨텍스트)를 생성해 주면 float 된 요소를 컨테이너 안에 온전히 가둘 수 있습니다. 과거에 이를 해결하기 위해 가장 흔하게 쓰였던 방식은 부모에게 overflow: auto를 주거나, 기본값인 overflow: visible이 아닌 다른 값을 설정하는 것이었습니다.
(MDN Playground에서 실행해보기 (Play))
<div class="box">
<div class="float">I am a floated box!</div>
<p>I am content inside the container.</p>
</div>
body {
font: 1.2em sans-serif;
}
.box {
background-color: rgb(224 206 247);
border: 5px solid rebeccapurple;
overflow: auto; /* 이 한 줄로 새로운 BFC가 생성됩니다! */
}
.float {
float: left;
width: 200px;
height: 150px;
background-color: white;
border: 1px solid black;
padding: 10px;
}
부모 <div>에 overflow: auto를 설정하여 새로운 BFC를 생성하자 float 요소가 부모 안에 쏙 들어갔습니다. 우리의 <div>는 이제 전체 레이아웃 내부의 훌륭한 독립적인 미니 레이아웃이 되었습니다. 자식 요소가 무엇이든 간에 무조건 이 컨테이너 안에 갇히게(contained) 됩니다.
⚠️ 강사의 중요 팁! "overflow를 쓰는 편법의 단점"
BFC를 만들기 위해overflow: hidden이나auto를 쓰는 것은 오랫동안 쓰여 온 '편법'입니다. 하지만 이렇게 하면 박스 밖으로 예쁘게 튀어나와야 할 그림자(box-shadow)가 싹둑 잘리거나, 내용이 없는데도 불필요한 스크롤바가 생기는 부작용이 발생할 수 있습니다!
새로운 BFC를 만들기 위해 overflow를 사용할 때의 문제점은, 사실 overflow 속성의 본래 목적은 브라우저에게 넘치는(overflowing) 콘텐츠를 어떻게 처리할지 지시하는 것이라는 점입니다. 오로지 BFC를 생성할 목적만으로 이 속성을 사용하다 보면 의도치 않은 스크롤바가 생기거나 그림자가 잘려버리는 등 원치 않는 부작용을 겪을 때가 있습니다. 또한, 코드를 나중에 보는 다른 개발자 입장에서는 "여기서 왜 뜬금없이 overflow를 썼지?" 하고 그 의도를 단번에 파악하기 어렵기 때문에 가독성이 떨어집니다. 만약 이 방식을 사용해야 한다면, 왜 이 속성을 썼는지 코드에 주석을 달아두는 것이 좋습니다.
이런 문제를 해결하기 위해, 컨테이닝 블록(부모 요소)에 display: flow-root (또는 display: flow-root list-item)를 사용하면 앞서 말한 부작용(스크롤바 생성 등) 없이 아주 깔끔하게 새로운 BFC를 생성할 수 있습니다.
(MDN Playground에서 실행해보기 (Play))
<div class="box">
<div class="float">I am a floated box!</div>
<p>I am content inside the container.</p>
</div>
body {
font: 1.2em sans-serif;
}
.box {
background-color: rgb(224 206 247);
border: 5px solid rebeccapurple;
display: flow-root; /* 부작용 없이 BFC를 만드는 가장 모던한 방법입니다! */
}
.float {
float: left;
width: 200px;
height: 100px;
background-color: white;
border: 1px solid black;
padding: 10px;
}
<div> 요소에 display: flow-root를 주면, 그 컨테이너 안에 있는 모든 것이 해당 컨테이너만의 블록 서식 컨텍스트에 참여하게 되며, float 된 요소들이 부모 요소 아래쪽으로 삐져나오는(poke out) 일이 사라집니다.
flow-root라는 키워드 이름은, 새로운 컨텍스트가 생성되고 그 안에서 흐름 레이아웃(flow layout)이 작동하는 방식을 볼 때, 본질적으로 <html> 태그가 하는 역할처럼 "새로운 루트(뿌리) 요소" 역할을 하는 무언가를 만들어낸다는 의미에서 붙여진 이름입니다.
인라인 서식 컨텍스트(Inline formatting contexts)는 다른 서식 컨텍스트 내부에 존재하며, 쉽게 말해 하나의 '단락(paragraph)' 환경이라고 생각하시면 됩니다. 단락 요소(예: <p>)는 그 안에 인라인 서식 컨텍스트를 생성하고, 이 컨텍스트 내부에서 텍스트에 적용되는 <strong>, <a>, 또는 <span>과 같은 요소들이 배치됩니다.
👨🏫 강사의 실무 팁! "Inline 요소의 한계"
초보 시절,<span>태그에 마진을 엄청 줬는데 위아래로는 전혀 공간이 벌어지지 않아 당황했던 경험 있으신가요? 바로 인라인 서식 컨텍스트 안에서는 박스 모델이 완벽하게 적용되지 않기 때문입니다!
인라인 서식 컨텍스트에 참여하는 항목들에는 박스 모델(box model)이 온전히 적용되지 않습니다.
가로 쓰기 모드(horizontal writing mode)에서 한 줄을 기준으로 볼 때, 인라인 요소에 수평 방향의 패딩(padding), 테두리(border), 마진(margin)을 주면 정상적으로 적용되어 좌우에 있는 텍스트들을 밀어냅니다.
하지만, 요소의 위(top)와 아래(bottom)에 적용된 마진은 무시됩니다. 수직 방향의 패딩과 테두리는 화면에 그려지긴 하지만, 인라인 서식 컨텍스트에서는 패딩과 테두리가 라인 박스(line boxes)들을 서로 밀어내지 못하기 때문에 결과적으로 위아래에 있는 다른 텍스트 콘텐츠와 겹쳐서(overlap) 보이게 됩니다.
(MDN Playground에서 실행해보기 (Play))
<p>
Before that night—<strong>a memorable night</strong>, as it was to
prove—hundreds of millions of people had watched the rising smoke-wreaths of
their fires without drawing any special inspiration from the fact.
</p>
body {
font: 1.2em sans-serif;
}
p {
margin-top: 2em;
}
strong {
margin: 20px; /* 좌우 마진은 먹히지만 상하 마진은 무시됩니다! */
padding: 20px; /* 상하 패딩은 칠해지긴 하지만 다른 줄의 글씨를 밀어내지 못하고 겹쳐버립니다! */
border: 5px solid rebeccapurple;
}
이 가이드는 기본 흐름 레이아웃(flow layout)에 대해서만 다루고 있으므로 다른 가능한 서식 컨텍스트들에 대해서는 언급하지 않았습니다. 하지만 명심해야 할 점은, 어떤 종류의 서식 컨텍스트를 만들든 그 컨텍스트 내부 요소들의 동작 방식은 변한다는 것입니다. 이러한 각각의 동작 방식들은 언제나 사양서에 자세히 설명되어 있으며, MDN 문서에서도 찾아보실 수 있습니다.
이번 가이드에서는 블록 및 인라인 서식 컨텍스트에 대해 더 자세히 살펴보고, 레이아웃 문제를 해결하는 데 아주 중요한 블록 서식 컨텍스트(BFC) 생성하기 주제를 집중적으로 다뤘습니다. 다음 가이드에서는 일반 흐름이 다양한 쓰기 모드(세로쓰기 등)와 어떻게 상호작용하는지에 대해 알아보겠습니다.
이 페이지가 도움이 되셨나요? [네 (Yes)] / [아니요 (No)]
기여하는 방법 알아보기 (Learn how to contribute)
이 페이지는 2025년 11월 7일에 MDN 기여자들 (MDN contributors)에 의해 마지막으로 수정되었습니다.