height: auto의 동작 원리는?

height auto가 자식 요소의 콘텐츠 크기를 기반으로 높이를 결정하는 방식과, 콘텐츠 기반 높이 계산이 레이아웃에 미치는 영향을 학습합니다

입문 15분 height auto 콘텐츠 기반 높이 계산

CSS에서 요소의 높이를 명시적으로 지정하지 않으면 브라우저는 기본값인 height: auto를 적용합니다. 이는 요소의 높이가 그 안에 담긴 콘텐츠의 크기에 따라 자동으로 결정된다는 것을 의미합니다. 언뜻 단순해 보이지만, 이 동작 원리를 정확히 이해하지 못하면 레이아웃이 예상과 다르게 렌더링되는 상황을 자주 마주하게 됩니다. width: auto가 부모 요소의 너비를 기준으로 계산되는 것과 달리, height: auto는 자식 요소의 콘텐츠를 기준으로 계산된다는 점에서 근본적인 비대칭이 존재합니다. 이 비대칭을 이해하는 것이 CSS 박스 모델에서 높이를 다루는 핵심 출발점입니다.

핵심 특징

  • 📐 height: auto는 자식 요소들의 총 콘텐츠 높이를 합산하여 부모의 높이를 결정하며, 이는 안에서 바깥으로 향하는 계산 방식입니다
  • width: auto는 부모 기준(바깥에서 안으로), height: auto는 자식 기준(안에서 바깥으로)으로 계산되어 서로 정반대 방향의 로직을 따릅니다
  • 🔄 자식 요소가 동적으로 추가되거나 제거되면 부모의 높이도 자동으로 재계산되어 반응형 레이아웃의 기본 토대가 됩니다
  • float된 자식이나 absolute로 배치된 자식은 부모의 높이 계산에서 제외되어 부모 요소의 높이가 0이 되는 현상이 발생할 수 있습니다
  • 퍼센트 높이를 사용하려면 부모에 명시적 높이가 필요한데, 이는 height: auto가 콘텐츠 기반이라 퍼센트의 기준값이 될 수 없기 때문입니다

실무에서의 영향

height: auto의 동작 원리를 이해하면 CSS 레이아웃에서 발생하는 많은 혼란을 해소할 수 있습니다. 실무에서 카드 컴포넌트, 모달, 아코디언 같은 UI를 구현할 때 높이가 콘텐츠에 맞게 자연스럽게 늘어나거나 줄어드는 동작은 모두 이 원리에 기반합니다. 특히 float 요소로 인해 부모의 높이가 사라지는 문제는 레거시 코드에서 빈번하게 발견되며, 이를 해결하기 위한 clearfix 기법이나 overflow 트릭의 원리도 height: auto의 계산 규칙에서 출발합니다. 또한 height: 100%가 왜 특정 상황에서 동작하지 않는지, Flexbox나 Grid 컨텍스트에서 높이 계산이 어떻게 달라지는지를 이해하는 데에도 이 개념이 필수적입니다. 결국 height: auto를 정확히 이해하는 것은 예측 가능한 레이아웃을 설계하고 디버깅 시간을 줄이는 데 직접적으로 기여합니다.


핵심 개념

콘텐츠 기반 높이 계산

입문

height: auto는 상자 안에 물건을 넣으면 상자가 알아서 크기를 맞추는 것과 같아요. 안에 뭐가 들어있는지에 따라 높이가 자동으로 정해져요.

📦 자동으로 늘어나는 상자 여러분이 종이 상자에 책을 넣는다고 상상해보세요. 책을 1권 넣으면 상자 높이가 낮고, 5권 넣으면 높아지죠? height: auto도 마찬가지예요. 안에 들어있는 내용물(콘텐츠)이 많으면 높이가 커지고, 적으면 낮아져요.

🎯 누가 높이를 정하나요? 부모가 정하는 게 아니라, 자식이 정해요! 부모 상자는 “나는 높이를 안 정할게, 네가 알아서 차지해”라고 말하는 거예요. 자식 요소들이 차지하는 공간만큼 부모의 높이가 결정돼요.

📏 빈 상자는 어떻게 되나요? 아무것도 넣지 않은 빈 상자는 높이가 0이 돼요. 콘텐츠가 없으면 늘어날 이유가 없으니까요. 마치 빈 봉투가 납작하게 눌려있는 것처럼, 내용물이 없으면 높이도 사라져요.

