Flexbox는 2015년경 모든 주요 브라우저에 적용되었으며, 개발자들이 십 년간 임시방편으로 해결해 온 문제인 수직 중앙 정렬을 즉시 해결했습니다. 하지만 대부분의 사람들은 Flexbox를 거꾸로 배웠습니다 — 왜 동작하는지 이해하지 못한 채 치트 시트에서 justify-content: center를 외웠습니다. 레이아웃이 예상대로 작동하지 않을 때, 이 이해의 공백이 5분짜리 작업을 한 시간의 시행착오로 바꿔놓습니다. 이 가이드는 먼저 정신적 모델을 구축하고, 실제 프로젝트에서 정말로 중요한 모든 속성을 다룹니다.
정신적 모델: 한 번에 하나의 축
Flexbox의 핵심 개념은 아이템을 하나의 축을 따라 배치한다는 것입니다. 주축(main axis)은 아이템이 흐르는 방향이고, 교차축(cross axis)은 그것과 수직으로 교차합니다. 거의 모든 초보자가 놓치는 핵심 인사이트: flex-direction이 어느 축이 무엇인지를 결정합니다. 나머지 모든 것은 그로부터 따라옵니다.
/* Default: main axis runs LEFT → RIGHT, cross axis runs TOP → BOTTOM */
.container {
display: flex;
flex-direction: row; /* default */
}
/* Rotated: main axis now runs TOP → BOTTOM, cross axis runs LEFT → RIGHT */
.container {
display: flex;
flex-direction: column;
}
/*
justify-content → controls alignment on the MAIN axis
align-items → controls alignment on the CROSS axis
This is why centering something in both axes looks like this:
*/
.center-both {
display: flex;
justify-content: center; /* horizontally centred (main axis = row) */
align-items: center; /* vertically centred (cross axis) */
}flex-direction: column으로 전환하면, justify-content는 더 이상 수평 정렬을 제어하지 않습니다 — 주축이 회전했기 때문에 수직 정렬을 제어합니다. align-items는 이제 수평을 제어합니다. 예상치 못한 곳에서 정렬이 안 될 때마다 이 회전을 염두에 두세요.컨테이너 속성
이 속성들은 display: flex가 있는 부모 요소인 flex 컨테이너에 적용됩니다. 모든 자식 아이템이 어떻게 배분되고 정렬되는지를 제어합니다.
/* display: flex makes the element a block-level flex container.
display: inline-flex makes it inline (sits in text flow). */
.nav {
display: flex;
}
/* flex-direction — which way does the main axis run? */
.nav {
flex-direction: row; /* left to right (default) */
flex-direction: row-reverse; /* right to left */
flex-direction: column; /* top to bottom */
flex-direction: column-reverse; /* bottom to top */
}
/* flex-wrap — what happens when items overflow the container? */
.card-grid {
flex-wrap: nowrap; /* all items forced onto one line (default) */
flex-wrap: wrap; /* items wrap onto additional lines */
flex-wrap: wrap-reverse; /* items wrap in reverse order */
}
/* gap — space between items. Modern approach; avoids margin hacks. */
.card-grid {
gap: 1.5rem; /* same in both directions */
gap: 1rem 2rem; /* row-gap column-gap */
}gap 속성은 특별히 언급할 가치가 있습니다. 이 속성이 존재하기 전에는 flex 아이템에 마진을 추가한 다음 음수 마진으로 외부 가장자리를 취소하는 것이 일반적인 패턴이었습니다. gap은 이 모든 것을 깔끔하게 대체합니다 — 항상 대신 사용하세요.
/* justify-content — alignment on the MAIN axis */
.nav {
justify-content: flex-start; /* packed to start (default) */
justify-content: flex-end; /* packed to end */
justify-content: center; /* packed to centre */
justify-content: space-between; /* first/last at edges, equal gaps between */
justify-content: space-around; /* equal space around each item */
justify-content: space-evenly; /* equal space between every gap */
}
/* align-items — alignment on the CROSS axis (single line) */
.nav {
align-items: stretch; /* items fill cross-axis height (default) */
align-items: flex-start;/* items align to start of cross axis */
align-items: flex-end; /* items align to end of cross axis */
align-items: center; /* items centred on cross axis */
align-items: baseline; /* items aligned by their text baseline */
}
/* align-content — cross-axis alignment when there are MULTIPLE lines
(only takes effect with flex-wrap: wrap and enough items to wrap) */
.card-grid {
flex-wrap: wrap;
align-content: flex-start; /* rows packed to top */
align-content: center; /* rows centred vertically */
align-content: space-between; /* rows spread out with space between */
}다음은 이것들을 결합한 실제 네비게이션 바입니다. 로고는 왼쪽에 있고, 네비게이션 링크는 margin-left: auto를 사용하여 오른쪽으로 밀립니다 — 클래식 Flexbox 트릭입니다. justify-content: space-between 방식도 작동하지만, 두 개 이상의 그룹이 있을 때 자동 마진 기법이 더 유연합니다.
/* Nav: logo left, links right */
.site-nav {
display: flex;
align-items: center;
gap: 1rem;
padding: 0 2rem;
height: 64px;
background: #1a1a2e;
}
.site-nav .logo {
font-size: 1.25rem;
font-weight: 700;
color: #fff;
text-decoration: none;
}
.site-nav .nav-links {
display: flex;
align-items: center;
gap: 1.5rem;
margin-left: auto; /* pushes everything after it to the right */
list-style: none;
margin-top: 0;
margin-bottom: 0;
padding: 0;
}
.site-nav .nav-links a {
color: #ccc;
text-decoration: none;
font-size: 0.9rem;
}
.site-nav .nav-links a:hover {
color: #fff;
}아이템 속성
이 속성들은 flex 컨테이너의 직접 자식인 flex 아이템에 적용됩니다. 개별 아이템이 어떻게 늘어나고, 줄어들고, 크기를 설정하는지를 제어합니다.
flex-grow— 이 아이템이 차지하는 가용 여유 공간의 양. 기본값0(늘어나지 않음). 아이템에1로 설정하면 남은 공간을 모두 차지합니다.flex-shrink— 공간이 부족할 때 이 아이템이 다른 것들에 비해 얼마나 줄어드는지. 기본값1. 아이템이 줄어들지 않도록 하려면0으로 설정하세요.flex-basis— 늘어나거나 줄어들기 전 아이템의 초기 크기. 길이(250px,30%) 또는auto(아이템의 콘텐츠 크기 사용)가 될 수 있습니다.align-self— 특정 아이템 하나에 대해align-items를 재정의합니다. 동일한 값을 허용합니다:auto,flex-start,flex-end,center,baseline,stretch.order— HTML을 건드리지 않고 시각적 순서를 변경합니다. 기본값0. 값이 낮을수록 먼저 나타납니다. 탭 순서를 깨고 키보드 및 스크린 리더 사용자에게 접근성 문제를 일으키므로 드물게 사용하세요.
/* Classic sidebar + main content layout */
.app-layout {
display: flex;
min-height: 100vh;
}
.sidebar {
flex: 0 0 280px; /* don't grow, don't shrink, always 280px wide */
background: #f5f5f5;
padding: 2rem 1.5rem;
}
.main-content {
flex: 1; /* grow to fill all remaining space */
padding: 2rem;
min-width: 0; /* critical: prevents content overflow in flex children */
}
/* Responsive: stack vertically on mobile */
@media (max-width: 768px) {
.app-layout {
flex-direction: column;
}
.sidebar {
flex: none; /* reset — don't use flex-basis on column layout */
}
}min-width: 0 함정: 기본적으로 flex 아이템은 최소 콘텐츠 크기 이하로 줄어들 수 없습니다. flex 아이템 안에 긴 단어나 넓은 테이블이 있으면 줄 바꿈이 되는 대신 오버플로우가 발생합니다. flex 아이템에 min-width: 0을 추가하면 이 문제가 해결됩니다. 사이드바 레이아웃의 메인 콘텐츠 영역에서 항상 나타납니다.flex 단축 속성 설명
flex 단축 속성은 flex-grow, flex-shrink, flex-basis를 하나의 선언으로 압축합니다. W3C 명세는 일반적인 경우를 다루는 몇 가지 키워드 값을 정의하며, 이것들은 자주 혼동됩니다.
/* flex: 1
Expands to: flex-grow: 1; flex-shrink: 1; flex-basis: 0%
Meaning: grow and shrink freely; start from zero (ignore content size).
All items with flex: 1 share space equally, regardless of content.
Most common choice for "fill available space". */
.tab { flex: 1; }
/* flex: auto
Expands to: flex-grow: 1; flex-shrink: 1; flex-basis: auto
Meaning: grow and shrink freely; start from the item's content size.
Items with more content get more space — proportional, not equal. */
.column { flex: auto; }
/* flex: none
Expands to: flex-grow: 0; flex-shrink: 0; flex-basis: auto
Meaning: completely rigid — don't grow, don't shrink, stay at content size.
Use for items that should never flex (icons, avatars, fixed labels). */
.avatar { flex: none; }
/* flex: 0 0 200px
Explicit: don't grow, don't shrink, always exactly 200px.
Same as writing out all three values. More readable for fixed-size items. */
.sidebar { flex: 0 0 200px; }
/* Practical difference: flex: 1 vs flex: auto in a tab bar
With flex: 1 → all tabs are equal width regardless of label length
With flex: auto → "Settings" tab is wider than "Home" tab */
.tabs { display: flex; }
.tab-equal { flex: 1; } /* equal width tabs */
.tab-auto { flex: auto; } /* content-sized tabs */일반적인 실제 레이아웃
다음은 Flexbox만으로 만든 정기적으로 사용하게 될 네 가지 패턴입니다.
/* 1. Sticky footer
The footer always sits at the bottom, even on short pages.
body (or .app-shell) becomes a column flex container. */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
main {
flex: 1; /* expands to push footer down */
}
footer {
/* stays at the bottom */
}/* 2. Centred modal / hero content
Both axes centred — the Flexbox vertical-centring party trick. */
.hero {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 2rem;
background: linear-gradient(135deg, #667eea, #764ba2);
}
.hero-content {
max-width: 640px;
text-align: center;
color: #fff;
}/* 3. Nav bar: logo left, links right */
.site-nav {
display: flex;
align-items: center;
padding: 0 2rem;
height: 64px;
}
.site-nav .nav-links {
margin-left: auto; /* auto margin consumes all free space to its left */
display: flex;
gap: 1.5rem;
list-style: none;
padding: 0;
margin-top: 0;
margin-bottom: 0;
}/* 4. Card row that wraps on mobile */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card {
flex: 1 1 280px; /* grow and shrink, but never below 280px wide */
background: #fff;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
/* On wide screens: 3-4 cards per row.
On narrow screens: cards wrap into their own rows automatically.
No media query needed for the wrap itself — flex-wrap handles it. */Flexbox vs Grid — 언제 무엇을 사용할까
이 질문은 항상 나오는데, 답은 대부분의 글이 만들어 내는 것보다 더 간단합니다: Flexbox는 1차원 레이아웃을 위한 것입니다. CSS Grid는 2차원 레이아웃을 위한 것입니다. 단일 행 또는 단일 열 — 네비게이션 바, 버튼 그룹, 카드 행 — 에 아이템을 배치하는 경우 Flexbox가 적합한 도구입니다. 행과 열을 동시에 제어해야 하는 경우 — 전체 페이지 레이아웃, 사진 모자이크, 폼 그리드 — Grid를 사용하세요.
실제로는 같은 프로젝트에서 둘 다 사용하게 됩니다. Grid는 페이지 수준 구조(헤더, 사이드바, 메인, 푸터)를 처리합니다. Flexbox는 해당 영역 내의 컴포넌트(네비게이션 바, 카드 콘텐츠, 모달 내부의 버튼 클러스터)를 처리합니다. 서로 경쟁하는 것이 아니라 보완합니다.
flex-wrap: wrap을 사용한 Flexbox로 이것을 할 수 있지만, 같은 시각적 행의 아이템들은 서로 관계가 없습니다 — 높이가 독립적입니다. Grid의 암묵적 행 크기 조정은 이것을 자동으로 처리합니다. 래핑된 행의 카드가 모두 같은 높이이어야 하고 내부 요소가 카드 전체에서 정렬되어야 한다면 Grid가 더 깔끔합니다.마무리
Flexbox의 정신적 모델은 이렇습니다: 방향을 선택하고, 어떤 속성이 어느 축을 제어하는지 이해한 다음, 아이템이 늘어나야 할지, 줄어들어야 할지, 고정되어야 할지를 선택하세요. 이것이 클릭되면 치트 시트는 목발이 아닌 참조가 됩니다. MDN Flexbox 가이드는 엣지 케이스에 대한 최고의 참조이며, CSS-Tricks 완전 가이드는 여전히 가장 유용한 시각적 치트 시트입니다. 프로덕션 사용을 위해 브라우저 지원은 사실상 전 세계적입니다 — 접두사가 필요하지 않습니다. W3C 명세는 진정으로 혼란스러운 엣지 케이스를 만나면 살펴볼 가치가 있습니다 — 알고리즘이 정확하게 문서화되어 있으며 대부분의 "왜 이렇게 동작하는가" 질문에 답합니다.