Flexbox 완전 정복 — 축, 정렬, 그리고 실전 레이아웃 패턴
display: flex만 붙이면 뭔가 되긴 하는데, 왜 원하는 대로 정렬이 안 될까요?
Flexbox 완전 정복 — 축, 정렬, 그리고 실전 레이아웃 패턴
Flexbox를 처음 쓸 때 가장 혼란스러운 부분은 "어떤 속성이 가로 정렬이고 어떤 속성이 세로 정렬인지"입니다. 사실 Flexbox에는 가로/세로 개념이 없어요. 주축(main axis) 과 교차축(cross axis) 이라는 개념이 있을 뿐이고, flex-direction에 따라 축의 방향이 바뀝니다. 이 글에서는 축 개념부터 시작해서 실전 레이아웃 패턴까지 한 번에 정리해 보겠습니다.
1. Flexbox가 뭔가 — 1차원 레이아웃 모델
Flexbox(Flexible Box Layout)는 한 방향으로 아이템을 배치하고 정렬하는 1차원 레이아웃 모델 입니다.
float이나inline-block으로 레이아웃을 잡던 시절의 고통을 해결하기 위해 등장했습니다- "1차원"이라는 건 행(row) 또는 열(column) 중 하나의 축만 다룬다는 뜻입니다
- 행과 열을 동시에 제어하고 싶다면 Grid를 사용합니다
/* Flexbox 활성화 */
.container {
display: flex; /* 자식 요소들이 flex 아이템이 된다 */
}
Flexbox는 컨테이너(부모)에
display: flex를 선언하는 것으로 시작합니다. 속성의 대부분은 컨테이너에 작성하고, 일부만 아이템(자식)에 작성합니다.
2. 주축과 교차축 — 모든 혼란의 원인
Flexbox를 제대로 이해하려면 축(axis) 개념을 먼저 잡아야 합니다.
- ** 주축(Main Axis)**: 아이템이 배치되는 방향
- ** 교차축(Cross Axis)**: 주축에 수직인 방향
핵심은 flex-direction에 따라 축이 바뀐다는 것입니다.
flex-direction | 주축 방향 | 교차축 방향 |
|---|---|---|
row (기본값) | 가로 (→) | 세로 (↓) |
row-reverse | 가로 (←) | 세로 (↓) |
column | 세로 (↓) | 가로 (→) |
column-reverse | 세로 (↑) | 가로 (→) |
공부하다 보니 여기서 정말 많이 헷갈렸습니다. justify-content는 항상 "가로 정렬"이 아니라, ** 주축 정렬 **이에요. flex-direction: column이면 justify-content가 세로 방향으로 동작합니다.
3. 컨테이너 속성 — 부모가 레이아웃을 결정한다
flex-direction
아이템의 배치 방향(주축)을 결정합니다.
.container {
flex-direction: row; /* 기본값: 가로 배치 */
flex-direction: column; /* 세로 배치 */
}
flex-wrap
아이템이 한 줄을 넘길 때 줄바꿈 여부를 결정합니다.
.container {
flex-wrap: nowrap; /* 기본값: 한 줄에 모두 (넘치면 축소) */
flex-wrap: wrap; /* 넘치면 다음 줄로 */
}
justify-content — 주축 정렬
.container {
justify-content: flex-start; /* 시작점 정렬 (기본값) */
justify-content: center; /* 가운데 정렬 */
justify-content: flex-end; /* 끝점 정렬 */
justify-content: space-between; /* 양 끝 붙이고 사이 균등 */
justify-content: space-around; /* 아이템 양쪽에 균등 여백 */
justify-content: space-evenly; /* 모든 간격 동일 */
}
align-items — 교차축 정렬 (한 줄)
.container {
align-items: stretch; /* 기본값: 교차축 꽉 채움 */
align-items: flex-start; /* 교차축 시작점 */
align-items: center; /* 교차축 가운데 */
align-items: flex-end; /* 교차축 끝점 */
align-items: baseline; /* 텍스트 기준선 정렬 */
}
align-content — 교차축 정렬 (여러 줄)
flex-wrap: wrap으로 여러 줄이 생겼을 때만 효과가 있습니다.
.container {
flex-wrap: wrap;
align-content: flex-start; /* 줄들을 시작점으로 */
align-content: center; /* 줄들을 가운데로 */
align-content: space-between; /* 줄 사이 균등 배치 */
}
align-items는 "줄 안에서" 아이템의 교차축 위치를,align-content는 "줄과 줄 사이"의 간격을 제어합니다. 이 차이를 구분하는 게 중요합니다.
gap
아이템 사이의 간격을 설정합니다. margin으로 간격을 잡는 것보다 훨씬 깔끔합니다.
.container {
gap: 16px; /* 행·열 간격 동일 */
gap: 16px 24px; /* 행 간격 16px, 열 간격 24px */
}
4. 아이템 속성 — 개별 아이템의 행동을 제어한다
flex-grow — 남은 공간 분배
.item {
flex-grow: 0; /* 기본값: 남은 공간을 가져가지 않음 */
flex-grow: 1; /* 남은 공간을 비율대로 가져감 */
}
예를 들어 A에 flex-grow: 1, B에 flex-grow: 2를 주면, 남은 공간을 1:2로 나눠 갖습니다.
flex-shrink — 공간 부족 시 축소 비율
.item {
flex-shrink: 1; /* 기본값: 공간 부족하면 축소됨 */
flex-shrink: 0; /* 절대로 줄어들지 않음 */
}
사이드바처럼 고정 너비가 필요한 요소에 flex-shrink: 0을 자주 사용합니다.
flex-basis — 초기 크기
주축 방향의 초기 크기를 설정합니다. width/height 대신 사용한다고 생각하면 됩니다.
.item {
flex-basis: auto; /* 기본값: 콘텐츠 크기 또는 width/height 값 */
flex-basis: 200px; /* 200px에서 시작 */
flex-basis: 0%; /* 콘텐츠 크기 무시, grow 비율로만 결정 */
}
flex shorthand — 이것만 기억하세요
flex는 flex-grow, flex-shrink, flex-basis의 축약 속성입니다.
.item {
flex: 0 1 auto; /* 기본값 (안 늘어남, 줄어듦, 콘텐츠 기반) */
flex: 1; /* = flex: 1 1 0% (늘어남, 줄어듦, 비율 기반) */
flex: auto; /* = flex: 1 1 auto (늘어남, 줄어듦, 콘텐츠 기반) */
flex: none; /* = flex: 0 0 auto (고정 크기) */
}
align-self — 개별 아이템의 교차축 정렬
컨테이너의 align-items를 개별 아이템에서 덮어쓸 수 있습니다.
.special-item {
align-self: center; /* 이 아이템만 교차축 가운데 */
}
order — 시각적 순서 변경
HTML 순서를 바꾸지 않고 시각적 순서만 변경합니다. 기본값은 0이고, 값이 작을수록 앞에 배치됩니다.
.first-visually {
order: -1; /* 맨 앞으로 */
}
5. flex: 1의 진짜 의미
flex: 1은 아마 Flexbox에서 가장 많이 쓰는 선언일 겁니다. 정확히 풀어쓰면 이렇습니다.
/* flex: 1은 아래와 같다 */
.item {
flex-grow: 1; /* 남은 공간을 가져간다 */
flex-shrink: 1; /* 공간이 부족하면 줄어든다 */
flex-basis: 0%; /* 콘텐츠 크기를 무시하고 비율로만 계산한다 */
}
flex-basis: 0%가 핵심입니다. 이게 auto가 아니라 0%이기 때문에 모든 아이템이 콘텐츠 크기와 관계없이 동일한 비율로 공간을 나눠 갖습니다.
flex: 1과flex: auto의 차이가 헷갈리면 이렇게 기억하세요.flex: 1은 "비율로 나눠",flex: auto는 "콘텐츠 크기 존중하면서 나눠"입니다.
6. 실전 레이아웃 패턴
완벽한 가운데 정렬
/* 수직·수평 가운데 정렬 — 이 2줄이면 끝 */
.center {
display: flex;
justify-content: center; /* 주축 가운데 */
align-items: center; /* 교차축 가운데 */
}
네비게이션 바
/* 로고는 왼쪽, 메뉴는 오른쪽 */
.navbar {
display: flex;
justify-content: space-between; /* 양쪽 끝 배치 */
align-items: center; /* 세로 가운데 */
padding: 0 24px;
}
Holy Grail Layout
헤더, 푸터, 사이드바 2개, 메인 콘텐츠로 구성된 클래식 레이아웃입니다.
/* 전체 구조 */
.page {
display: flex;
flex-direction: column;
min-height: 100vh; /* 화면 꽉 채우기 */
}
/* 중간 영역: 사이드바 + 메인 */
.body {
display: flex;
flex: 1; /* 남은 세로 공간 모두 차지 */
}
.sidebar-left {
flex: 0 0 200px; /* 고정 너비 200px, 줄어들지 않음 */
}
.main {
flex: 1; /* 남은 가로 공간 전부 */
}
.sidebar-right {
flex: 0 0 200px;
}
카드 그리드 (Flex + Wrap)
/* 반응형 카드 목록 */
.card-grid {
display: flex;
flex-wrap: wrap; /* 넘치면 줄바꿈 */
gap: 16px;
}
.card {
flex: 1 1 300px; /* 최소 300px, 남으면 늘어남 */
}
카드 그리드는 Grid의
auto-fill/auto-fit이 더 깔끔한 경우가 많습니다. 하지만 아이템 수가 유동적이고 단순한 구조라면 Flex + Wrap으로도 충분합니다.
7. Flexbox vs Grid — 언제 무엇을 쓰는가
| 기준 | Flexbox | Grid |
|---|---|---|
| 차원 | 1차원 (행 또는 열) | 2차원 (행과 열 동시) |
| 적합한 상황 | 네비게이션, 버튼 그룹, 카드 행 | 페이지 전체 레이아웃, 대시보드 |
| 아이템 크기 | 콘텐츠 기반으로 유연하게 | 트랙(행/열) 기반으로 엄격하게 |
| 정렬 | 한 축의 정렬이 핵심 | 양 축 동시 정렬 |
둘은 대체 관계가 아닙니다. 실무에서는 페이지 전체 구조는 Grid, ** 컴포넌트 내부 정렬은 Flexbox**로 쓰는 경우가 많습니다.
간단한 판단 기준은 이렇습니다.
- "한 줄(또는 한 열)에 아이템을 나열한다" → Flexbox
- "행과 열을 동시에 정의해야 한다" → Grid
- 잘 모르겠으면 → Flexbox부터 시도하고, 한계가 느껴지면 Grid로 전환
마무리
Flexbox를 정리하면서 느낀 건, 결국 ** 축 개념 **만 확실히 잡으면 나머지는 자연스럽게 따라온다는 것입니다.
justify-content는 주축,align-items는 교차축flex-direction이 바뀌면 축도 바뀐다flex: 1은flex-grow:1 flex-shrink:1 flex-basis:0%align-items는 줄 안,align-content는 줄 사이
이 네 가지만 기억하면, 대부분의 Flexbox 레이아웃은 머릿속에서 바로 그려질 겁니다.