현록

CSS box-sizing content-box와 border-box 차이

CSS에서 width를 지정했는데 요소가 예상보다 넓어질 때가 있다.
대부분 paddingborder가 최종 너비에 더해졌기 때문이다.

box-sizingwidthheight가 박스의 어느 영역까지 포함하는지 정한다.
기본값인 content-box는 content 크기만 지정한다.
border-box는 content, padding, border를 합친 크기를 지정한다.

레이아웃 너비를 예측하기 쉽게 만들고 싶다면 두 계산 방식의 차이부터 이해해야 한다.

CSS 박스 모델의 구성

HTML 요소는 안쪽부터 content, padding, border, margin 영역으로 구성된다.

.box {
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  margin: 24px;
}

content는 글자나 이미지가 놓이는 영역이다.
padding은 content와 border 사이의 안쪽 여백이다.
border는 요소의 테두리다.
margin은 border 바깥에서 다른 요소와 거리를 만든다.

box-sizing은 이 가운데 content, padding, border와 width의 관계를 바꾼다.
margin은 어떤 값을 사용해도 지정한 width에 포함되지 않는다.

안쪽 여백과 바깥 간격의 차이는 CSS margin과 padding 차이에서 더 자세히 다뤘다.

content-box의 계산 방식

content-box는 CSS 표준에서 정한 box-sizing의 초기값이다.
지정한 widthheight가 content 영역에만 적용된다.

.box {
  box-sizing: content-box;
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
}

이 요소의 content 너비는 300px이다.
좌우 padding은 각각 20px이므로 합계가 40px이다.
좌우 border는 각각 2px이므로 합계가 4px이다.

브라우저에 그려지는 border 바깥쪽 너비는 다음과 같다.

300px + 20px + 20px + 2px + 2px = 344px

CSS에 width: 300px을 적었지만 눈에 보이는 박스는 344px을 차지한다.
content 영역을 정확히 300px로 유지해야 한다면 이 동작이 필요할 수 있다.

border-box의 계산 방식

border-box는 지정한 width 안에 content, padding, border를 모두 포함한다.

.box {
  box-sizing: border-box;
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
}

브라우저에 그려지는 border 바깥쪽 너비는 그대로 300px이다.
padding과 border가 그 안에 들어가므로 content 영역이 줄어든다.

content 너비는 다음과 같다.

300px - 20px - 20px - 2px - 2px = 256px

박스의 최종 너비를 300px로 유지하는 대신 실제 content가 사용할 수 있는 너비는 256px이 된다.

레이아웃에서는 최종 크기를 바로 읽을 수 있어 border-box가 편한 경우가 많다.

두 값의 핵심 차이

두 값은 width가 의미하는 범위에서 차이가 난다.

  • content-box에서 width는 content 영역의 너비다.
  • border-box에서 width는 content, padding, border를 합친 너비다.
  • content-box는 padding과 border가 최종 박스 크기에 더해진다.
  • border-box는 padding과 border가 지정한 크기 안으로 들어간다.
  • margin은 두 방식 모두 width 바깥에서 계산된다.
  • box-sizing의 초기값은 content-box다.

content 자체의 정확한 크기가 중요하면 content-box를 고려할 수 있다.
화면에 배치되는 최종 박스 크기가 중요하면 border-box가 읽기 쉽다.

width 100%와 overflow

초보자가 자주 만나는 문제는 width: 100%인 요소가 부모를 넘는 상황이다.

.parent {
  width: 320px;
}

.child {
  width: 100%;
  padding: 20px;
  border: 1px solid;
}

기본 content-box에서는 child의 content 너비가 부모의 320px을 사용한다.
여기에 좌우 padding 40px과 border 2px이 더해진다.
최종 너비는 362px이 되어 부모보다 커진다.

border-box를 적용하면 padding과 border가 100% 안에 포함된다.

