안녕하세요! 프론트엔드 개발자로서 튼탄한 기본기를 다지기 위해 꼭 정복해야 하는 CSS의 핵심 개념, 바로 컨테이닝 블록(Containing block, 포함 블록)에 대한 공식 문서입니다.
기술 면접 단골 질문 중 하나가 바로 position: absolute 요소가 정확히 어떤 부모를 찾아가서 위치를 잡는지, 그리고 margin이나 padding에 퍼센트(%)를 주면 도대체 무엇을 기준으로 크기가 계산되는지에 대한 것입니다. 이 문서를 꼼꼼히 읽어두시면 이런 레이아웃 관련 질문들에 아주 자신 있게 답변하실 수 있을 거예요. 자, 그럼 MDN 문서를 바탕으로 친절하게 풀어서 설명해 드릴게요!
요소의 크기와 위치는 종종 그 요소의 컨테이닝 블록(containing block)에 의해 큰 영향을 받습니다. 대부분의 경우, 컨테이닝 블록은 요소와 가장 가까운 블록 레벨(block-level) 조상의 콘텐츠 영역(content area)이지만, 항상 그런 것은 아닙니다. 이 글에서는 어떤 요소의 컨테이닝 블록을 결정하는 요인들이 무엇인지 자세히 살펴보겠습니다.
사용자 에이전트(브라우저 등)가 문서를 레이아웃할 때, 모든 요소에 대해 박스(box)를 생성합니다. 각 박스는 다음 네 가지 영역으로 나뉩니다:
많은 개발자들이 '어떤 요소의 컨테이닝 블록은 무조건 부모 요소의 콘텐츠 영역이다'라고 믿지만, 이는 반드시 참은 아닙니다. 도대체 무엇이 요소의 컨테이닝 블록을 결정하는지 그 요인들을 깊이 파헤쳐 봅시다.
요소의 컨테이닝 블록을 결정하는 것이 무엇인지 배우기 전에, 도대체 이게 왜 중요한지부터 아는 것이 좋겠죠.
요소의 크기와 위치는 종종 그 요소의 컨테이닝 블록의 영향을 받습니다. 절대 위치가 지정된 요소(즉, position 속성이 absolute 또는 fixed로 설정된 요소)의 width, height, padding, margin, 그리고 오프셋 속성(top, bottom 등)에 적용되는 퍼센트(%) 값들은 모두 해당 요소의 컨테이닝 블록으로부터 계산됩니다.
컨테이닝 블록을 식별하는 과정은 전적으로 해당 요소의 position 속성값에 달려 있습니다:
position 속성이 static, relative, 또는 sticky인 경우, 컨테이닝 블록은 블록 컨테이너(예: inline-block, block, list-item 요소)이거나 서식 문맥(formatting context)을 형성하는(예: table container, flex container, grid container 또는 블록 컨테이너 그 자체) 가장 가까운 조상 요소의 콘텐츠 박스(content box) 가장자리에 의해 형성됩니다.position 속성이 absolute인 경우, 컨테이닝 블록은 position 값이 static이 아닌(fixed, absolute, relative, 또는 sticky) 가장 가까운 조상 요소의 패딩 박스(padding box) 가장자리에 의해 형성됩니다.position 속성이 fixed인 경우, 컨테이닝 블록은 연속 매체(웹 페이지)의 경우 뷰포트(viewport)에 의해, 페이지 매체(인쇄)의 경우 페이지 영역에 의해 설정됩니다.position 속성이 absolute 또는 fixed인 경우, 다음과 같은 특징 중 하나라도 가지고 있는 가장 가까운 조상 요소의 패딩 박스(padding box) 가장자리에 의해 컨테이닝 블록이 형성될 수 있습니다:filter, backdrop-filter, transform, perspective, rotate, scale, 또는 translate 속성값이 none이 아닌 경우.contain 속성값이 layout, paint, strict 또는 content인 경우 (예: contain: paint;).container-type 속성값이 normal이 아닌 경우.will-change 속성값에, 초기값이 아닐 경우 컨테이닝 블록을 형성하게 되는 속성(예: filter나 transform)이 포함되어 있는 경우.content-visibility 속성값이 auto인 경우.💡 강사의 기술 면접 꿀팁!
면접관이 "position: fixed요소는 언제나 브라우저 창(Viewport)을 기준으로 배치되나요?" 라고 묻는다면, 이것은 전형적인 함정 질문입니다! 위 규칙의 4번을 보세요. 조상 요소 중에transform이나filter등이 적용된 요소가 있다면, 뷰포트가 아니라 바로 그 조상 요소가fixed요소의 기준점(컨테이닝 블록)이 되어버립니다. 이 예외 상황을 정확히 짚어주시면 면접관이 아주 흡족해할 거예요!
💡 노트 (Note):
최상위 요소(<html>)가 위치하는 컨테이닝 블록은 초기 컨테이닝 블록(initial containing block)이라는 직사각형입니다. 이것은 뷰포트(연속 매체의 경우) 또는 페이지 영역(페이지 매체의 경우)의 크기를 갖습니다.
💡 노트 (Note):
perspective와filter속성이 컨테이닝 블록을 형성하는 것에 관해서는 브라우저마다 약간의 불일치가 존재합니다.
위에서 언급했듯이, 특정 속성들에 퍼센트(%) 값이 주어질 때, 그 계산된 값은 요소의 컨테이닝 블록에 따라 달라집니다. 이렇게 작동하는 속성들은 박스 모델 속성(box model properties)과 오프셋 속성(offset properties)입니다:
height, top, bottom 속성은 컨테이닝 블록의 height(높이)를 기준으로 퍼센트 값을 계산합니다.width, left, right, padding, margin 속성은 컨테이닝 블록의 width(너비)를 기준으로 퍼센트 값을 계산합니다.💡 강사의 핵심 포인트!
여기서 정말 중요한 CSS의 특징이 등장합니다. 2번을 자세히 보세요. 수직 방향인padding-top이나margin-bottom에 퍼센트 값을 주어도, 기준은 부모의 '높이'가 아니라 '너비(width)'라는 사실입니다! 웹에서 종횡비(Aspect Ratio)가 유지되는 반응형 유튜브 썸네일이나 카드 UI를 만들 때 자주 쓰이는padding-top: 56.25%같은 트릭의 원리가 바로 이것이랍니다.
💡 노트 (Note):
블록 컨테이너(block container)(예: inline-block, block, list-item 요소)는 오직 인라인 서식 문맥(inline formatting context)에 참여하는 인라인 레벨 박스들만 포함하거나, 오직 블록 서식 문맥(block formatting context)에 참여하는 블록 레벨 박스들만 포함합니다. 요소는 블록 레벨 또는 인라인 레벨 박스를 포함할 때만 블록 컨테이너가 됩니다.
모든 예제에 사용될 공통 HTML 코드는 다음과 같습니다:
<body>
<section>
<p>This is a paragraph!</p>
</section>
</body>
아래 각 사례에서는 오직 CSS만 변경됩니다.
이 예제에서 문단(<p>)은 정적으로(statically) 위치가 지정되어(position: static을 의미) 있으므로, 그 컨테이닝 블록은 <section>이 됩니다. <section>이 (display: block을 가진) 가장 가까운 블록 컨테이너 조상이기 때문입니다.
body {
background: beige;
}
section {
display: block;
width: 400px;
height: 160px;
background: lightgray;
}
p {
width: 50%; /* == 400px * .5 = 200px */
height: 25%; /* == 160px * .25 = 40px */
margin: 5%; /* == 400px * .05 = 20px */
padding: 5%; /* == 400px * .05 = 20px */
background: cyan;
}
이 예제에서 문단의 컨테이닝 블록은 <body> 요소입니다. 왜냐하면 <section>은 (display: inline을 가지므로) 블록 컨테이너가 아니며, 어떠한 서식 문맥도 형성하지 않기 때문입니다.
body {
background: beige;
}
section {
display: inline;
background: lightgray;
}
p {
width: 50%; /* == body 너비의 절반 */
height: 200px; /* 주의: 퍼센트 값을 사용하면 0이 됩니다 */
background: cyan;
}
이 예제에서 문단의 컨테이닝 블록은 <section>입니다. 왜냐하면 <section>의 position이 absolute이기 때문입니다. 문단의 퍼센트 값은 컨테이닝 블록의 padding 영역까지 포함하여 영향을 받습니다. (단, 컨테이닝 블록의 box-sizing 값이 border-box라면 다르게 계산될 것입니다.)
body {
background: beige;
}
section {
position: absolute;
left: 30px;
top: 30px;
width: 400px;
height: 160px;
padding: 30px 20px;
background: lightgray;
}
p {
position: absolute;
width: 50%; /* == (400px + 20px + 20px) * .5 = 220px */
height: 25%; /* == (160px + 30px + 30px) * .25 = 55px */
margin: 5%; /* == (400px + 20px + 20px) * .05 = 22px */
padding: 5%; /* == (400px + 20px + 20px) * .05 = 22px */
background: cyan;
}
이 예제에서 문단의 position은 fixed이므로, 그 컨테이닝 블록은 '초기 컨테이닝 블록'(화면의 경우 뷰포트)이 됩니다. 따라서 브라우저 창의 크기에 따라 문단의 치수가 변경됩니다.
body {
background: beige;
}
section {
width: 400px;
height: 480px;
margin: 30px;
padding: 15px;
background: lightgray;
}
p {
position: fixed;
width: 50%; /* == (50vw - (수직 스크롤바의 너비)) */
height: 50%; /* == (50vh - (수평 스크롤바의 높이)) */
margin: 5%; /* == (5vw - (수직 스크롤바의 너비)) */
padding: 5%; /* == (5vw - (수직 스크롤바의 너비)) */
background: cyan;
}
이 예제에서 문단의 position은 absolute이므로, 컨테이닝 블록은 <section>이 됩니다. <section>이 none이 아닌 transform 속성을 가진 가장 가까운 조상이기 때문입니다.
body {
background: beige;
}
section {
transform: rotate(0deg);
width: 400px;
height: 160px;
background: lightgray;
}
p {
position: absolute;
left: 80px;
top: 30px;
width: 50%; /* == 200px */
height: 25%; /* == 40px */
margin: 5%; /* == 20px */
padding: 5%; /* == 20px */
background: cyan;
}
all 속성contain 속성aspect-ratio 속성box-sizing 속성min-content 및 max-content 크기 값이 페이지가 도움이 되셨나요?
기여하는 방법 알아보기 (Learn how to contribute)
이 페이지는 MDN 기여자들에 의해 2026년 1월 6일에 마지막으로 수정되었습니다 (MDN contributors).