본문으로 건너뛰기
Previous
Next
CSS 박스 모델 | Margin, Padding, Border 완벽 정리

CSS 박스 모델 | Margin, Padding, Border 완벽 정리

CSS 박스 모델 | Margin, Padding, Border 완벽 정리

이 글의 핵심

박스 모델은 시각적 은유일 뿐 아니라, 브라우저가 ‘어떤 사각형을 어디에 그릴지’를 결정하는 레이아웃 알고리즘의 입력입니다.

CSS 기초에서 선택자·캐스케이드 쓰다가 이제 레이아웃 잡을 차례면, 박스 모델이 먼저야. Flex·Grid 가기 전에 “왜 너비가 이상해졌지?”를 규칙으로 설명하려면 여기가 출발선.

box-sizing: border-box 써. 무조건.

솔직히 말할게. content-box는 명세 기본값이고 의미는 분명한데, 실무 UI에서는 전역 border-box 박는 게 맞다. width: 300px인데 padding이랑 border 붙으면 content-box에선 밖으로 불어나서 50% + 50% 그리드가 터지고, 머릿속에서만 계산기 돌아가.

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

border-boxwidth콘텐츠 + padding + border를 합친 “테두리 박스”에 맞춰진다. 산수 싸움을 줄이는 쪽이 이김. 레거시랑 px 단위로 맞춰야 하는 특수 케이스 아니면, 나는 이거 없이 새 프로젝트 안 만듦.

marginbox-sizing이 뭐든 항상 바깥이야. width에 안 들어간다. 헷갈리면 DevTools 박스 다이어그램 열어.

시각으로만 비교해보자. 같은 width: 300px, 좌우 padding: 20px, border: 2px일 때:

content-box:  "300"은 맨 안 content만 → 겉 점유는 300+40+4 … 더 넓어짐
border-box:   "300"이 랩 전체(패딩·보더 포함) → 겉 300, 안쪽 콘텐츠만 쪼그라듦

둘 다 맞는 말이고, 내 선택은 둘째 치고 실무 팀 룰은 border-box로 고정하라.

margin collapsing 때문에 3시간…

한번은 섹션 간격 맞춘다고 margin-bottom: 2rem 줬는데, 위아래 섹션이 “내 계산”이랑 안 맞는 거야. Figma는 이렇게 안 말했는데. DevTools로 찍으면 숫자는 맞는데 간격이 반토막처럼 보이거나, 아래는 큰데 위는 이상한 식. 결국 margin collapsing(마진 병합) 이었다. 인접한 블록의 위·아래 margin더하기가 아니라 max 쪽으로 합쳐지는 거. 20+30이 50이 아니라 30이 될 수 있음. 그날 3시간은 그냥 “CSS가 나를 혐오한다”는 감정만 쌓였지.

.box1 { margin-bottom: 30px; }
.box2 { margin-top: 20px; }
/* 사이 실제 간격: 50 아니고 30 (큰 쪽) */

부모에 borderpadding도 없이 첫 자식만 margin-top 줬더니, 그 여백이 부모 밖으로 “튀어 보이는” 것도 같은 계열. “내가 parent에 margin 준 적 없는데” 하고 찾다가 끝남.

병합 끊는 데 실제로 쓰는 것들: 부모에 padding 한 스푼이나 border: 1px solid transparent 같은 거, display: flow-root, overflowvisible이 아닌 경우(환경에 따라 BFC/격리), Flex/Grid + gap으로 간격을 아예 margin 대신 통일. 나는 “형제끼리 위아래 margin 양쪽 다” 패턴을 가능한 안 씀. 한쪽이나, 아니면 gap.

상자 층은 이렇게만 기억해

테이블로 정리하던 시절은 접자. 바깥→안으로 감싸는 상자:

        ┌ margin (배경 안 칠해지는 편) ┐
        │  ┌ border (경계선) ┐         │
        │  │ ┌ padding ────┐ │         │
        │  │ │   content   │ │         │
        │  │ └─────────────┘ │         │
        │  └─────────────────┘         │
        └──────────────────────────────┘
  • content: 말 그대로 글, 이미지
  • padding: 테두리 안, 배경이 보통 같이 깔림 → 버튼/카드 “안쪽” 넓힐 땐 이거
  • border: 선. 두께가 “점유”에 들어감 (border-box에선 width 안에 묶임)
  • margin: 박스 밖. 배경 색 올라오지 않음