🔄 내용물이 바뀌면요? 책을 더 넣거나 빼면 상자 높이가 변하듯이, 웹페이지에서도 글이 추가되거나 사라지면 높이가 자동으로 다시 계산돼요. 이게 바로 웹페이지가 다양한 내용에 맞춰 유연하게 변하는 비결이에요.

💡 padding도 높이에 포함되나요? 상자 안에 뽁뽁이(완충재)를 넣으면 상자가 더 커지듯이, padding(안쪽 여백)도 높이에 포함돼요. 콘텐츠 높이 + 위 padding + 아래 padding이 최종 높이가 되는 거예요.

중급

height: auto는 CSS의 기본값으로, 요소의 높이를 콘텐츠 크기에 따라 자동으로 결정합니다. 명시적으로 높이를 지정하지 않은 블록 레벨 요소는 모두 이 방식으로 높이가 계산됩니다.

높이 계산 공식 블록 포매팅 컨텍스트(BFC)에서 height: auto인 요소의 높이는 다음과 같이 결정됩니다:

  • 인플로우(in-flow) 자식 요소들의 마진 에지(margin edge) 최상단부터 최하단까지의 거리
  • padding-top, 콘텐츠 영역, padding-bottom을 합산
  • 자식이 없거나 콘텐츠가 없으면 높이는 0
/* height를 지정하지 않으면 auto가 기본값 */
.parent {
  /* height: auto; ← 생략해도 동일 */
  padding: 20px;
  border: 1px solid #ccc;
}

.child-1 { height: 50px; }
.child-2 { height: 80px; }

/* .parent의 높이 = 50px + 80px + 40px(padding) = 170px */

margin 겹침(collapsing)과의 관계 자식 요소들의 수직 마진은 겹칠 수 있습니다. 인접한 자식 간 마진이 겹치면 더 큰 값 하나만 적용되므로, 단순 합산과 실제 높이가 다를 수 있습니다. 또한 부모에 padding이나 border가 없으면 자식의 margin-top/bottom이 부모 밖으로 빠져나가(마진 붕괴) 부모 높이에 반영되지 않습니다.

.parent {
  /* padding, border 없음 → 자식 마진이 부모 밖으로 빠져나감 */
  background: lightblue;
}

.child {
  margin: 30px 0;
  height: 40px;
}

/* 자식이 2개일 때:
   - 예상: 30 + 40 + 30 + 30 + 40 + 30 = 200px
   - 실제: 자식 마진이 부모 밖으로 → 부모 높이 = 40 + 30 + 40 = 110px
   (인접 마진 겹침 30px + 상하 마진 부모 밖으로 빠짐) */

심화

height: auto의 높이 결정은 CSS 2.1 명세 Section 10.6.3 “Block-level non-replaced elements in normal flow when ‘overflow’ computes to ‘visible‘“에 정의된 알고리즘을 따르며, 요소가 속한 포매팅 컨텍스트(Formatting Context)에 따라 계산 방식이 달라집니다.

CSS 2.1 Section 10.6.3 높이 계산 알고리즘 overflowvisible이고 heightauto인 블록 레벨 요소의 높이는 가장 위쪽 블록 레벨 자식 박스의 상단 마진 에지(top margin edge)부터 가장 아래쪽 블록 레벨 자식 박스의 하단 마진 에지(bottom margin edge)까지의 거리로 결정됩니다. 단, 이때 자식의 마진 겹침(Margin Collapsing, CSS 2.1 Section 8.3.1)이 적용된 후의 결과를 사용합니다.

핵심적으로, 이 알고리즘은 오직 인플로우(in-flow) 자식만을 대상으로 합니다. 아웃오브플로우(out-of-flow) 요소 — 즉 float되었거나 position: absolute/fixed인 요소 — 는 높이 계산에서 완전히 제외됩니다.

BFC 생성 시의 높이 계산 차이 CSS 2.1 Section 10.6.7에 따르면, 새로운 블록 포매팅 컨텍스트(BFC, Block Formatting Context)를 생성하는 요소는 높이 계산 방식이 달라집니다. BFC 루트 요소의 height: auto 계산은 float된 자식까지 포함하여 마진 에지 최하단까지 확장됩니다. 이것이 overflow: hidden이나 display: flow-root가 float를 포함하여 부모 높이를 복원하는 원리입니다.

