본문 바로가기
CSS

마진(Margin)이 왜 이럴까

by Vintz 2025. 9. 25.

마진은 가끔 이상하다. 내 예상과 다른 결과물을 보여줄 때가 있다. 마진은 요소의 바깥쪽에 여백을 주려할 때 사용하는데, 어쩔 때는 적용이 안 된 것처럼 보이거나 자식에서 적용한 마진이 부모 바깥의 여백으로 '나와 보이는' 화면이 보이기도 한다.

 

이런 이유 때문에 종종 혼란스러울 때가 있다. 그래서 이번에는 마진에 대해 이해해 보는 시간을 가지게 되었다. 마진은 단순히 '바깥 여백'이라는 정의만으로 사용하기에는 부족한 점이 있다. 조금 더 주의해야 할 상황들을 예시를 통해 하나씩 살펴보자.

서로 붙어있는 형제 요소

가장 일반적으로 발생할 수 있는 상황이다. 형제 요소가 붙어 있을 때, 마진을 적용하면 어느 한쪽이 상쇄(Collapsing)된다.

인접한 형제 요소에 수직으로 마진을 적용한 모습

제목과 내용 사이에는 여백이 28px + 15px을 더한 43px이 아닌, 28px이 적용되었다. 둘 중에 더 큰 크기의 여백으로 합쳐진(축소된) 것인데, 이것을 "마진 상쇄(또는 마진 겹침, Margin Collapsing)"라고 부른다.

+------------------------------------------------------------+
| Container                                                  |
|                                                            |
|   +----------------------------------------------------+   |
|   |                   a  H1 margin(28px)               |   |
|   +----------------------------------------------------+   |
|   |                                                    |   |
|   |                        제목                         |   |
|   |                                                    |   |
|   +----------------------------------------------------+   |
|                                                            |
|                         max(a, b)                          |
|                                                            |
|   +----------------------------------------------------+   |
|   |                                                    |   |
|   |                        내용                         |   |
|   |                                                    |   |
|   +----------------------------------------------------+   |
|   |                 b  P margin(15px)                  |   |
|   +----------------------------------------------------+   |
|                                                            |
+------------------------------------------------------------+

두 개의 마진이 서로 영향을 받아, 한 쪽의 효과가 없어지는 것이다. 여기서 주의할 점은 다음과 같다.

  • 마진의 크기가 28px, 28px로 서로 같다면, 단일 여백인 28px이 적용된다.
  • 이러한 동작은 상단 및 하단에만 적용된다. 즉, 수직 방향에서만 의미가 있다.

구분이 안 되는 부모와 자손 요소

부모 블록과 자손 요소 사이를 나누는 콘텐츠가 없을 때도 바깥 여백이 합쳐진다.

  • margin-top의 경우
    • border-top, padding-top 속성이 없음
    • 자손에 clear 속성을 적용하지 않음
  • margin-bottom의 경우
    • border-bottom, padding-bottom 속성이 없음
    • height, min-height 지정을 하지 않음
  • 공통
    • 텍스트 또는 인라인 요소들(e.g. span, a, img)이 없음

부모 요소의 margin-top은 부모 요소에 위의 속성 중 해당되거나 인라인 콘텐츠(Inline Content)가 하나도 없다면, 흐름 안(In-flow)에 있는 첫 번째 자손의 margin-top과 겹치게 된다. 즉, 마진 상쇄가 발생한다.

<style>
  .parent { margin-block: 8px; }
  .child { margin-block: 16px; }
  .child:first-child { margin-top: 32px; }
  .out-of-flow {
    position: absolute;
    top: -32px;
    color: red;
  }
</style>

<div class="parent">
  <div class="child out-of-flow">흐름 밖의 자식</div>
  <!-- 흐름 안에 있는 첫 번째 자식의 margin-top과 겹친다. -->
  <div class="child in-flow-first">흐름 안의 자식</div>
  <div class="child in-flow-last">흐름 안의 자식</div>
</div>

이때, 흐름 내에 있다는 것은 자신만의 공간을 차지하고, 블록 요소의 다음 요소는 그 아래 줄에 나타나는 것처럼 다른 요소의 배치에 영향을 주는 일반적인 상황을 뜻한다. 하지만 특정 CSS 속성들은 흐름에서 벗어나게(Out-of-flow) 할 수 있다.

  • position: absolute/fixed
  • float: left/right

해당 속성을 사용하면 요소를 정상적인 흐름에서 제외할 수 있다. 이를 "out-of-flow" 상태라고 부른다. 따라서, 위 예시는 부모 블록과 자식 간의 구분이 가능한 콘텐츠가 없으며 HTML 구조상 첫 번째 자식은 position: absolute가 적용되어, 계산 대상에서 제외된다. 그리고 그다음으로 나오는 "in-flow" 상태의 자식이 마진 상쇄 계산을 위한 첫 번째 자식이 되어, 마진은 16px로 겹쳐진다.

 

out-of-flow 상태더라도, 마진이 겹치지 않을 수 있다. 다음은 clear 속성을 적용했을 때의 예시이다:

<style>
  .parent { margin-block: 8px; }
  .child { margin-block: 16px; }
  .child:first-child { margin-top: 32px; }
  .out-of-flow {
    float: left;
    color: red;
  }
  .in-flow-first { clear: both; }
