CSS 강좌 / Flexbox 정렬·간격·줄바꿈·크기 조절하기
Flexbox의 정렬은 주축에 justify-content, 교차축에 align-items를 사용합니다. 줄바꿈은 flex-wrap, 항목 간격은 gap, 크기 분배는 아이템의 flex로 조절하며, 여러 줄의 묶음을 정렬하는 align-content는 별도의 조건이 필요합니다.
정렬 속성을 축과 적용 대상으로 구분하기
| 속성 | 적용 대상 | 확인할 조건 |
|---|---|---|
| justify-content | 컨테이너의 주축 방향 아이템 분배 | 아이템 크기를 정한 뒤 남는 공간 |
| align-items | 각 아이템의 교차축 정렬 | 아이템의 교차축 크기와 줄 높이 |
| align-self | 특정 아이템의 교차축 정렬 | 컨테이너 기본 정렬을 개별적으로 변경 |
| align-content | 여러 줄의 묶음 | wrap 가능한 컨테이너와 교차축의 남는 공간 |
| gap | 인접 아이템·줄 사이 간격 | 정렬로 더해진 공간과 구분 |
row에서 justify-content는 주로 가로, align-items는 세로를 조절합니다. column으로 바꾸면 주축이 세로가 되어 두 속성의 물리적인 방향도 바뀝니다. 특히 column의 justify-content를 비교하려면 컨테이너 높이에 남는 공간이 있어야 합니다. 높이가 내용에 딱 맞으면 center나 space-between을 바꿔도 이동할 공간이 거의 없습니다.
justify-content: center는 아이템 묶음을 가운데에 놓고 space-between은 첫 항목과 마지막 항목을 양 끝에 놓은 뒤 사이 공간을 나눕니다. gap은 지정한 간격이며 space-between은 남는 공간을 분배합니다. 함께 사용하면 실제 사이 간격은 gap보다 커질 수 있습니다. 아이템의 flex-grow가 남는 공간을 모두 사용하거나 주축 방향의 auto margin이 공간을 가져가면 justify-content의 변화가 눈에 보이지 않을 수 있습니다.
align-items: center는 한 줄 안에서 높이가 다른 항목을 가운데에 맞춥니다. align-content: center는 여러 줄 전체를 교차축 가운데에 모읍니다. 둘을 같은 기능으로 생각하면 안 됩니다. align-content는 nowrap인 한 줄 컨테이너에는 효과가 없고, wrap 컨테이너라도 줄들이 차지한 크기와 컨테이너 높이가 같으면 비교할 여유가 없습니다. MDN의 Flexbox 정렬 안내에서 적용 조건을 확인할 수 있습니다.
전체 예제로 아이템과 줄 묶음 정렬 비교하기
아래 코드를 flex-align.html로 저장해 엽니다. 첫 구역은 높이가 다른 세 항목, 둘째 구역은 두 줄이 생기는 네 항목입니다. 둘째 구역은 높이를 280px로 지정해 줄 묶음을 옮길 여유를 만들었습니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>flex-align.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.toolbar { display: flex; gap: 12px; justify-content: space-between; align-items: center;
width: 100%; max-width: 640px; min-height: 120px; border: 2px solid #789; padding: 12px; }
.toolbar > div { padding: 8px; background: #dceefa; }
.lines { display: flex; flex-wrap: wrap; gap: 12px; width: min(100%, 360px); height: 280px;
align-items: center; align-content: flex-start; border: 2px solid #789; padding: 12px; }
.lines > div { flex: 0 0 140px; padding: 8px; background: #e8f5de; }
.tall { min-height: 60px; }
</style>
</head>
<body>
<h1>한 줄 정렬과 여러 줄 정렬</h1>
<h2>도구 모음</h2><div class="toolbar"><div>짧은 항목</div><div>두 줄 항목<br>둘째 줄</div><div>버튼 자리</div></div>
<h2>줄 묶음</h2><div class="lines"><div>1</div><div class="tall">2<br>둘째 줄</div><div>3</div><div class="tall">4<br>둘째 줄</div></div>
</body>
</html>
- .toolbar의 justify-content를 center로 바꿔 세 항목이 가운데로 모이는지 확인합니다. flex-start도 비교한 뒤 space-between으로 되돌립니다. 아이템 크기와 gap은 그대로 두세요.
- .toolbar의 align-items를 flex-start로 바꿔 위쪽에 정렬되는지 확인합니다. stretch로 바꾸면 교차축 크기가 auto인 항목이 늘어납니다. 비교 후 center로 복원합니다.
- .lines의 align-content만 center로 바꿉니다. 각 줄의 아이템 관계를 유지하면서 두 줄 묶음이 내려옵니다. 다시 flex-start로 돌린 뒤 align-items만 flex-start로 바꾸면 각 줄 안의 짧은 항목 위치가 바뀝니다.
- .lines의 flex-wrap만 nowrap으로 바꾸면 모든 항목이 한 줄을 유지하려고 하며 이 예제는 축소를 끄고 있어 가로로 넘칩니다. 이 상태에서는 align-content로 여러 줄을 정렬할 수 없습니다. 확인 후 wrap으로 복원합니다.
flex-grow·flex-shrink·flex-basis 이해하기
flex: 1 1 192px은 순서대로 grow, shrink, basis입니다. basis는 주축의 공간 분배를 시작할 기준 크기이고 최종 너비를 보장하는 값이 아닙니다. grow는 남는 공간에 대한 확대 비율, shrink는 공간이 부족할 때의 축소 비율입니다. shorthand의 숫자 한 개나 auto는 생략값 규칙이 있으므로 학습 예제에서는 세 값을 명시해 비교합니다.
| 선언 | 공간이 남을 때 | 공간이 부족할 때 |
|---|---|---|
| flex: 0 1 192px | 기준 크기를 유지 | 축소에 참여 |
| flex: 1 1 192px | 남는 공간을 분배받음 | 축소에 참여 |
| flex: 0 0 192px | 기준 크기를 유지 | 축소하지 않음. nowrap이면 넘칠 수 있음 |
예를 들어 콘텐츠 너비가 640px인 부모 안에 기준 크기 100px인 항목 세 개와 gap 두 개(각 20px)가 있으면 남는 공간은 640 − 300 − 40 = 300px입니다. grow 비율이 1:2:1이면 추가되는 공간은 75px, 150px, 75px이며 최종 너비는 175px, 250px, 175px입니다. 콘텐츠 최소 크기나 min/max 제약이 개입하지 않는 조건의 계산입니다.
축소량은 shrink 숫자만으로 균등 분배하지 않습니다. 축소 비율과 기준 크기를 함께 고려합니다. 기준 크기가 200px와 100px이고 shrink가 둘 다 1인 두 항목에서 60px을 줄여야 한다면, 제약이 없는 단순한 조건에서는 40px과 20px을 줄입니다. padding·border·최소 콘텐츠 크기 등이 있으면 결과가 달라질 수 있으므로 적용값을 실제로 확인해야 합니다. MDN의 flex 설명에 shorthand와 크기 조건이 설명되어 있습니다.
크기 분배와 줄바꿈 카드 실습하기
다음 코드는 flex-size.html로 저장합니다. 계산 구역은 별도 padding·border 없이 최대 너비 640px로 만들어 위 계산과 대응합니다. 카드 목록은 너비 192px를 줄바꿈의 기준으로 사용하되 남는 공간을 채우도록 합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>flex-size.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.calc { display: flex; gap: 20px; width: min(100%, 640px); background: #eef2f6; }
.calc > div { flex: 1 1 100px; min-width: 0; height: 80px; background: #dceefa; }
.calc > div:nth-child(2) { flex-grow: 2; }
.cards { display: flex; flex-wrap: wrap; gap: 16px; list-style: none; padding: 0; }
.cards > li { flex: 1 1 192px; min-width: 0; padding: 16px; background: #e8f5de; overflow-wrap: anywhere; }
</style>
</head>
<body>
<h1>flex 값과 공간 분배</h1><div class="calc"><div>grow 1</div><div>grow 2</div><div>grow 1</div></div>
<h2>줄바꿈 카드</h2><ul class="cards"><li>첫째 카드</li><li>둘째 카드</li><li>셋째 카드</li><li>넷째 카드</li></ul>
</body>
</html>
- 브라우저를 넓혀 .calc의 너비가 640px인 상태에서 개발자 도구로 세 항목의 너비를 확인합니다. 175px·250px·175px가 됩니다. 둘째 항목의 grow만 1로 바꾸면 세 항목은 200px씩 됩니다. 비교 후 2로 복원합니다.
- .cards > li의 grow만 0으로 바꾸어 마지막 줄 카드가 남는 너비를 채우지 않는지 확인합니다. 다시 1로 복원하면 마지막 줄도 공간을 분배받습니다.
- 화면을 좁혀 한 줄에 놓이는 카드 수가 줄어드는지 확인합니다. wrap은 배치할 줄을 결정하고 각 줄은 따로 남는 공간을 분배합니다. 마지막 줄과 앞 줄의 카드 너비가 반드시 일치하지는 않습니다.
- 줄바꿈 대신 한 줄 넘침을 보려면 flex-wrap을 nowrap, 아이템을 flex: 0 0 192px로 바꿉니다. 두 조건을 확인한 뒤 원래 wrap과 1 1 192px로 복원합니다.
긴 문자열은 최소 크기와 줄바꿈을 함께 확인하기
Flex 아이템의 자동 최소 너비 때문에 긴 주소나 식별자가 들어간 영역이 줄어들지 못할 수 있습니다. min-width: 0은 아이템의 축소를 허용하고, overflow-wrap: anywhere는 문자열 자체가 줄바꿈될 수 있게 합니다. 상자를 줄였다고 그 안의 글자가 자동으로 잘리는 것은 아니므로 두 문제를 구분해야 합니다.
아래 코드는 flex-long-text.html로 저장합니다. 실제 주소가 아닌 공백 없는 긴 식별자를 넣었습니다. 화면을 좁혀도 안내 영역 옆의 문자열이 여러 줄로 나뉘어 읽힙니다. MDN의 overflow-wrap 설명에서 줄바꿈 규칙을 확인할 수 있습니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>flex-long-text.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.notice { display: flex; gap: 12px; max-width: 600px; border: 2px solid #789; padding: 12px; }
.icon { flex: 0 0 48px; background: #ffe4b5; }
.text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; background: #dceefa; }
.text p { margin: 0; }
</style>
</head>
<body>
<h1>긴 문자열과 최소 크기</h1><div class="notice"><div class="icon">안내</div><div class="text"><p>ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</p></div></div>
</body>
</html>
- .text에서 overflow-wrap만 normal로 바꿉니다. 아이템 너비는 줄어들어도 문자열이 경계 밖으로 넘칠 수 있습니다. anywhere로 복원합니다.
- 최소 너비의 영향을 따로 보려면 overflow-wrap: normal을 유지한 상태에서 min-width: 0을 제거합니다. 긴 문자열의 최소 크기가 아이템 배치에 미치는 영향을 확인한 뒤 두 선언을 원래 값으로 복원합니다.
정렬이 안 될 때는 먼저 부모의 축과 남는 공간을 확인하고, 크기가 안 줄어들 때는 아이템의 최소 크기와 내부 문자열을 확인하세요. 문제를 감추려고 전체 부모에 overflow: hidden을 넣으면 내용이나 포커스 표시를 잃을 수 있습니다. 레이아웃의 크기와 콘텐츠의 표시를 별도로 해결하는 편이 결과를 판단하기 쉽습니다.