BFC를 생성하는 조건에는 overflowvisible이 아닌 값, display: flow-root, floatnone이 아닌 값, position: absolute/fixed 등이 있으며, 각각은 동일한 높이 확장 규칙을 따릅니다.

브라우저 렌더링 엔진의 레이아웃 패스 Blink(Chrome) 렌더링 엔진에서 height: auto 계산은 레이아웃 트리(Layout Tree)의 2-패스 알고리즘으로 수행됩니다. 첫 번째 패스(Measure Pass)에서 자식 요소들의 고유 크기(intrinsic size)를 수집하고, 두 번째 패스(Layout Pass)에서 최종 위치와 크기를 확정합니다. height: auto인 요소는 Measure Pass에서 자식의 크기 합산을 기다려야 하므로, 레이아웃 트리를 리프(leaf) 노드부터 루트(root)까지 역방향으로 탐색(bottom-up traversal)합니다. 이는 width: auto가 루트부터 리프 방향(top-down)으로 전파되는 것과 정확히 반대 방향입니다.

width: auto와 height: auto의 비대칭

입문

너비(width)와 높이(height)에 auto를 쓰면 비슷하게 동작할 것 같지만, 실제로는 완전히 반대 방향으로 계산돼요!

🔄 방향이 반대라고요? width: auto는 부모가 자식에게 “너는 이만큼 쓸 수 있어”라고 알려주는 거예요. 바깥에서 안으로 크기가 전달돼요. 반면 height: auto는 자식이 부모에게 “나는 이만큼 필요해”라고 알려주는 거예요. 안에서 바깥으로 크기가 전달되는 거죠.

📐 왜 너비는 부모를 따라가나요? 웹페이지는 옛날부터 가로로 정해진 화면 크기 안에서 세로로 스크롤하도록 만들어졌어요. 그래서 가로 크기는 화면 너비라는 명확한 기준이 있어요. 마치 노트의 가로 폭이 정해져 있어서 그 안에서 글을 쓰는 것과 같아요.

📏 왜 높이는 자식을 따라가나요? 세로 방향은 스크롤이 가능하니까 끝이 정해져 있지 않아요. 그래서 “부모가 얼마만큼 높이를 줄까”가 아니라, “자식이 얼마만큼 필요한가”로 높이를 정하는 거예요. 일기장에 글을 쓸 때 내용이 많으면 페이지를 넘기는 것처럼요.

🚨 이걸 모르면 뭐가 문제인가요? height: 50%라고 쓰면 “부모 높이의 절반”을 기대하지만, 부모가 height: auto면 부모의 높이 자체가 자식에 의해 결정되니까 기준이 없어서 동작하지 않아요. 마치 “엄마 키의 절반만큼 자라세요”라고 했는데 엄마 키가 아직 정해지지 않은 것과 같아요.

중급

CSS에서 width: autoheight: auto는 같은 auto 키워드를 사용하지만, 계산 방향이 정반대입니다.

width: auto → 부모에서 자식으로 (외부→내부) 블록 레벨 요소의 width: auto는 부모의 콘텐츠 영역 너비에서 자신의 margin, border, padding을 뺀 나머지를 차지합니다. 즉 부모가 먼저 크기를 결정하고, 자식이 그에 맞춥니다.

height: auto → 자식에서 부모로 (내부→외부) height: auto는 자식 요소들의 높이 합산을 기반으로 부모의 높이가 결정됩니다. 자식이 먼저 크기를 결정하고, 부모가 그에 맞춥니다.

.parent {
  width: 500px; /* 명시적 너비 */
  /* height: auto; ← 기본값 */
}

.child {
  /* width: auto → 부모 500px에서 margin/border/padding 빼고 채움 */
  /* height: auto → 자신의 콘텐츠에 맞춰 높이 결정 */
  padding: 20px;
}

퍼센트 값의 동작 차이 이 비대칭은 퍼센트 값 사용 시 명확하게 드러납니다. width: 50%는 부모의 너비가 auto여도 동작합니다(뷰포트에서 역산 가능). 반면 height: 50%는 부모에 명시적 높이가 없으면 auto로 처리되어 퍼센트가 무시됩니다.

.parent {
  /* height 미지정 → auto */
}

.child {
  height: 50%; /* 무시됨! 부모 height가 auto이므로 기준값 없음 */
}