display: inline이면 세로 margin은 대체로 먹지 않는다는 것만 같이 떠올리면 됨. (CSS 기초에서 본 “표시 방식”이랑 합쳐서 기억.)

width / % — “누구 기준%?” 짜증나는 지점

블록에 width: auto면 보통 부모 콘텐츠 너비 꽉 채우게 사용값이 잡힌다. width: 100%는 부모 콘텐츠의 100%인데, content-box에선 padding·border를 또 얹다가 가로 스크롤 날 수 있음. “꽉 차되 안 넘침”이면 auto vs 100% 차이 꼭 짚어봐.

padding-top: 50% 같은 거 줬다가 “왜 가로에 맞춰지지?” 하면, 퍼센트 기준 축이 속성마다 달라서 그래. 가로 padding/margin %는 (보통) containing block 너비 기준이고, height: 50%는 부모 height가 제대로 잡혀 있어야 의미가 생김. Computed에서 “누구의 몇 %”인지 역추적해.

min-width / max-width는 반응형·카드에서 기본. Flex 자식이 말도 안 하게 넓어지면 min-width: auto + 콘텐츠 때문인 경우가 많아서, 그때는 Flexboxmin-width: 0 얘기로 이어짐.

padding / border / margin 코드만 남기고

padding shorthand: 값 하나(전부), 둘(상하|좌우), 셋(상|좌우|하), 넷(상|우|하|좌 시계). 음수는 안 됨.

border: 2px solid #333 한 줄 쓰고, border-radius는 카드/버튼에서 같이. border-box 쓰면 보더 굵어져도 “겉 width”는 안 늘어나는 편이니 싸움 줄음.

margin: 0 0 1rem 이런 식으로 한 방향만 쓰는 습관 들이면, 병합+양쪽 중복을 동시에 줄이기 좋다. 가로 가운데는 margin-inline: auto + width (또는 max-width) 잡힌 블록. 인라인에 auto만 박는 건 효과 없을 수 있음.

음수 margin은 끼워 맞추기·겹침용인데, 포커스 링/터치 영역 꼬이면 접근성 팀이 찾아옴. 진짜 필요할 때만.

outline vs border — 박스 밖에 겹쳐 그리는 애

border는 (border-box에 따라) 레이아웃 숫자에 관여. outline겹쳐 그리는 외곽선이라 공간 안 먹는 쪽. 포커스 링에 자주 쓰고, 디버그할 땐 *outline: 1px solid rgba(255,0,0,0.35) 같은 거 잠깐 씌우기. outline: none만 박지 마. 대체 포커스 스타일 없으면 키보드 유저는 길 잃어.

DevTools

Elements → Layout/Computed에 박스 그려줌. margin병합인지, 100%가 어디서 터졌는지, box-sizing이 전역 *에 묻었는지 여기서 끝내. 의심 순서: (1) 병합 (2) 자식 100% + 부모 padding (3) 스크롤 컨테이너의 sizing.

overflow: auto / hidden은 잘라내기 + 병합/BFC 쪽이랑 엮이니, “뭔가 끊겼다” 싶을 때 같이 봐. border-radius + overflow: hidden은 이미지 클리핑 패턴.

그래서 실제로

카드: box-sizing: border-box, max-width, border + border-radius, overflow: hidden 자주. card__bodypadding. 타이틀/문단 margin한 방향으로만 리듬 맞추기.

.card {
  box-sizing: border-box;
  max-width: 300px;
  border: 1px solid #e5e5e5;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.08);
}
.card__body { padding: 20px; }
.card__title { margin: 0 0 0.5rem; }

섹션 간격은 margin만으로 형제 margin 양면에 기대지 말고, 부모 padding + gap, Flex/Grid 쓰면 gap이 병합이랑 별로 안 싸움.

.vstack { display: flex; flex-direction: column; gap: 1rem; }

max-width: 100%는 미디어에도 잊지 말고. reflow 잦게 만드는 건 width/margin/font-size 쪽 — 애니메이션은 되도록 transform/opacity 쪽으로.

브라우저 기본 h1 margin 같은 건 reset/normalize 팀 룰에 맡기고, 전역 border-box는 그 룰의 첫줄이면 좋다고 본다. 내 의견은 위에 썼지.

이어 읽기

본문 밖 팁: 배포 전엔 git addcommitpushnpm run deploy 순으로.