</style>

<div class="parent">
  <div class="child out-of-flow">흐름 밖의 자식</div>
  <div class="child in-flow-first">흐름 안의 자식</div>
  <div class="child in-flow-last">흐름 안의 자식</div>
</div>

흐름 안의 첫 번째 자식은, float 속성이 적용된 흐름 밖의 자식에게 영향을 받지 않고 그 아래로 배치되도록 clear: both를 주었다. 이런 경우에는 부모 요소의 margin-top에 마진 상쇄가 발생하지 않는다.

부모의 margin-top은 border-top, padding-top, 그리고 'clearance가 적용'되면 겹쳐지지 않는다.

부모의 margin-bottom도 비슷한 맥락이지만, margin-top과 다르게 height, min-height 지정과 같이 서로 다른 방지 조건을 가지고 있다. 부모의 height 또는 min-height가 마진 상쇄를 방지하려면, 부모 요소 높이가 자식 콘텐츠에 의해 결정되지 않도록 해야한다.

 

또한, 구분이 안 되는 부모와 '자손' 요소인 점을 기억하자. 위의 조건들이 부합하면 모든 하위 요소들과 마진 상쇄가 발생한다.

빈 블록

빈 블록도 마진 상쇄가 발생한다. border, padding, 인라인 콘텐츠, height 또는 min-height가 1px도 없다면 상쇄가 일어난다.

<style>
  .above,
  .below {
    background: #eee;
  }
  .empty {
    margin-top: 30px;
    margin-bottom: 50px;
  }
</style>

<div class="above">위</div>
<div class="empty"></div>
<div class="below">아래</div>

빈 블록은 30px + 50px이 더해져, 80px의 여백이 아닌 50px의 여백이 생긴다.

50px 만큼 수직 여백이 생긴 모습


블록 서식 컨텍스트(Block Formatting Context, BFC)

border, padding, 인라인 콘텐츠 등이 부모와 자손 사이의 구분을 돕는 '경계'의 역할을 했다면, BFC는 독립된 레이아웃 공간을 만드는 것과 같다. 따라서 BFC를 생성하면 마진 상쇄를 방지할 수 있다.

display: flow-root;

오직 BFC를 만들기 위한 속성으로, 가장 간편한 방법이다. 위 예시에서 해당 속성을 빈 블록에 적용하면, 아래와 같이 마진 상쇄가 발생하지 않는다.

빈 블록에 display: flow-root;를 적용한 모습

빈 블록에 독립된 레이아웃 공간이 만들어져, 마진이 축소되지 않고 공간 내에 여백이 생긴다. 이처럼 간단하게 BFC를 생성할 수 있으며 더 다양한 방법으로도 만들 수가 있다.

 

가장 흔하게 볼 수 있는 사례는 flex와 grid를 적용했을 때다. 이때도 BFC가 생성되어 마진 상쇄가 일어나지 않는다.

<style>
  main { display: grid; }
</style>

<header>헤더</header>
<main>
  <h1>제목</h1>
  <p>메인 내용</p>
</main>
<footer>푸터</footer>

서로 붙어있는 형제 요소인 제목과 메인 내용이 grid에 의해 BFC가 만들어져, 수직 여백이 축소되지 않은 걸 볼 수 있다.

main 요소에 grid를 적용한 후 마진 상쇄가 일어나지 않은 모습. (해당 마진은 h1과 p 태그의 브라우저 기본 스타일이다.)

마진 상쇄(겹침/축소)는 왜 일어나는 걸까?

브라우저는 왜 바깥 수직 여백을 합산하는 대신 하나의 여백으로 축소하고, 가장 큰 여백을 우선시하는 걸까? 마진 상쇄라는 용어는 정의되지 않았지만 CSS1 초기 문서(1996년)부터 존재했던 개념으로, 해당 문서에서 약간의 힌트를 얻을 수 있었다.

In most cases, after collapsing the vertical margins the result is visually more pleasing and closer to what the designer expects. - W3C CSS, level 1(1996)

수직 마진이 상쇄된 결과는 시각적으로 더 보기 좋고 디자이너가 의도한 결과에 더 가깝다는 것이다. 디자이너는 '제목과 본문 사이에 24px 간격이 필요해'라고 생각하지, '제목 아래는 14px, 본문 위는 10px을 더해서 총 24px을 만들어야지'라고 생각하지 않는다. 특히 목록(<ul>, <ol>)이나 연속된 문단에서는 더욱 중요한 역할을 한다.

 

'문서'라는 관점에서 생각해 보면 더 이해하기 쉬울 것 같다. Microsoft Word도 제목과 본문 사이에 각각 간격을 지정하면, 둘 중 가장 큰 간격이 적용된다. 이처럼 직관적이고 일관된 수직 리듬을 위해 이러한 개념이 생겨났다고 볼 수 있다.

참고

Mastering margin collapsing - MDN
Block formatting context - MDN
Introduction to formatting contexts - MDN
Cascading Style Sheets, Level 1, Vertical formatting - W3C
Cascading Style Sheets, Level 2, collapsed-margin - W3C
Cascading Style Sheets, Level 2, clearance - W3C
What is the point of CSS collapsing margins?
반응형