/* 해결: 부모에 명시적 높이 지정 */
.parent-fixed {
  height: 400px; /* 또는 100vh */
}

.child-works {
  height: 50%; /* 200px으로 계산됨 */
}

심화

width: autoheight: auto의 비대칭은 CSS의 레이아웃 모델이 가로 방향을 “제약 기반(constraint-based)“으로, 세로 방향을 “콘텐츠 기반(content-based)“으로 설계한 근본적인 아키텍처 결정에서 비롯됩니다.

CSS 2.1 Section 10.3 vs 10.6의 구조적 차이 너비 계산(Section 10.3.3)은 포함 블록(containing block)의 너비를 기준으로 하는 제약 방정식(constraint equation)입니다. margin-left + border-left + padding-left + width + padding-right + border-right + margin-right = containing block width라는 등식이 성립하며, auto 값은 이 등식을 만족시키도록 역산됩니다.

반면 높이 계산(Section 10.6.3)에는 이러한 등식이 존재하지 않습니다. height: auto는 단순히 자식 요소들의 마진 에지를 합산하는 축적(accumulation) 방식입니다. 이 비대칭은 웹의 역사적 맥락에서 비롯됩니다 — 초기 웹은 고정 뷰포트 너비에 무한 스크롤 높이를 전제로 설계되었기 때문입니다.

퍼센트 높이의 명세적 근거 CSS 2.1 Section 10.5에 따르면, 포함 블록의 높이가 auto일 때 자식의 퍼센트 높이는 auto로 처리됩니다. 이는 순환 의존(circular dependency) 방지를 위한 것입니다 — 부모 높이가 자식에 의존하고, 자식 높이가 다시 부모에 의존하면 무한 루프가 발생하기 때문입니다.

단, CSS Flexible Box Layout Module Level 1에서는 이 규칙이 완화됩니다. flex 컨테이너의 자식은 부모에 명시적 높이가 없어도 퍼센트 높이가 동작할 수 있습니다. 이는 flex 레이아웃이 2-패스 알고리즘(hypothetical main size → definite cross size)으로 순환 의존을 해소하기 때문입니다.

렌더링 엔진의 레이아웃 방향 Blink 엔진의 LayoutNG 아키텍처에서 이 비대칭은 “사용 가능 크기(available size)“와 “고유 크기(intrinsic size)“라는 두 가지 개념으로 구현됩니다. 인라인 축(일반적으로 가로)은 사용 가능 크기가 부모로부터 전달(top-down)되고, 블록 축(일반적으로 세로)은 고유 크기가 자식으로부터 수집(bottom-up)됩니다. 이 이중 방향 탐색이 레이아웃 계산의 복잡도를 O(n)에서 O(n²)로 증가시킬 수 있으며, 이를 방지하기 위해 캐싱(LayoutResult 캐시)과 조기 종료(early return) 최적화가 적용됩니다.

높이 계산에서 제외되는 요소

입문

어떤 특별한 방법으로 배치된 요소들은 부모의 높이 계산에 포함되지 않아요. 그래서 부모의 높이가 갑자기 0이 되는 신기한 현상이 일어나요!

👻 투명인간이 된 자식 교실에서 학생 수를 세는데, 몇몇 학생이 투명인간이 되어버렸다고 상상해보세요. 선생님 눈에는 학생이 없으니 “이 교실은 비었네”라고 판단하게 돼요. CSS에서도 비슷한 일이 일어나요. 특정 방법으로 배치된 자식 요소는 부모 눈에 보이지 않게 돼요.

🎈 둥둥 떠다니는 요소 (float) float은 요소를 물 위의 풍선처럼 둥둥 뜨게 만들어요. 풍선이 바닥에서 뜨면 바닥은 “위에 아무것도 없네”라고 생각하죠. 그래서 float된 자식만 있으면 부모의 높이가 0이 되어버려요!

📌 고정된 요소 (absolute/fixed) position: absolutefixed로 배치된 요소는 원래 자리에서 완전히 빠져나와요. 마치 게시판에 핀으로 고정한 메모지처럼, 원래 줄 서있던 자리에서 사라지는 거예요. 부모는 이 요소가 있는지조차 모르게 돼요.

🔧 어떻게 해결하나요? 부모에게 “떠다니는 자식도 포함해서 높이를 계산해!”라고 알려주면 돼요. 가장 간단한 방법은 부모에 overflow: hidden을 주거나, 최신 방법인 display: flow-root를 사용하는 거예요. 그러면 부모가 float된 자식까지 포함해서 높이를 다시 계산해요.