.child {
  box-sizing: border-box;
  width: 100%;
  padding: 20px;
  border: 1px solid;
}

이제 최종 너비가 부모의 content 너비 안에 맞는다.

다만 overflow의 원인이 항상 box-sizing인 것은 아니다.
긴 단어, 고정 너비 자식, transform, 음수 margin, grid track의 최소 크기도 함께 확인해야 한다.

전역 border-box 설정

많은 프로젝트는 모든 요소에 border-box를 기본으로 적용한다.

*,
*::before,
*::after {
  box-sizing: border-box;
}

모든 일반 요소와 pseudo-element가 같은 계산 방식을 사용한다.
컴포넌트마다 box-sizing을 반복해서 적지 않아도 된다.

상속을 활용하는 방식도 있다.

html {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

특정 subtree에서 계산 방식을 바꾸고 싶을 때 상속 구조가 편할 수 있다.

프로젝트의 reset이나 normalize 파일에 이미 설정되어 있을 가능성이 높다.
새 규칙을 추가하기 전에 현재 전역 스타일을 먼저 확인하는 편이 좋다.

height 계산의 차이

box-sizing은 너비뿐 아니라 높이에도 같은 방식으로 적용된다.

.box {
  box-sizing: content-box;
  height: 100px;
  padding-block: 16px;
  border-block: 2px solid;
}

content-box의 최종 높이는 100px + 32px + 4px136px이다.

.box {
  box-sizing: border-box;
  height: 100px;
  padding-block: 16px;
  border-block: 2px solid;
}

border-box의 최종 높이는 100px이다.
content가 사용할 수 있는 높이는 64px로 줄어든다.

고정 높이에 padding을 많이 넣으면 content 영역이 너무 작아질 수 있다.
텍스트가 여러 줄이 되는 UI에서는 height보다 min-height가 안전한 경우가 많다.

padding이 content를 줄이는 동작

border-box는 padding을 무시하지 않는다.
padding을 지정한 최종 크기 안에 넣기 위해 content 영역을 줄인다.

.button {
  box-sizing: border-box;
  width: 120px;
  padding-inline: 24px;
}

좌우 padding이 합계 48px이므로 텍스트가 사용할 수 있는 공간은 그만큼 작아진다.
버튼 label이 길면 줄바꿈되거나 넘칠 수 있다.

border-box를 사용하면 모든 크기 문제가 자동으로 해결된다고 생각하면 안 된다.
지정한 너비 안에서 content와 padding이 나눠 쓴다는 점을 함께 봐야 한다.

margin이 포함되지 않는 이유

border-box라는 이름처럼 계산 범위는 border edge까지다.
margin은 요소 바깥의 다른 박스와 간격을 만들기 때문에 width에 포함되지 않는다.

.box {
  box-sizing: border-box;
  width: 300px;
  margin-inline: 20px;
}

박스 자체의 너비는 300px이다.
레이아웃에서 차지하는 가로 범위는 좌우 margin까지 고려하면 340px이 될 수 있다.

부모 안에서 여러 열을 계산할 때 margin 때문에 넘친다면 gap을 사용하는 구조도 검토할 수 있다.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

항목 사이 간격을 부모가 관리하면 각 item의 바깥 너비 계산이 단순해진다.

input과 button의 기본값

모든 요소가 브라우저 기본 스타일에서 같은 box-sizing을 쓰는 것은 아니다.
MDN에 따르면 일부 form control과 table 요소는 기본적으로 border-box를 사용한다.

그래서 같은 width를 준 divbutton의 크기가 다르게 보일 수 있다.

.box,
.button {
  width: 200px;
  padding: 16px;
  border: 2px solid;
}

전역 border-box 설정은 이런 기본값 차이를 줄이고 크기 계산을 통일하는 데도 도움이 된다.

HTML에서 링크와 버튼을 선택하는 의미 기준은 HTML button과 a 태그 차이에서 확인할 수 있다.

min-width와 max-width

box-sizingmin-widthmax-width가 해석되는 범위에도 영향을 준다.

.panel {
  box-sizing: border-box;
  width: 100%;
  max-width: 720px;
  padding-inline: 24px;
}

border-box에서는 panel의 content, padding, border를 합친 너비가 720px을 넘지 않는다.

반대로 content-box에서는 max-width가 content 영역에 적용되고 padding과 border가 바깥에 더해진다.
페이지 container처럼 최종 외곽 너비를 제한하려면 border-box가 이해하기 쉽다.

DevTools에서 확인하는 방법

브라우저 DevTools의 Computed panel에는 box model 그림이 표시된다.
content, padding, border, margin의 실제 계산 값을 한눈에 볼 수 있다.

요소가 예상보다 크다면 다음 순서로 확인한다.

  1. Computed style에서 box-sizing 값을 확인한다.
  2. widthheight의 computed value를 확인한다.
  3. 좌우 또는 위아래 padding 합계를 확인한다.
  4. border 두께를 확인한다.
  5. margin과 부모의 gap을 별도로 확인한다.
  6. 자식 콘텐츠가 overflow를 만드는지 확인한다.

스타일 선언만 읽는 것보다 브라우저가 계산한 box model을 보면 원인을 빠르게 찾을 수 있다.

content-box를 사용하는 경우

border-box가 항상 정답은 아니다.
content 영역의 크기 자체를 고정해야 한다면 content-box가 의도를 더 정확하게 표현할 수 있다.

.canvas-preview {
  box-sizing: content-box;
  width: 640px;
  height: 360px;
  padding: 12px;
  border: 1px solid;
}

실제 미디어나 좌표계가 들어가는 content 영역을 정확히 640 × 360으로 유지하고, frame을 바깥에 더하려는 경우다.

하지만 일반적인 카드, input, button, page container처럼 외곽 크기를 기준으로 배치하는 UI에서는 border-box가 더 직관적인 경우가 많다.

자주 생기는 실수

width: 100%에 padding을 추가한 뒤 가로 스크롤이 생겼다면 box-sizing을 확인한다.

border-box를 적용했는데 글자가 잘린다면 padding과 border가 content 공간을 얼마나 줄였는지 계산한다.

고정 height에 padding을 넣고 내용이 넘친다면 min-height로 바꿀 수 있는지 살펴본다.

전역 reset이 있는데 컴포넌트에서 content-box로 덮어쓴 곳이 없는지 확인한다.

margin까지 width에 포함된다고 생각하지 않는다.

pseudo-element만 크기가 다르게 보인다면 *::before, *::after에도 설정이 적용되는지 확인한다.

선택 기준

레이아웃에 배치되는 최종 박스 너비와 높이를 CSS 값 그대로 읽고 싶다면 border-box를 사용한다.

.card {
  box-sizing: border-box;
  width: 320px;
  padding: 24px;
}

content 영역 자체를 지정한 크기로 유지하고 padding과 border를 바깥에 더하려면 content-box를 사용한다.

.preview {
  box-sizing: content-box;
  width: 640px;
}

대부분의 UI 프로젝트에서는 전역 border-box를 기본으로 두고, 정말 필요한 요소만 content-box로 바꾸는 흐름이 이해하기 쉽다.

정리

content-box에서 width는 content 영역만 뜻한다.
padding과 border는 최종 박스 크기에 추가된다.

border-box에서 width는 content, padding, border를 합친 크기다.
padding과 border가 늘어나면 그만큼 content 영역이 줄어든다.

margin은 두 방식 모두 지정한 width에 포함되지 않는다.

요소가 예상보다 커졌다면 먼저 현재 box-sizing과 좌우 padding, border를 확인하면 된다.
최종 외곽 크기를 기준으로 레이아웃을 만들 때는 border-box가 계산을 단순하게 만든다.

참고 자료