중급

height: auto 계산에서 인플로우(in-flow) 요소만 높이에 기여합니다. 아웃오브플로우(out-of-flow) 요소는 높이 계산에서 완전히 제외됩니다.

높이 계산에서 제외되는 요소

  • float: left/right — 플로팅된 요소
  • position: absolute — 절대 위치 요소
  • position: fixed — 고정 위치 요소

이 요소들은 일반 흐름(normal flow)에서 벗어나므로 부모의 높이에 영향을 주지 않습니다. 만약 부모의 모든 자식이 아웃오브플로우라면, 부모 높이는 0이 됩니다.

<div class="parent">
  <div class="child" style="float: left; height: 200px;">
    Float 자식
  </div>
</div>
.parent {
  border: 2px solid red;
  /* 높이가 0! float 자식은 높이 계산에서 제외됨 */
}

해결 방법: BFC 생성 새로운 블록 포매팅 컨텍스트(BFC)를 생성하면 float 자식이 높이 계산에 포함됩니다.

/* 방법 1: overflow 사용 (전통적) */
.parent-fix1 {
  overflow: hidden; /* 또는 auto */
}

/* 방법 2: display: flow-root (권장, 모던) */
.parent-fix2 {
  display: flow-root;
}

/* 방법 3: clearfix 해킹 (레거시) */
.parent-fix3::after {
  content: "";
  display: block;
  clear: both;
}

주의: absolute/fixed는 BFC로도 해결 불가 overflow: hidden이나 display: flow-root는 float 자식만 높이에 포함시킵니다. position: absolute/fixed인 요소는 어떤 방법으로도 부모의 height: auto 계산에 포함시킬 수 없습니다. 이 요소들의 크기를 반영하려면 JavaScript로 동적 높이를 설정하거나, 레이아웃 구조를 재설계해야 합니다.

심화

아웃오브플로우 요소의 높이 제외는 CSS 2.1 명세에서 일반 흐름(normal flow)과 그 이탈을 구분하는 핵심 메커니즘이며, 포매팅 컨텍스트의 유형에 따라 제외 범위가 달라집니다.

CSS 2.1 Section 9.5 Float과 높이 계산의 관계 float된 요소는 CSS 2.1 Section 9.5에 따라 일반 흐름에서 제거(taken out of flow)됩니다. Section 10.6.3에서 overflow: visible인 요소의 높이 계산은 인플로우 자식만 포함하므로, float 자식은 자동으로 제외됩니다. 그러나 Section 10.6.7에서 BFC 루트의 높이 계산은 “하단 마진 에지가 요소의 하단 콘텐츠 에지 아래에 있는 float가 있다면, 높이는 해당 float의 하단 마진 에지까지 확장된다”고 명시합니다.

이 차이가 overflow: hidden이나 display: flow-root로 float 포함 문제를 해결하는 명세적 근거입니다. BFC를 생성하면 해당 요소는 Section 10.6.7의 규칙을 따르게 되어 float 자식의 마진 에지까지 높이가 확장됩니다.

display: flow-root의 명세적 의미 CSS Display Module Level 3에서 도입된 display: flow-root는 명시적으로 BFC를 생성하기 위한 값입니다. overflow: hidden과 달리 콘텐츠 클리핑이라는 부작용(side effect) 없이 순수하게 BFC만 생성합니다. 내부적으로는 overflow: hidden과 동일한 레이아웃 경로를 따르지만, 페인팅(painting) 단계에서 클리핑을 적용하지 않는다는 차이가 있습니다.

Clearfix의 동작 원리 전통적인 clearfix(::after { clear: both })는 BFC와 다른 메커니즘으로 동작합니다. clear 속성은 CSS 2.1 Section 9.5.2에 따라 해당 요소의 상단 마진 에지를 선행 float의 하단 마진 에지 아래로 이동시킵니다. ::after 의사 요소는 인플로우이므로 부모의 높이 계산에 포함되고, clear에 의해 float 아래로 밀려난 위치가 부모의 높이를 확장시키는 것입니다. 즉, clearfix는 “float를 높이에 포함시키는 것”이 아니라 “인플로우 요소를 float 아래로 밀어서 간접적으로 높이를 확보하는 것”입니다.